Alchemix contest - MiloTruck's results

A protocol for self-repaying loans with no liquidation risk.

General Information

Platform: Code4rena

Start Date: 05/05/2022

Pot Size: $125,000 DAI

Total HM: 17

Participants: 62

Period: 14 days

Judge: leastwood

Total Solo HM: 15

Id: 120

League: ETH

Alchemix

Findings Distribution

Researcher Performance

Rank: 34/62

Findings: 2

Award: $270.50

🌟 Selected for report: 0

πŸš€ Solo Findings: 0

QA Report

Non-critical issues

Floating compiler versions

Non-library/interface files should use fixed compiler versions, not floating ones:

contracts-full/AlchemicTokenV2.sol:
   2:        pragma solidity ^0.8.11;

contracts-full/CrossChainCanonicalAlchemicTokenV2.sol:
   2:        pragma solidity ^0.8.11;

contracts-full/gALCX.sol:
   2:        pragma solidity ^0.8.11;

contracts-full/TransmuterBuffer.sol:
   2:        pragma solidity ^0.8.11;

contracts-full/TransmuterV2.sol:
   2:        pragma solidity ^0.8.11;

contracts-full/WETHGateway.sol:
   1:        pragma solidity ^0.8.11;

contracts-full/AutoleverageCurveMetapool.sol:
   2:        pragma solidity ^0.8.11;

contracts-full/AutoleverageCurveFactoryethpool.sol:
   2:        pragma solidity ^0.8.11;

contracts-full/StakingPools.sol:
   2:        pragma solidity ^0.8.11;

contracts-full/CrossChainCanonicalGALCX.sol:
   2:        pragma solidity ^0.8.11;

contracts-full/AlchemistV2.sol:
   1:        pragma solidity ^0.8.11;

contracts-full/adapters/yearn/YearnTokenAdapter.sol:
   1:        pragma solidity ^0.8.11;

contracts-full/utils/Whitelist.sol:
   1:        pragma solidity ^0.8.11;

event is missing indexed fields

Each event should use three indexed fields if there are three or more fields:

contracts-full/ThreePoolAssetManager.sol:
 115:        event MintThreePoolTokens(ThreePoolAsset asset, uint256 amount, uint256 mintedThreePoolTokens);
 122:        event BurnThreePoolTokens(ThreePoolAsset asset, uint256 amount, uint256 withdrawn);
 135:        event MintMetaPoolTokens(MetaPoolAsset asset, uint256 amount, uint256 minted);
 142:        event BurnMetaPoolTokens(MetaPoolAsset asset, uint256 amount, uint256 withdrawn);
 161:        event ClaimRewards(bool success, uint256 amountCurve, uint256 amountConvex);

contracts-full/gALCX.sol:
  20:        event Stake(address _from, uint _gAmount, uint _amount);
  21:        event Unstake(address _from, uint _gAmount, uint _amount);

contracts-full/EthAssetManager.sol:
  97:        event MintMetaPoolTokens(MetaPoolAsset asset, uint256 amount, uint256 minted);
 104:        event BurnMetaPoolTokens(MetaPoolAsset asset, uint256 amount, uint256 withdrawn);
 123:        event ClaimRewards(bool success, uint256 amountCurve, uint256 amountConvex);

contracts-full/StakingPools.sol:
  63:        event TokensDeposited(
  64:            address indexed user,
  65:            uint256 indexed poolId,
  66:            uint256 amount
  67:          );

  69:        event TokensWithdrawn(
  70:            address indexed user,
  71:            uint256 indexed poolId,
  72:            uint256 amount
  73:          );

  75:        event TokensClaimed(
  76:            address indexed user,
  77:            uint256 indexed poolId,
  78:            uint256 amount
  79:          );

Gas Optimization Report

Unecessary initialization of variables with default values

Uninitialized variables are assigned with a default value depending on its type:

  • uint: 0
  • bool: false
  • address: address(0)

Thus, explicitly initializing a variable with its default value costs unnecesary gas. For example, the following code:

bool b = false;
address c = address(0);
uint256 a = 0;
for (uint256 i = 0; i < length; ++i) {

can be changed to:

uint256 a;
bool b;
address c;
for (uint256 i; i < length; ++i) {

Consider declaring the following lines without explicitly setting a value:

contracts-full/ThreePoolAssetManager.sol:
 250:        for (uint256 i = 0; i < NUM_STABLE_COINS; i++) {
 254:        for (uint256 i = 0; i < NUM_META_COINS; i++) {
 353:        for (uint256 i = 0; i < 256; i++) {
 771:        uint256 normalizedTotal   = 0;
 773:        for (uint256 i = 0; i < NUM_STABLE_COINS; i++) {
 901:        uint256 total = 0;
 902:        for (uint256 i = 0; i < NUM_META_COINS; i++) {

contracts-full/TransmuterBuffer.sol:
 172:        for (uint256 i = 0; i < _yieldTokens[underlyingToken].length; i++) {
 186:        for (uint256 i = 0; i < tokens.length; i++) {
 235:        for (uint256 i = 0; i < registeredUnderlyings.length; i++) {
 242:        for (uint256 i = 0; i < registeredUnderlyings.length; i++) {
 272:        for (uint256 i = 0; i < registeredUnderlyings.length; i++) {
 382:        for (uint256 j = 0; j < registeredUnderlyings.length; j++) {
 387:        for (uint256 i = 0; i < numYTokens; i++) {
 479:        for (uint256 j = 0; j < weighting.tokens.length; j++) {
 534:        uint256 want = 0;
 549:        uint256 exchangeDelta = 0;

contracts-full/TransmuterV2.sol:
  96:        address public constant ZERO_ADDRESS = address(0);

contracts-full/EthAssetManager.sol:
 214:        for (uint256 i = 0; i < NUM_META_COINS; i++) {
 566:        uint256 total = 0;
 567:        for (uint256 i = 0; i < NUM_META_COINS; i++) {

contracts-full/StakingPools.sol:
 188:        for (uint256 _poolId = 0; _poolId < _pools.length(); _poolId++) {
 363:        for (uint256 _poolId = 0; _poolId < _pools.length(); _poolId++) {

contracts-full/AlchemistV2.sol:
 990:        for (uint256 i = 0; i < depositedTokens.values.length; i++) {
1282:        for (uint256 i = 0; i < depositedTokens.values.length; i++) {
1355:        for (uint256 i = 0; i < depositedTokens.values.length; i++) {
1458:        uint256 totalValue = 0;
1461:        for (uint256 i = 0; i < depositedTokens.values.length; i++) {
1524:        for (uint256 i = 0; i < depositedTokens.values.length; i++) {

contracts-full/adapters/fuse/FuseTokenAdapterV1.sol:
  36:        uint256 private constant NO_ERROR = 0;

For-Loops: Cache array length outside of loops

Reading an array length at each iteration of the loop takes 6 gas (3 for mload and 3 to place memory_offset) in the stack.

Caching the array length in the stack saves around 3 gas per iteration.

For example:

for (uint256 i; i < arr.length; ++i) {}

can be changed to:

uint256 len = arr.length; for (uint256 i; i < len; ++i) {}

Consider making the following change to these lines:

contracts-full/TransmuterBuffer.sol:
 186:        for (uint256 i = 0; i < tokens.length; i++) {
 235:        for (uint256 i = 0; i < registeredUnderlyings.length; i++) {
 242:        for (uint256 i = 0; i < registeredUnderlyings.length; i++) {
 272:        for (uint256 i = 0; i < registeredUnderlyings.length; i++) {
 382:        for (uint256 j = 0; j < registeredUnderlyings.length; j++) {
 479:        for (uint256 j = 0; j < weighting.tokens.length; j++) {

contracts-full/AlchemistV2.sol:
 990:        for (uint256 i = 0; i < depositedTokens.values.length; i++) {
1282:        for (uint256 i = 0; i < depositedTokens.values.length; i++) {
1355:        for (uint256 i = 0; i < depositedTokens.values.length; i++) {
1461:        for (uint256 i = 0; i < depositedTokens.values.length; i++) {
1524:        for (uint256 i = 0; i < depositedTokens.values.length; i++) {

For-Loops: Index increments can be left unchecked

From Solidity v0.8 onwards, all arithmetic operations come with implicit overflow and underflow checks.

In for-loops, as it is impossible for the index to overflow, it can be left unchecked to save gas every iteration.

For example, the code below:

for (uint256 i; i < numIterations; ++i) {  
    // ...  
}  

can be changed to:

for (uint256 i; i < numIterations;) { // ... unchecked { ++i; } }

Consider making the following change to these lines:

contracts-full/ThreePoolAssetManager.sol:
 250:        for (uint256 i = 0; i < NUM_STABLE_COINS; i++) {
 254:        for (uint256 i = 0; i < NUM_META_COINS; i++) {
 353:        for (uint256 i = 0; i < 256; i++) {
 773:        for (uint256 i = 0; i < NUM_STABLE_COINS; i++) {
 902:        for (uint256 i = 0; i < NUM_META_COINS; i++) {

contracts-full/TransmuterBuffer.sol:
 172:        for (uint256 i = 0; i < _yieldTokens[underlyingToken].length; i++) {
 186:        for (uint256 i = 0; i < tokens.length; i++) {
 235:        for (uint256 i = 0; i < registeredUnderlyings.length; i++) {
 242:        for (uint256 i = 0; i < registeredUnderlyings.length; i++) {
 272:        for (uint256 i = 0; i < registeredUnderlyings.length; i++) {
 382:        for (uint256 j = 0; j < registeredUnderlyings.length; j++) {
 387:        for (uint256 i = 0; i < numYTokens; i++) {
 479:        for (uint256 j = 0; j < weighting.tokens.length; j++) {

contracts-full/EthAssetManager.sol:
 214:        for (uint256 i = 0; i < NUM_META_COINS; i++) {
 567:        for (uint256 i = 0; i < NUM_META_COINS; i++) {

contracts-full/StakingPools.sol:
 188:        for (uint256 _poolId = 0; _poolId < _pools.length(); _poolId++) {
 363:        for (uint256 _poolId = 0; _poolId < _pools.length(); _poolId++) {

contracts-full/AlchemistV2.sol:
 990:        for (uint256 i = 0; i < depositedTokens.values.length; i++) {
1282:        for (uint256 i = 0; i < depositedTokens.values.length; i++) {
1355:        for (uint256 i = 0; i < depositedTokens.values.length; i++) {
1461:        for (uint256 i = 0; i < depositedTokens.values.length; i++) {
1524:        for (uint256 i = 0; i < depositedTokens.values.length; i++) {

Use != 0 instead of > 0 for unsigned integers

uint will go below 0. Thus, > 0 is gas inefficient in comparisons as checking if != 0 is sufficient and costs less gas.

Consider changing > 0 to != 0 in these lines:

contracts-full/gALCX.sol:
  75:        if (balance > 0) {

contracts-full/TransmuterBuffer.sol:
 521:        if (wantShares > 0) {
 544:        if (want > 0) {
 556:        if (exchangeDelta > 0) {

contracts-full/TransmuterV2.sol:
 342:        if (state.distributeAmount > 0) {
 409:        if (state.unexchangedBalance > 0 && satisfiedTick >= cache.occupiedTick) {
 417:        if (state.unexchangedBalance > 0) {
 459:        bool previouslyActive = cache.unexchangedBalance > 0;
 460:        bool currentlyActive = state.unexchangedBalance > 0;

contracts-full/AutoleverageCurveMetapool.sol:
  15:        if (msg.value > 0) revert IllegalArgument("msg.value should be 0");

contracts-full/AutoleverageCurveFactoryethpool.sol:
  22:        if (msg.value > 0) {

contracts-full/AlchemistV2.sol:
 353:        _checkArgument(config.creditUnlockBlocks > 0);
 466:        _checkArgument(blocks > 0);
 678:        _checkArgument(amount > 0);
 692:        _checkArgument(amount > 0);
 707:        _checkArgument(amount > 0);
 722:        _checkState((debt = _accounts[recipient].debt) > 0);
 746:        _checkArgument(amount > 0);
 764:        _checkState((debt = _accounts[recipient].debt) > 0);
 810:        _checkArgument(shares > 0);
 826:        _checkState((unrealizedDebt = _calculateUnrealizedDebt(msg.sender)) > 0);
 846:        _checkState(amountUnderlyingTokens > 0);
1103:        _checkArgument(amount > 0);
1267:        if (unlockedCredit > 0) {
1531:        currentAccruedWeight += unlockedCredit > 0

Reduce the length of error messages (long revert strings)

Shortening revert strings to fit in 32 bytes will decrease deployment time gas and will decrease runtime gas when the revert condition is met.

Revert strings that are longer than 32 bytes require at least one additional mstore, along with additional overhead for computing memory offset, etc.

In these instances, consider shortening the revert strings to fit within 32 bytes, or using custom errors:

contracts-full/StakingPools.sol:
 106:        require(_governance != address(0), "StakingPools: governance address cannot be 0x0");
 124:        require(_pendingGovernance != address(0), "StakingPools: pending governance address cannot be 0x0");
 131:        require(msg.sender == pendingGovernance, "StakingPools: only pending governance");
 160:        require(tokenPoolIds[_token] == 0, "StakingPools: token already has a pool");
 183:        require(_rewardWeights.length == _pools.length(), "StakingPools: weights length mismatch");

++i costs less gas compared to i++ or i += 1

++i costs less gas compared to i++ or i += 1 for unsigned integers, as pre-increment is cheaper (about 5 gas per iteration). This statement is true even with the optimizer enabled.

i++ increments i and returns the initial value of i. Which means:

uint i = 1;  
i++; // == 1 but i == 2  

But ++i returns the actual incremented value:

uint i = 1;  
++i; // == 2 and i == 2 too, so no need for a temporary variable  

In the first case, the compiler has to create a temporary variable (when used) for returning 1 instead of 2, thus it costs more gas.

The same logic applies for --i and i--.

Consider using ++i instead of i++ or i += 1 in the following instances:

contracts-full/ThreePoolAssetManager.sol:
 250:        for (uint256 i = 0; i < NUM_STABLE_COINS; i++) {
 254:        for (uint256 i = 0; i < NUM_META_COINS; i++) {
 353:        for (uint256 i = 0; i < 256; i++) {
 773:        for (uint256 i = 0; i < NUM_STABLE_COINS; i++) {
 902:        for (uint256 i = 0; i < NUM_META_COINS; i++) {

contracts-full/TransmuterBuffer.sol:
 172:        for (uint256 i = 0; i < _yieldTokens[underlyingToken].length; i++) {
 186:        for (uint256 i = 0; i < tokens.length; i++) {
 235:        for (uint256 i = 0; i < registeredUnderlyings.length; i++) {
 242:        for (uint256 i = 0; i < registeredUnderlyings.length; i++) {
 272:        for (uint256 i = 0; i < registeredUnderlyings.length; i++) {
 382:        for (uint256 j = 0; j < registeredUnderlyings.length; j++) {
 387:        for (uint256 i = 0; i < numYTokens; i++) {
 479:        for (uint256 j = 0; j < weighting.tokens.length; j++) {

contracts-full/EthAssetManager.sol:
 214:        for (uint256 i = 0; i < NUM_META_COINS; i++) {
 567:        for (uint256 i = 0; i < NUM_META_COINS; i++) {

contracts-full/StakingPools.sol:
 188:        for (uint256 _poolId = 0; _poolId < _pools.length(); _poolId++) {
 363:        for (uint256 _poolId = 0; _poolId < _pools.length(); _poolId++) {

contracts-full/AlchemistV2.sol:
 990:        for (uint256 i = 0; i < depositedTokens.values.length; i++) {
1282:        for (uint256 i = 0; i < depositedTokens.values.length; i++) {
1355:        for (uint256 i = 0; i < depositedTokens.values.length; i++) {
1461:        for (uint256 i = 0; i < depositedTokens.values.length; i++) {
1524:        for (uint256 i = 0; i < depositedTokens.values.length; i++) {

contracts-full/libraries/Tick.sol:
  38:        self.position++;

Use custom errors instead of revert strings

Since Solidity v0.8.4, custom errors should be used instead of revert strings due to:

  • Cheaper deployment cost
  • Lower runtime cost upon revert

Taken from Custom Errors in Solidity:

Starting from Solidity v0.8.4, there is a convenient and gas-efficient way to explain to users why an operation failed through the use of custom errors. Until now, you could already use strings to give more information about failures (e.g., revert("Insufficient funds.");), but they are rather expensive, especially when it comes to deploy cost, and it is difficult to use dynamic information in them.

Custom errors can be defined using of the error statement, both inside or outside of contracts.

Instances where custom errors can be used instead:

contracts-full/gALCX.sol:
  33:        require(msg.sender == owner, "Not owner");
  90:        require(success, "Transfer failed");
 107:        require(success, "Transfer failed"); 

contracts-full/StakingPools.sol:
 106:        require(_governance != address(0), "StakingPools: governance address cannot be 0x0");
 114:        require(msg.sender == governance, "StakingPools: only governance");
 124:        require(_pendingGovernance != address(0), "StakingPools: pending governance address cannot be 0x0");
 131:        require(msg.sender == pendingGovernance, "StakingPools: only pending governance");
 160:        require(tokenPoolIds[_token] == 0, "StakingPools: token already has a pool");
 183:        require(_rewardWeights.length == _pools.length(), "StakingPools: weights length mismatch");

Consider declaring constants as non-public to save gas

If a constant is not used outside of its contract, declaring it as private or internal instead of public can save gas.

Consider changing the visibility of the following from public to internal or private:

contracts-full/AlchemicTokenV2.sol:
  21:        bytes32 public constant ADMIN_ROLE = keccak256("ADMIN");
  24:        bytes32 public constant SENTINEL_ROLE = keccak256("SENTINEL");
  27:        bytes32 public constant CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan");

contracts-full/gALCX.sol:
  15:        uint public constant exchangeRatePrecision = 1e18;

contracts-full/TransmuterBuffer.sol:
  34:        bytes32 public constant KEEPER = keccak256("KEEPER");

contracts-full/TransmuterV2.sol:
  96:        address public constant ZERO_ADDRESS = address(0);
 102:        bytes32 public constant SENTINEL = keccak256("SENTINEL");

contracts-full/AutoleverageCurveFactoryethpool.sol:
  14:        address public constant wethAddress = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

contracts-full/libraries/FixedPointMath.sol:
   9:        uint256 public constant DECIMALS = 18;
  10:        uint256 public constant ONE = 10**DECIMALS;

contracts-full/libraries/Limiters.sol:
  12:        uint256 constant public MAX_COOLDOWN_BLOCKS = 7200;

public functions can be set to external

Calls to external functions are cheaper than public functions. Thus, if a function is not used internally in any contract, it should be set to external to save gas and improve code readability.

Consider changing following functions from public to external:

contracts-full/AlchemicTokenV2.sol:
 173:        function maxFlashLoan(address token) public view override returns (uint256) {

contracts-full/ThreePoolAssetManager.sol:
 298:        function exchangeRate(ThreePoolAsset asset) public view returns (uint256) {

 330:        function calculateRebalance(
 331:            MetaPoolAsset rebalanceAsset,
 332:            ThreePoolAsset targetExchangeAsset,
 333:            uint256 targetExchangeRate
 334:        ) public view returns (uint256 delta, bool add) {

 408:        function claimableRewards() public view returns (uint256 amountCurve, uint256 amountConvex) {
 717:        function reclaimThreePoolAsset(ThreePoolAsset asset, uint256 amount) public lock onlyAdmin {

contracts-full/TransmuterBuffer.sol:
 371:        function refreshStrategies() public override {

contracts-full/EthAssetManager.sol:
 253:        function exchangeRate() public view returns (uint256) {
 269:        function claimableRewards() public view returns (uint256 amountCurve, uint256 amountConvex) {
 494:        function reclaimEth(uint256 amount) public lock onlyAdmin {

Use Shift Right/Left instead of Division/Multiplication if possible

A division/multiplication by any number x being a power of 2 can be calculated by shifting log2(x) to the right/left.

While the DIV opcode uses 5 gas, the SHR opcode only uses 3 gas. Furthermore, Solidity's division operation also includes a division-by-0 prevention which is bypassed using shifting.

For example, the following code:

uint256 b = a / 2;
uint256 c = a / 4;
uint256 d = a * 8;

can be changed to:

uint256 b = a >> 1;
uint256 c = a >> 2;
uint256 d = a << 3;

Consider making this change to the following lines:

contracts-full/ThreePoolAssetManager.sol:
 355:        if ((examineBalance = (v.maximum + v.minimum) / 2) == previousBalance) break;

Variables declared as constant are expressions, not constants

Due to how constant variables are implemented (replacements at compile-time), an expression assigned to a constant variable is recomputed each time that the variable is used, which wastes some gas.

If the variable was immutable instead: the calculation would only be done once at deploy time (in the constructor), and then the result would be saved and read directly at runtime rather than being recalculated.

See: ethereum/solidity#9232:

Consequences: each usage of a β€œconstant” costs ~100 gas more on each access (it is still a little better than storing the result in storage, but not much). since these are not real constants, they can’t be referenced from a real constant environment (e.g. from assembly, or from another library)

contracts-full/AlchemicTokenV2.sol:
  21:        bytes32 public constant ADMIN_ROLE = keccak256("ADMIN");
  24:        bytes32 public constant SENTINEL_ROLE = keccak256("SENTINEL");
  27:        bytes32 public constant CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan");

contracts-full/TransmuterBuffer.sol:
  31:        bytes32 public constant ADMIN = keccak256("ADMIN");
  34:        bytes32 public constant KEEPER = keccak256("KEEPER");

contracts-full/TransmuterV2.sol:
  96:        address public constant ZERO_ADDRESS = address(0);
  99:        bytes32 public constant ADMIN = keccak256("ADMIN");
 102:        bytes32 public constant SENTINEL = keccak256("SENTINEL");

contracts-full/libraries/FixedPointMath.sol:
  10:        uint256 public constant ONE = 10**DECIMALS;

Change these expressions from constant to immutable and implement the calculation in the constructor. Alternatively, hardcode these values in the constants and add a comment to say how the value was calculated.

Storage variables should be declared immutable when possible

If a storage variable is assigned only in the constructor, it should be declared as immutable. This would help to reduce gas costs as calls to immutable variables are much cheaper than regular state variables, as seen from the Solidity Docs:

Compared to regular state variables, the gas costs of constant and immutable variables are much lower. Immutable variables are evaluated once at construction time and their value is copied to all the places in the code where they are accessed.

Consider declaring these variables as immutable:

contracts-full/TransmuterBuffer.sol:
  64:        address public debtToken;

contracts-full/TransmuterV2.sol:
 108:        address public syntheticToken;
 111:        address public override underlyingToken;
 136:        address public override whitelist;
 139:        uint256 public override conversionFactor;

contracts-full/EthAssetManager.sol:
 157:        IWETH9 public weth;

contracts-full/WETHGateway.sol:
  20:        address public whitelist;

contracts-full/TransmuterConduit.sol:
  13:        address public token;
  16:        address public sourceTransmuter;
  19:        address public sinkTransmuter;

contracts-full/StakingPools.sol:
  82:        IERC20Mintable public reward;

contracts-full/AlchemistV2.sol:
  57:        address public override debtToken;
  84:        address public override whitelist;
  87:        Limiters.LinearGrowthLimiter private _mintingLimiter;

Unnecessary definition of variables

Some variables are defined even though they are only used once in their respective functions. Not defining these variables can help to reduce gas cost and contract size.

Instances include:

contracts-full/ThreePoolAssetManager.sol:
 751:        uint256 reduction = totalCliffs - cliff;
 777:        uint256 missingDecimals = threePoolDecimals - tokenDecimals;
 792:        uint256 minimumMintAmount = expectedOutput * threePoolSlippage / SLIPPAGE_PRECISION;
 829:        uint256 normalizedAmount  = amount * 10**missingDecimals;
 831:        uint256 minimumMintAmount = expectedOutput * threePoolSlippage / SLIPPAGE_PRECISION;
 863:        uint256 index = uint256(asset);
 871:        uint256 normalizedAmount = amount * threePoolSlippage / SLIPPAGE_PRECISION;
 873:        uint256 minimumAmountOut = expectedOutput / 10**missingDecimals;
 916:        uint256 minimumMintAmount = expectedOutput * metaPoolSlippage / SLIPPAGE_PRECISION;
 941:        uint256 minimumMintAmount = amount * metaPoolSlippage / SLIPPAGE_PRECISION;
 963:        uint256 index = uint256(asset);
 969:        uint256 minimumAmountOut = expectedOutput * metaPoolSlippage / SLIPPAGE_PRECISION;

contracts-full/TransmuterBuffer.sol:
 386:        uint256 numYTokens = supportedYieldTokens.length;
 481:        uint256 actionAmt = (amount * weighting.weights[token]) / weighting.totalWeight;
 520:        uint256 minimumAmountOut = amountUnderlying - amountUnderlying * 100 / 10000;

contracts-full/TransmuterV2.sol:
 463:        bool modifyLiquidity = previouslyActive && currentlyActive && !migrate;
 474:        bool addLiquidity = (!previouslyActive && currentlyActive) || (currentlyActive && migrate);
 475:        bool subLiquidity = (previouslyActive && !currentlyActive) || (previouslyActive && migrate);

contracts-full/EthAssetManager.sol:
 549:        uint256 reduction = totalCliffs - cliff;
 584:        uint256 minimumMintAmount = expectedOutput * metaPoolSlippage / SLIPPAGE_PRECISION;
 613:        uint256 minimumMintAmount = amount * metaPoolSlippage / SLIPPAGE_PRECISION;
 647:        uint256 index = uint256(asset);
 653:        uint256 minimumAmountOut = expectedOutput * metaPoolSlippage / SLIPPAGE_PRECISION;

contracts-full/AlchemistV2.sol:
 897:        uint256 shares = _yieldTokens[yieldToken].totalShares - _accounts[msg.sender].balances[yieldToken];
1106:        address underlyingToken = yieldTokenParams.underlyingToken;
1261:        uint256 pendingCredit     = yieldTokenParams.pendingCredit;
1262:        uint256 distributedCredit = yieldTokenParams.distributedCredit;
1264:        uint256 lockedCredit      = pendingCredit - (distributedCredit + unlockedCredit);
1317:        address underlyingToken = yieldTokenParams.underlyingToken;
1374:        uint256 balance          = account.balances[yieldToken];
1375:        uint256 unrealizedCredit = (currentAccruedWeight - lastAccruedWeight) * balance / FIXED_POINT_SCALAR;
1463:        address underlyingToken        = _yieldTokens[yieldToken].underlyingToken;
1464:        uint256 shares                 = _accounts[owner].balances[yieldToken];
1539:        uint256 balance = _accounts[owner].balances[yieldToken];
1540:        uint256 unrealizedCredit = ((currentAccruedWeight - lastAccruedWeight) * balance) / FIXED_POINT_SCALAR;
1590:        uint256 creditUnlockRate      = yieldTokenParams.creditUnlockRate;
1592:        uint256 lastDistributionBlock = yieldTokenParams.lastDistributionBlock;

contracts-full/libraries/Tick.sol:
  65:        FixedPointMath.Number memory startingAccumulatedWeight = startingTick.accumulatedWeight;
  66:        FixedPointMath.Number memory endingAccumulatedWeight = endingTick.accumulatedWeight;

contracts-full/libraries/Limiters.sol:
  98:        uint256 delta = elapsed * self.rate / FIXED_POINT_SCALAR;
AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax Β© 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter