Created
January 11, 2025 02:17
-
-
Save Git-on-my-level/59aacf73aa3b0257e93b9a9b2cdb1b65 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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