1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 | 546× 546× 546× 545× 545× 1× 1× 1× 1× 2× 2× 1× 1× 42× 41× 40× 5× 5× 5× 197× 197× 197× 16× 16× 16× 191× 191× 191× 191× 191× 191× 1× 190× 191× 188× 187× 186× 167× 36× 36× 36× 36× 36× 36× 33× 36× 36× 33× 33× 32× 32× 9× 9× 9× 5× 5× 5× 5× 17× 17× 17× 17× 7× 28× 28× 28× 28× 25× 25× 25× 25× 25× 24× 13× 3× 3× 14× 13× 10× 10× | // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import "../BaseLogic.sol"; /// @notice Tokenised representation of debts contract DToken is BaseLogic { constructor(bytes32 moduleGitCommit_) BaseLogic(MODULEID__DTOKEN, moduleGitCommit_) {} function CALLER() private view returns (address underlying, AssetStorage storage assetStorage, address proxyAddr, address msgSender) { (msgSender, proxyAddr) = unpackTrailingParams(); address eTokenAddress = dTokenLookup[proxyAddr]; require(eTokenAddress != address(0), "e/unrecognized-dtoken-caller"); assetStorage = eTokenLookup[eTokenAddress]; underlying = assetStorage.underlying; } // Events event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); // External methods /// @notice Debt token name, ie "Euler Debt: DAI" function name() external view returns (string memory) { (address underlying,,,) = CALLER(); return string(abi.encodePacked("Euler Debt: ", IERC20(underlying).name())); } /// @notice Debt token symbol, ie "dDAI" function symbol() external view returns (string memory) { (address underlying,,,) = CALLER(); return string(abi.encodePacked("d", IERC20(underlying).symbol())); } /// @notice Decimals of underlying function decimals() external view returns (uint8) { (,AssetStorage storage assetStorage,,) = CALLER(); return assetStorage.underlyingDecimals; } /// @notice Address of underlying asset function underlyingAsset() external view returns (address) { (address underlying,,,) = CALLER(); return underlying; } /// @notice Sum of all outstanding debts, in underlying units (increases as interest is accrued) function totalSupply() external view returns (uint) { (address underlying, AssetStorage storage assetStorage,,) = CALLER(); AssetCache memory assetCache = loadAssetCacheRO(underlying, assetStorage); return assetCache.totalBorrows / INTERNAL_DEBT_PRECISION / assetCache.underlyingDecimalsScaler; } /// @notice Sum of all outstanding debts, in underlying units normalized to 27 decimals (increases as interest is accrued) function totalSupplyExact() external view returns (uint) { (address underlying, AssetStorage storage assetStorage,,) = CALLER(); AssetCache memory assetCache = loadAssetCacheRO(underlying, assetStorage); return assetCache.totalBorrows; } /// @notice Debt owed by a particular account, in underlying units function balanceOf(address account) external view returns (uint) { (address underlying, AssetStorage storage assetStorage,,) = CALLER(); AssetCache memory assetCache = loadAssetCacheRO(underlying, assetStorage); return getCurrentOwed(assetStorage, assetCache, account) / assetCache.underlyingDecimalsScaler; } /// @notice Debt owed by a particular account, in underlying units normalized to 27 decimals function balanceOfExact(address account) external view returns (uint) { (address underlying, AssetStorage storage assetStorage,,) = CALLER(); AssetCache memory assetCache = loadAssetCacheRO(underlying, assetStorage); return getCurrentOwedExact(assetStorage, assetCache, account, assetStorage.users[account].owed); } /// @notice Transfer underlying tokens from the Euler pool to the sender, and increase sender's dTokens /// @param subAccountId 0 for primary, 1-255 for a sub-account /// @param amount In underlying units (use max uint256 for all available tokens) function borrow(uint subAccountId, uint amount) external nonReentrant { (address underlying, AssetStorage storage assetStorage, address proxyAddr, address msgSender) = CALLER(); address account = getSubAccount(msgSender, subAccountId); updateAverageLiquidity(account); emit RequestBorrow(account, amount); AssetCache memory assetCache = loadAssetCache(underlying, assetStorage); if (amount == type(uint).max) { amount = assetCache.poolSize; } else { amount = decodeExternalAmount(assetCache, amount); } require(amount <= assetCache.poolSize, "e/insufficient-tokens-available"); pushTokens(assetCache, msgSender, amount); increaseBorrow(assetStorage, assetCache, proxyAddr, account, amount); checkLiquidity(account); logAssetStatus(assetCache); } /// @notice Transfer underlying tokens from the sender to the Euler pool, and decrease sender's dTokens /// @param subAccountId 0 for primary, 1-255 for a sub-account /// @param amount In underlying units (use max uint256 for full debt owed) function repay(uint subAccountId, uint amount) external nonReentrant { (address underlying, AssetStorage storage assetStorage, address proxyAddr, address msgSender) = CALLER(); address account = getSubAccount(msgSender, subAccountId); updateAverageLiquidity(account); emit RequestRepay(account, amount); AssetCache memory assetCache = loadAssetCache(underlying, assetStorage); if (amount != type(uint).max) { amount = decodeExternalAmount(assetCache, amount); } uint owed = getCurrentOwed(assetStorage, assetCache, account); if (owed == 0) return; if (amount > owed) amount = owed; amount = pullTokens(assetCache, msgSender, amount); decreaseBorrow(assetStorage, assetCache, proxyAddr, account, amount); logAssetStatus(assetCache); } /// @notice Allow spender to send an amount of dTokens to a particular sub-account /// @param subAccountId 0 for primary, 1-255 for a sub-account /// @param spender Trusted address /// @param amount In underlying units (use max uint256 for "infinite" allowance) function approveDebt(uint subAccountId, address spender, uint amount) public nonReentrant returns (bool) { (address underlying, AssetStorage storage assetStorage, address proxyAddr, address msgSender) = CALLER(); address account = getSubAccount(msgSender, subAccountId); require(!isSubAccountOf(spender, account), "e/self-approval"); AssetCache memory assetCache = loadAssetCache(underlying, assetStorage); assetStorage.dTokenAllowance[account][spender] = amount == type(uint).max ? type(uint).max : decodeExternalAmount(assetCache, amount); emitViaProxy_Approval(proxyAddr, account, spender, amount); return true; } /// @notice Retrieve the current debt allowance /// @param holder Xor with the desired sub-account ID (if applicable) /// @param spender Trusted address function debtAllowance(address holder, address spender) external view returns (uint) { (address underlying, AssetStorage storage assetStorage,,) = CALLER(); AssetCache memory assetCache = loadAssetCacheRO(underlying, assetStorage); uint allowance = assetStorage.dTokenAllowance[holder][spender]; return allowance == type(uint).max ? type(uint).max : allowance / assetCache.underlyingDecimalsScaler; } /// @notice Transfer dTokens to another address (from sub-account 0) /// @param to Xor with the desired sub-account ID (if applicable) /// @param amount In underlying units. Use max uint256 for full balance. function transfer(address to, uint amount) external reentrantOK returns (bool) { return transferFrom(address(0), to, amount); } /// @notice Transfer dTokens from one address to another /// @param from Xor with the desired sub-account ID (if applicable) /// @param to This address must've approved the from address, or be a sub-account of msg.sender /// @param amount In underlying units. Use max uint256 for full balance. function transferFrom(address from, address to, uint amount) public nonReentrant returns (bool) { (address underlying, AssetStorage storage assetStorage, address proxyAddr, address msgSender) = CALLER(); AssetCache memory assetCache = loadAssetCache(underlying, assetStorage); if (from == address(0)) from = msgSender; require(from != to, "e/self-transfer"); updateAverageLiquidity(from); updateAverageLiquidity(to); emit RequestTransferDToken(from, to, amount); if (amount == type(uint).max) amount = getCurrentOwed(assetStorage, assetCache, from); else amount = decodeExternalAmount(assetCache, amount); if (amount == 0) return true; if (!isSubAccountOf(msgSender, to) && assetStorage.dTokenAllowance[to][msgSender] != type(uint).max) { require(assetStorage.dTokenAllowance[to][msgSender] >= amount, "e/insufficient-debt-allowance"); unchecked { assetStorage.dTokenAllowance[to][msgSender] -= amount; } emitViaProxy_Approval(proxyAddr, to, msgSender, assetStorage.dTokenAllowance[to][msgSender] / assetCache.underlyingDecimalsScaler); } transferBorrow(assetStorage, assetCache, proxyAddr, from, to, amount); checkLiquidity(to); logAssetStatus(assetCache); return true; } } |