Fees de transacción ¿Cómo se agregan?
Ahmed Castro
Posted on April 5, 2022
Una manera de mantener con oxígeno a nuestros tokens o ecosistemas es mediante los fees por transacción, o sea cobrar una comisión cada vez que se mueven fondos. Esta comisión puede ir a una billetera de los creadores o a una bóveda. Solidity y las librerías de OpenZeppelin nos dan todas las herramientas necesarias para hacerlo. En este video exploramos diferentes formas de lograrlo, para ambos principiantes y avanzados.
Dependencias
Para este tutorial ocuparás Metamask con fondos de Rinkeby Testnet que puedes conseguir desde el Faucet. También necesitarás conectar tu wallet a Polygon Mainnet y conseguir MATIC desde algún exchange.
1. Fee fijo por transacción simple
Los contratos ERC-20 de OpenZeppelin exponen la función _afterTokenTransfer
que nos hacen muy conveniente implementar tax fees.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.12;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MyToken is ERC20
{
address public vault_address;
uint public fee;
bool private is_in_fee_transfer;
constructor () ERC20("My Token", "TKN")
{
// Edit here
vault_address = 0xb6F5414bAb8d5ad8F33E37591C02f7284E974FcB;
fee = 1 ether;
_mint(msg.sender, 1_000_000 ether);
// End edit
}
function _afterTokenTransfer(
address from,
address to,
uint amount
) internal virtual override(ERC20)
{
require(amount >= fee, "Amount must be greater than fee");
super._afterTokenTransfer(from, to, amount);
if(!is_in_fee_transfer)
{
is_in_fee_transfer = true;
_transfer(to, vault_address, fee);
is_in_fee_transfer = false;
}
}
}
2. Método avanzado de capturas de fees
- Con ayuda del Router de Uniswap podemos capturar un porcentaje diferente de fees en caso de Buy, Sell y P2P: Compra, Venta y transacción normal a otra billetera
- Esta vez capturamos un porcentaje de la tansacción, no un valor fijo
- Además agragamos addresses operadores como Taxless, exemptos de fees
Recuerda ajustar la dirección del router dependiendo de tu network:
- Polygon Quickswap:
0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff
- Ethereum Uniswap V2:
0x10ED43C718714eb63d5aA57B78B54704E256024E
- BSC Mainnet Pancake:
0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
- BSC Testnet Pancake:
0xD99D1c33F9fC3444f8101754aBC46c52416550D1
// SPDX-License-Identifier: MIT
pragma solidity 0.8.12;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
interface IUniswapV2Factory {
function createPair(address tokenA, address tokenB) external returns (address pair);
}
interface IUniswapV2Router02 {
function factory() external pure returns (address);
function WETH() external pure returns (address);
}
contract MyToken is ERC20
{
address public vault_address;
address public pair;
uint public fee_decimal = 2;
enum FeesIndex{ BUY, SELL, P2P }
uint[] public fee_percentages;
mapping(address => bool) public is_taxless;
bool private is_in_fee_transfer;
constructor () ERC20("My Token", "TKN")
{
IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02(0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff);
pair = IUniswapV2Factory(_uniswapV2Router.factory()).createPair(address(this), _uniswapV2Router.WETH());
// Edit here
vault_address = 0xb6F5414bAb8d5ad8F33E37591C02f7284E974FcB;
fee_percentages.push(1000); // Buy fee is 10.00%
fee_percentages.push(1500); // Sell fee is 15.00%
fee_percentages.push(500); // Buy fee is 5.00%
// End edit
is_taxless[msg.sender] = true;
is_taxless[vault_address] = true;
is_taxless[address(this)] = true;
is_taxless[address(0)] = true;
_mint(msg.sender, 1_000_000 ether);
}
function _afterTokenTransfer(
address from,
address to,
uint amount
) internal virtual override(ERC20)
{
super._afterTokenTransfer(from, to, amount);
if(!is_in_fee_transfer)
{
uint fees_collected;
if (!is_taxless[from] && !is_taxless[to]) {
bool sell = to == pair;
bool p2p = from != pair && to != pair;
uint fee = calculateFee(p2p ? FeesIndex.P2P : sell ? FeesIndex.SELL : FeesIndex.BUY, amount);
fees_collected += fee;
}
if(fees_collected > 0)
{
is_in_fee_transfer = true;
_transfer(to, vault_address, fees_collected);
is_in_fee_transfer = false;
}
}
}
function calculateFee(FeesIndex fee_index, uint amount) internal view returns(uint) {
return (amount * fee_percentages[uint(fee_index)]) / (10**(fee_decimal + 2));
}
}
3. Método avanzado más completo
- Funciones editables
onlyOwner
- Split de fees a diferentes billeteras
// SPDX-License-Identifier: MIT
pragma solidity 0.8.12;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
interface IUniswapV2Factory {
function createPair(address tokenA, address tokenB) external returns (address pair);
}
interface IUniswapV2Router02 {
function factory() external pure returns (address);
function WETH() external pure returns (address);
}
contract MyToken is ERC20, Ownable
{
uint internal wallet_a_collected;
uint internal wallet_b_collected;
address public wallet_a;
address public wallet_b;
address public pair;
enum FeesIndex{ BUY, SELL, P2P }
uint[] public wallet_a_fee_percentages;
uint[] public wallet_b_fee_percentages;
uint public fee_decimal = 2;
mapping(address => bool) public is_taxless;
bool private is_in_fee_transfer;
constructor () ERC20("My Token", "TKN")
{
IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02(0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff);
pair = IUniswapV2Factory(_uniswapV2Router.factory()).createPair(address(this), _uniswapV2Router.WETH());
// Edit here
wallet_a = 0xb6F5414bAb8d5ad8F33E37591C02f7284E974FcB;
wallet_a = 0xb6F5414bAb8d5ad8F33E37591C02f7284E974FcB;
wallet_a_fee_percentages.push(500); // Buy fee is 5.00%
wallet_a_fee_percentages.push(500); // Sell fee is 5.00%
wallet_a_fee_percentages.push(500); // Buy fee is 5.00%
wallet_b_fee_percentages.push(1000); // Buy fee is 10.00%
wallet_b_fee_percentages.push(1500); // Sell fee is 15.00%
wallet_b_fee_percentages.push(500); // Buy fee is 5.00%
// End edit
is_taxless[msg.sender] = true;
is_taxless[wallet_a] = true;
is_taxless[wallet_b] = true;
is_taxless[address(this)] = true;
is_taxless[address(0)] = true;
_mint(msg.sender, 1_000_000 ether);
}
function _afterTokenTransfer(
address from,
address to,
uint amount
) internal virtual override(ERC20)
{
super._afterTokenTransfer(from, to, amount);
if(!is_in_fee_transfer)
{
uint fees_collected;
if (!is_taxless[from] && !is_taxless[to]) {
uint wallet_a_fee;
uint wallet_b_fee;
bool sell = to == pair;
bool p2p = from != pair && to != pair;
(wallet_a_fee, wallet_b_fee) = calculateFee(p2p ? FeesIndex.P2P : sell ? FeesIndex.SELL : FeesIndex.BUY, amount);
wallet_a_collected += wallet_a_fee;
wallet_b_collected += wallet_b_fee;
fees_collected += wallet_a_fee + wallet_b_fee;
}
if(fees_collected > 0)
{
is_in_fee_transfer = true;
_transfer(to, address(this), fees_collected);
is_in_fee_transfer = false;
}
}
}
function calculateFee(FeesIndex fee_index, uint amount) internal view returns(uint, uint) {
uint wallet_a_fee = (amount * wallet_a_fee_percentages[uint(fee_index)]) / (10**(fee_decimal + 2));
uint wallet_b_fee = (amount * wallet_b_fee_percentages[uint(fee_index)]) / (10**(fee_decimal + 2));
return (wallet_a_fee, wallet_b_fee);
}
// Owner admin functions
function setWalletA(address wallet) external onlyOwner {
wallet_a = wallet;
}
function setWalletB(address wallet) external onlyOwner {
wallet_b = wallet;
}
function setWalletAFee(uint buy, uint sell, uint p2p) external onlyOwner {
wallet_a_fee_percentages[0] = buy;
wallet_a_fee_percentages[1] = sell;
wallet_a_fee_percentages[2] = p2p;
}
function setWalletBFee(uint buy, uint sell, uint p2p) external onlyOwner {
wallet_b_fee_percentages[0] = buy;
wallet_b_fee_percentages[1] = sell;
wallet_b_fee_percentages[2] = p2p;
}
function setIsTaxless(address _address, bool value) external onlyOwner {
is_taxless[_address] = value;
}
// Fee collector functions
function collectWalletAFee() external {
require(msg.sender == wallet_a, "Sender must be buy address");
wallet_a_collected = 0;
transfer(wallet_a, wallet_a_collected);
}
function collectWalletBFee() external {
require(msg.sender == wallet_b, "Sender must be buy address");
wallet_b_collected = 0;
transfer(wallet_b, wallet_b_collected);
}
}
¡Gracias por ver este tutorial!
Sígueme en dev.to y en Youtube para todo lo relacionado al desarrollo en Blockchain en Español.
Posted on April 5, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
August 30, 2024