Skip to content

Instantly share code, notes, and snippets.

@Git-on-my-level
Created January 11, 2025 02:17
Show Gist options
  • Select an option

  • Save Git-on-my-level/59aacf73aa3b0257e93b9a9b2cdb1b65 to your computer and use it in GitHub Desktop.

Select an option

Save Git-on-my-level/59aacf73aa3b0257e93b9a9b2cdb1b65 to your computer and use it in GitHub Desktop.
pragma solidity ^0.8.10;
import "./interfaces/IBalanceChecker.sol";
import "../lending/core/interfaces/IAToken.sol";
import "../lending/core/interfaces/IVariableDebtToken.sol";
import "../lending/core/interfaces/IPool.sol";
/// @notice Error thrown when trying to get balances for an external token that isn't mapped to a dToken
error ExternalTokenNotMapped(address externalToken);
/**
* @title dLendBalanceChecker
* @author dTrinity
* @notice Contract for checking effective balances of dLEND tokens
* @dev The effective balance is the portion of a user's position which is not borrowed against,
* calculated as: userBalance * (totalSupply - totalDebt) / totalSupply
*/
contract dLendBalanceChecker is IBalanceChecker {
/// @notice The Pool contract address
IPool public immutable POOL;
/// @notice Mapping from external token to its corresponding dToken
mapping(address => address) public externalSourceToDToken;
/// @notice Maximum number of addresses that can be checked in a single call
uint256 private constant MAX_ADDRESSES = 1000;
/**
* @param pool The address of the Pool contract
*/
constructor(address pool) {
require(pool != address(0), "INVALID_POOL_ADDRESS");
POOL = IPool(pool);
}
/**
* @notice Maps an external token to its corresponding dToken
* @param externalToken The address of the external token
* @param dToken The address of the corresponding dToken
*/
function mapExternalSource(address externalToken, address dToken) external {
require(
externalToken != address(0) && dToken != address(0),
"INVALID_ADDRESS"
);
externalSourceToDToken[externalToken] = dToken;
}
/**
* @inheritdoc IBalanceChecker
*/
function tokenBalances(
address token,
address[] memory addresses
) external view override returns (uint256[] memory result) {
require(addresses.length <= MAX_ADDRESSES, "TOO_MANY_ADDRESSES");
require(token != address(0), "INVALID_TOKEN_ADDRESS");
result = new uint256[](addresses.length);
// Get associated variable debt token
address underlyingAsset = IAToken(token).UNDERLYING_ASSET_ADDRESS();
address debtToken = POOL
.getReserveData(underlyingAsset)
.variableDebtTokenAddress;
// If no debt token found, check if this is an external token
address originalToken = token;
if (debtToken == address(0)) {
address mappedDToken = externalSourceToDToken[token];
if (mappedDToken == address(0)) {
revert ExternalTokenNotMapped(token);
}
// Use the mapped dToken for LTV calculations
token = mappedDToken;
underlyingAsset = IAToken(token).UNDERLYING_ASSET_ADDRESS();
debtToken = POOL
.getReserveData(underlyingAsset)
.variableDebtTokenAddress;
}
// Get total supply from dToken
uint256 totalSupply = IAToken(token).totalSupply();
if (totalSupply == 0) return result;
// Get total debt from dToken
uint256 totalDebt = IVariableDebtToken(debtToken).scaledTotalSupply();
// If total debt >= total supply, effective balance is 0 for everyone
if (totalDebt >= totalSupply) return result;
// Calculate the ratio with 18 decimals precision
uint256 ratio = ((totalSupply - totalDebt) * 1e18) / totalSupply;
// Calculate effective balance for each address
for (uint256 i = 0; i < addresses.length; i++) {
// Use original token for balance lookup, but dToken's LTV ratio
uint256 balance = IAToken(originalToken).balanceOf(addresses[i]);
// Apply ratio to get effective balance, maintaining 18 decimals precision
result[i] = (balance * ratio) / 1e18;
}
}
/**
* @inheritdoc IBalanceChecker
*/
function batchTokenBalances(
address[] memory sources,
address[] memory addresses
) external view override returns (uint256[] memory result) {
require(addresses.length <= MAX_ADDRESSES, "TOO_MANY_ADDRESSES");
require(sources.length > 0, "NO_SOURCES_PROVIDED");
result = new uint256[](addresses.length);
// Process each source token and accumulate balances
for (uint256 i = 0; i < sources.length; i++) {
uint256[] memory sourceBalances = this.tokenBalances(
sources[i],
addresses
);
for (uint256 j = 0; j < addresses.length; j++) {
result[j] += sourceBalances[j];
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment