DEV Community

Cover image for Ingresos extra fácil desde Aave en Solidity 💸
Ahmed Castro
Ahmed Castro

Posted on • Edited on

Ingresos extra fácil desde Aave en Solidity 💸

¿Es posible generar ingresos pasivos en el tesoro de tu DAO? ¿O durante tu preventa de tokens, subasta o en cualquier proyecto donde tengas tokens ERC20 inactivos en un contrato? En esta guía, lanzaremos un contrato de staking que realiza préstamos en AAVE en segundo plano.

Vamos a lanzar este contrato en Scroll Sepolia, pero también te mostraré qué se debe hacer para lanzarlo en cualquier otra red donde esté desplegado AAVE.

Nota: Vamos a usar Remix y Etherscan para lanzar e interactuar con los contratos. También puedes usar este repo como alternativa que incluye comentarios de código, unit tests en foundry, y una github action.

Paso1: Lanza el contrato Aave Lender

Aave Pool Supply

Si no lo has hecho aún, necesitarás instalar una wallet como Metamask, conectar tu wallet a Scroll Sepolia, y obtener fondos desde un faucet.

Lanza el siguiente contrato en Remix pasando las direcciones aavePoolAddress y stakedTokenAddress como parámetros en el constructor, en Scroll Sepolia estas son:

  • aavePoolAddress: 0x48914C788295b5db23aF2b5F0B3BE775C4eA9440
  • stakedTokenAddress: 0x7984E363c38b590bB4CA35aEd5133Ef2c6619C40, el DAI testnet en Scroll Sepolia
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

library DataTypes {
  struct ReserveConfigurationMap {
    uint256 data;
  }

  struct ReserveData {
    ReserveConfigurationMap configuration;
    uint128 liquidityIndex;
    uint128 currentLiquidityRate;
    uint128 variableBorrowIndex;
    uint128 currentVariableBorrowRate;
    uint128 currentStableBorrowRate;
    uint40 lastUpdateTimestamp;
    uint16 id;
    address aTokenAddress;
    address stableDebtTokenAddress;
    address variableDebtTokenAddress;
    address interestRateStrategyAddress;
    uint128 accruedToTreasury;
    uint128 unbacked;
    uint128 isolationModeTotalDebt;
  }
}

interface IPool {
    function borrow(
        address asset,
        uint256 amount,
        uint256 interestRateMode, // 1 for Stable, 2 for Variable
        uint16 referralCode,
        address onBehalfOf) external;

    function supply(
        address asset,
        uint256 amount,
        address onBehalfOf,
        uint16 referralCode) external;

    function withdraw(
        address asset,
        uint256 amount,
        address to) external returns (uint256);

    function getReserveData(
        address asset) external view returns (DataTypes.ReserveData memory);
}

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address to, uint256 value) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 value) external returns (bool);
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

contract AaveLender {
    address public immutable AAVE_POOL_ADDRESS;
    address public immutable STAKED_TOKEN_ADDRESS;
    address public immutable ATOKEN_ADDRESS;
    address public immutable OWNER;
    mapping(address account => uint amount) public stakeByAccount;
    uint public totalStake;

    constructor(address aavePoolAddress, address stakedTokenAddress) {
        AAVE_POOL_ADDRESS = aavePoolAddress;
        STAKED_TOKEN_ADDRESS = stakedTokenAddress;
        OWNER = msg.sender;
        ATOKEN_ADDRESS = IPool(aavePoolAddress).getReserveData(stakedTokenAddress).aTokenAddress;
    }

    function stake(uint amount) public {
        totalStake += amount;
        stakeByAccount[msg.sender] += amount;
        IERC20(STAKED_TOKEN_ADDRESS).transferFrom(msg.sender, address(this), amount);
        IERC20(STAKED_TOKEN_ADDRESS).approve(AAVE_POOL_ADDRESS, amount);
        IPool(AAVE_POOL_ADDRESS).supply(
            STAKED_TOKEN_ADDRESS,
            amount,
            address(this),
            0);
    }

    function unstake(uint amount) public {
        require(amount <= stakeByAccount[msg.sender], "Not enough stake");
        totalStake -= amount;
        stakeByAccount[msg.sender] -= amount;
        IPool(AAVE_POOL_ADDRESS).withdraw(
          STAKED_TOKEN_ADDRESS,
          amount,
          msg.sender
        );
    }

    function yieldEarned() public view returns(uint){
        return IERC20(ATOKEN_ADDRESS).balanceOf(address(this)) - totalStake;
    }

    function withdraw(uint amount) public {
        require(msg.sender == OWNER, "Sender is not owner");
        require(amount <= yieldEarned(), "Maximum withdraw exceeded");
        IPool(AAVE_POOL_ADDRESS).withdraw(
          STAKED_TOKEN_ADDRESS,
          amount,
          msg.sender
        );
    }
}
Enter fullscreen mode Exit fullscreen mode

Paso 2: Mintea DAI en testnet

Dirígete ahora al Faucet de Aave y conecta tu wallet desde Scroll Sepolia. Haz click en el botón Faucet de DAI y confirma la transacción.

Faucet de Aave

Una vez confirmada la transacción podrás ver en tu wallet tus DAIs en testnet si agregas su address de token 0x7984E363c38b590bB4CA35aEd5133Ef2c6619C40.

Recuerda que no puedes hacer borrow y lending de cualquier token en Aave, únicamente de los que el protocolo le haya dado soporte de antemano. Y también toma nota que esta versión de DAI testnet en Scroll Sepolia no es la misma que la oficial circulando en los demás protocolos de Sepolia y Scroll Sepolia como Uniswap v3. Esta es una versión especial deployada por Aave y es la única versión compatible con Aave en el Testnet de Scroll. Si estás en cualquier red en mainnet sí deberás usar la versión oficial del token.

Paso 3: Aprueba el contrato de Aave Lending

Necesitamos aprobar el contrato de staking que recién lanzamos para que pueda acceder a los fondos.

Esto lo vamos a hacer en el Scroll-Etherscan.

En el contrato de DAI en: 0x7984E363c38b590bB4CA35aEd5133Ef2c6619C40, conecta tu wallet y pasa los siguientes parámetros en la función approve.

  • spender: El address del contrato de staking que recién lanzamos
  • amount: 100000000000000000000 que es 100 DAI en el formato wei.

Approve AaveLender contract

Paso 4: Obtener ganancias

Ahora puedes stakear llamando la función stake y pasando como párametro la cantidad de DAI que aprobaste.

stake and lend on AAVE

En cada bloque, las ganancias (el yield) irán aumentando poco a poco. Las puedes observar llamando la función yieldEarned. Las ganancias pueden ser enviadas al owner llamando la función withdraw.

withdraw yield earned on aave

Próximos pasos

Este tutorial muestra cómo prestar en AAVE desde un smart contract con el código mínimo necesario. Ahora depende de ti utilizarlo para mejorar un proyecto existente o crear algo nuevo. Personalmente, me gustaría agregar este contrato a un sistema de lotería en el que estoy trabajando, donde mientras se elige el ganador el contrato deposita todas las recaudaciones en Aave. Déjame saber en los comentarios si tienes ideas o preguntas sobre este tutorial.

Conoce más sobre Aave desde la documentación oficial.

¡Gracias por ver este tutorial!

Sígueme en dev.to y en Youtube para todo lo relacionado al desarrollo en Blockchain en Español.

Top comments (2)

Collapse
 
rubsoft profile image
Ruben Monrroy

Lo revierte la operación enviando el siguiente error :
[block:4401995 txIndex:6]from: 0xF26...16A31to: AaveLender.(constructor)value: 100 weidata: 0x610...19c40logs: 0hash: 0xfef...1ef10

Ante de eso cuando se hace la firma en el metamask envia la siguiente alerta :

Gas estimation failed
Gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending?
Returned error: {"jsonrpc":"2.0","error":"execution reverted","id":3115197577395175}

Collapse
 
turupawn profile image
Ahmed Castro

Hola amigo, un poco tarde en responder pero quizás te ayude a debuggear este video que hice sobre este artículo
youtube.com/watch?v=6GCktUAPbkk