- tags: Blockchain, ETH, DeFi
AAVE 的收益直接体现在接收的 AToken
上,比如 aEthUSDT,可以通过 balanceOf
查询代币余额,余额及本金+收益,
所以思路是看下 balanceOf
的实现即可推算出计算收益的方式,其实现如下1:
/// @inheritdoc IERC20
function balanceOf(
address user
) public view virtual override(IncentivizedERC20, IERC20) returns (uint256) {
return super.balanceOf(user).rayMul(POOL.getReserveNormalizedIncome(_underlyingAsset));
}
上面代码调用标准的 IERC20
获取用户的余额,代表了用户的原始投入,然后乘了一个系数,这个系数应该就是代表了用户的收益,对应的实现2:
/// @inheritdoc IPool
function getReserveNormalizedIncome(
address asset
) external view virtual override returns (uint256) {
return _reserves[asset].getNormalizedIncome();
}
继续跟踪代码3:
/**
* @notice Returns the ongoing normalized income for the reserve.
* @dev A value of 1e27 means there is no income. As time passes, the income is accrued
* @dev A value of 2*1e27 means for each unit of asset one unit of income has been accrued
* @param reserve The reserve object
* @return The normalized income, expressed in ray
*/
function getNormalizedIncome(
DataTypes.ReserveData storage reserve
) internal view returns (uint256) {
uint40 timestamp = reserve.lastUpdateTimestamp;
//solium-disable-next-line
if (timestamp == block.timestamp) {
//if the index was updated in the same block, no need to perform any calculation
return reserve.liquidityIndex;
} else {
return
MathUtils.calculateLinearInterest(reserve.currentLiquidityRate, timestamp).rayMul(
reserve.liquidityIndex
);
}
}
可以看到这里涉及一个 reserve.liquidityIndex
的存储变量,通过查阅文档我们可以通过 Pool.getReserveData(address asset)
来获取4:
function getReserveData(address asset) external view virtual override returns (DataTypes.ReserveData memory)
读取的 Go 代码:
package main
import (
"context"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rpc"
"github.com/shopspring/decimal"
)
func getAAVELiquidityNetValue() (decimal.Decimal, error){
assetAddress := common.HexToAddress("0xdAC17F958D2ee523a2206206994597C13D831ec7") // USDT
fun := []byte("getReserveData(address)")
hash := crypto.NewKeccakState()
hash.Write(fun)
methodID := hash.Sum(nil)[:4]
var data []byte
data = append(data, methodID...)
data = append(data, common.LeftPadBytes(assetAddress.Bytes(), 32)...)
callMsg := map[string]interface{}{
"from": assetAddress,
"to": common.HexToAddress(aavePool),
"data": hexutil.Bytes(data),
}
var result string
rpcClient, err := rpc.DialHTTP(client.url)
if err != nil {
return decimal.Zero, err
}
err := rpcClient.CallContext(context.TODO(), &result, "eth_call", callMsg, nil)
if err != nil {
return decimal.Zero, err
}
bi := new(big.Int).SetBytes(common.FromHex(result[66 : 66+64]))
return decimal.NewFromBigInt(bi, -27), nil
}
计算收益的方法参考5:
uint256 scaledBalance = AToken(asset_address).scaledBalanceOf(address(this));
uint256 value_now = (scaledBalance * liquidityIndex) * 1e18 / 1e27;
-
https://github.com/aave/aave-v3-core/blob/782f51917056a53a2c228701058a6c3fb233684a/contracts/protocol/tokenization/AToken.sol#L127-L132 ↩︎
-
https://github.com/aave/aave-v3-core/blob/782f51917056a53a2c228701058a6c3fb233684a/contracts/protocol/pool/Pool.sol#L501 ↩︎
-
https://github.com/aave/aave-v3-core/blob/782f51917056a53a2c228701058a6c3fb233684a/contracts/protocol/libraries/logic/ReserveLogic.sol#L47 ↩︎
-
https://aave.com/docs/developers/smart-contracts/pool#view-methods-getreservedata ↩︎