Anchor contest - hickuphh3's results

The Benchmark DeFi Yield.

General Information

Platform: Code4rena

Start Date: 24/02/2022

Pot Size: $170,000 UST

Total HM: 15

Participants: 16

Period: 14 days

Judge: Albert Chon

Total Solo HM: 11

Id: 82

League: COSMOS

Anchor

Findings Distribution

Researcher Performance

Rank: 9/16

Findings: 2

Award: $4,319.20

🌟 Selected for report: 1

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: hickuphh3

Also found by: 0xliumin, 0xwags, BondiPestControl, IllIllI, WatchPug, broccoli, cccz, cmichel, defsec, gzeon, hubble, robee

Labels

bug
QA (Quality Assurance)

Awards

3649.7358 USDC - $3,649.74

External Links

Codebase Impressions & Summary

This audit contest is probably the largest in codebase size to date, consisting of various components such as tokens, airdrop, governance, staking, liquidity mining, reward distributions, a money market and cross-chain bridge functionality. While these components aren’t complex when reviewed in isolation, the code complexity becomes a little higher because of the various interactions with each other.

Overall, the codebase quality is very high. Inline comments provided are helpful in describing why some checks were not performed (mainly because they are done elsewhere).

The level of documentation is also high, where the documentation portal and READMEs are adequate and kept up-to-date with the codebase.

Tests ran without issues, and could be easily modified to test out POCs and potential vulnerabilities.

The findings made involve the lack of sanity checks for some key config values (eg. their values should not exceed 100%). While the likelihood of the creation of governance polls to modify these values is low because the proposer loses his ANC deposit if quorum isn’t met, the impact of such polls, should they be successful, is high.

Low Severity Findings

L01: CrossAnchorBridge: Decide if whitelisting feature is to be kept or removed

Line References

https://github.com/code-423n4/2022-02-anchor/blob/main/contracts/cross-chain-contracts/ethereum/CrossAnchorBridge.sol#L125-L130

https://github.com/code-423n4/2022-02-anchor/blob/main/contracts/cross-chain-contracts/ethereum/CrossAnchorBridge.sol#L147-L151

https://github.com/code-423n4/2022-02-anchor/blob/main/contracts/cross-chain-contracts/ethereum/CrossAnchorBridge.sol#L172-L173

https://github.com/code-423n4/2022-02-anchor/blob/main/contracts/cross-chain-contracts/ethereum/CrossAnchorBridge.sol#L251

https://github.com/code-423n4/2022-02-anchor/blob/main/contracts/cross-chain-contracts/ethereum/CrossAnchorBridge.sol#L256

Description

The whitelisted mappings are defined and set to true for certain token addresses, but the lines of code where they are used are commented out.

Decide whether to keep the whitelisting requirement. Either comment out the remaining lines / remove them entirely, or uncomment the lines where they are used.

L02: Incorrect opcode specification documentation

Line References

https://github.com/code-423n4/2022-02-anchor/blob/main/contracts/cross-chain-contracts/README.md#op-code-specification

https://github.com/code-423n4/2022-02-anchor/blob/main/contracts/cross-chain-contracts/terra/contracts/wormhole-bridge/src/contract.rs#L99-L125

Description

It is unclear if the full opcode values include the flags as well. If so, they should be 8 bits in length, which isn’t the case. Their decimal representations are also incorrect.

| Opcode | Flags | Full Opcode | Decimal |
|:-------|:------|:------------|:-------|
| Deposit Stable | `FLAG_BOTH_TRANSFERS` | `0b110000` | `192` |
| Redeem Stable | `FLAG_BOTH_TRANSFERS` | `0b110001` | `193` |
| Repay Stable | `FLAG_INCOMING_TRANSFER` | `0b1000000` | `64` |
| Lock Collateral | `FLAG_INCOMING_TRANSFER` | `0b1000001` | `65` |
| Unlock Collateral | `FLAG_OUTGOING_TRANSFER` | `0b0100000` | `32` |
| Borrow Stable | `FlAG_OUTGOING_TRANSFER` | `0b0100001` | `33` |
| Claim Rewards | `FLAG_OUTGOING_TRANSFER` | `0b0100010` | `34` |

The opcodes were also not updated in the comments of the wormhole bridge terra contract.

Update the comments to reflect the latest changes.

| Opcode | Flags | Full Opcode | Decimal |
|:-------|:------|:------------|:-------|
| Deposit Stable | `FLAG_BOTH_TRANSFERS` | `0b11000000` | `192` |
| Redeem Stable | `FLAG_BOTH_TRANSFERS` | `0b11000001` | `193` |
| Repay Stable | `FLAG_INCOMING_TRANSFER` | `0b10000000` | `128` |
| Lock Collateral | `FLAG_INCOMING_TRANSFER` | `0b10000001` | `129` |
| Unlock Collateral | `FLAG_OUTGOING_TRANSFER` | `0b01000000` | `64` |
| Borrow Stable | `FlAG_OUTGOING_TRANSFER` | `0b01000001` | `65` |
| Claim Rewards | `FLAG_OUTGOING_TRANSFER` | `0b01000010` | `66` |
/*
struct Instruction {
  uint8 op_code; // [1 byte]
  bytes32 sender_address; // [32 bytes]
  one_of {
    // [deposit_stable] opcode = 192
    uint64 sequence; // [8 bytes]

    // [repay_stable] opcode = 128
    uint64 sequence; // [8 bytes]

    // [unlock_collateral] opcode = 64
    bytes32 collorateral_token_address; // [32 bytes]
    uint128 amount; // [16 bytes]

    // [borrow_stable] opcode = 65
    uint256 amount; // [16 bytes]

    // [claim rewards] opcode = 66
    // N/A for now

    // [redeem_stable] opcode = 193
    uint64 sequence; // [8 bytes]

    // [lock_collateral] opcode = 129
    uint64 sequence; // [8 bytes]
  }
}
*/

L03: Slightly imprecise tax calculation

Line References

https://github.com/code-423n4/2022-02-anchor/blob/main/contracts/anchor-bAsset-contracts/packages/basset/src/tax_querier.rs#L12-L15

https://docs.anchorprotocol.com/developers-ethereum/fees#terra-blockchain-tax

Description

The tax amount is calculated as amount * DENOM / DENOM + tax_rate instead of amount * tax_rate / DENOM, which means the tax percentage isn’t as precise (less tax is actually charged).

We see this in the test case:

// normal tax
assert_eq!(
  deduct_tax(&deps.as_mut().querier, Coin::new(50000000u128, "uusd")).unwrap(),
  Coin {
    denom: "uusd".to_string(),
    amount: Uint128::new(49504950u128)
  }
);

Given an amount of 50_000_000, assuming a tax rate of 1%,the amount after tax would be 0.99 * 50_000_000 = 49500000, but the calculated amount is 50_000_000 * 100 / 101 ~= 49504950.

Update the calculation to be

(coin.amount.checked_sub(coin.amount.multiply_ratio(
    Uint128::new(1) * tax_rate,
    DECIMAL_FRACTION,
)))?,

L04: Duplicate vesting schedules can be added

Line References

https://github.com/code-423n4/2022-02-anchor/blob/main/contracts/anchor-token-contracts/contracts/staking/src/contract.rs

Description

In the LP staking contract, it is possible to instantiate / add duplicate vesting schedules.

Proof of Concept

#[test]
fn test_instantiate_duplicate_schedules() {
  let mut deps = mock_dependencies(&[]);

  let msg = InstantiateMsg {
    anchor_token: "reward0000".to_string(),
    staking_token: "staking0000".to_string(),
    distribution_schedule: vec![
      (
        mock_env().block.time.seconds() + 100,
	mock_env().block.time.seconds() + 200,
	Uint128::from(1000000u128),
      ),
      (
        mock_env().block.time.seconds() + 100,
	mock_env().block.time.seconds() + 200,
	Uint128::from(1000000u128),
      ),
    ],
  };

  let info = mock_info("addr0000", &[]);
  let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
}

Recommendation

De-duplicate the new vesting schedules when it is added. It is easier done if the schedules are sorted prior.

Non-Critical Findings

NC01: money-market-contracts: Market max_borrow_factor is not capped

Line References

https://github.com/code-423n4/2022-02-anchor/blob/main/contracts/money-market-contracts/contracts/market/src/contract.rs#L66

https://github.com/code-423n4/2022-02-anchor/blob/main/contracts/money-market-contracts/contracts/market/src/contract.rs#L321-L323

Description

There is no check to ensure that max_borrow_factor is less than 100% (Decimal::One()). It is therefore possible to set a borrow factor of > 1. However, the consequence of it is negligible because it would be the same as setting the value to 100% (can’t borrow if there is no liquidity left).

Proof of Concept

Change the instantiation and update config test values to a value greater than 100%, such as Decimal256::MAX.

https://github.com/code-423n4/2022-02-anchor/blob/main/contracts/money-market-contracts/contracts/market/src/testing/tests.rs#L36

let msg = InstantiateMsg {
  owner_addr: "owner".to_string(),
  stable_denom: "uusd".to_string(),
  aterra_code_id: 123u64,
  anc_emission_rate: Decimal256::one(),
  max_borrow_factor: Decimal256::MAX,
};

https://github.com/code-423n4/2022-02-anchor/blob/main/contracts/money-market-contracts/contracts/market/src/testing/tests.rs#L215

let msg = ExecuteMsg::UpdateConfig {
  owner_addr: None,
  interest_model: Some("interest2".to_string()),
  distribution_model: Some("distribution2".to_string()),
  max_borrow_factor: Some(Decimal256::percent(120)),
};

Ensure max_borrow_factor doesn’t exceed Decimal256::one().

// add error in market/src/error.rs
pub enum ContractError {
  #[error("Setting greater than theoretical borrow factor")]
  MaxTheoreticalBorrowFactorExceeded {},
  ...
}

// in market/src/contract.rs
// before store_config() in L54
if msg.max_borrow_factor > Decimal256::one() {
  return Err(ContractError::MaxTheoreticalBorrowFactorExceeded {});
}

// in update_config()
if let Some(max_borrow_factor) = max_borrow_factor {
  if max_borrow_factor > Decimal256::one() {
    return Err(ContractError::MaxTheoreticalBorrowFactorExceeded {});
  }
  config.max_borrow_factor = max_borrow_factor;
}

NC02: Incorrect description of Safe Ratio in documentation

Reference

https://docs.anchorprotocol.com/protocol/anchor-governance/modify-liquidation-parameters

Description

The docs say that

“A low Safe Ratio value allows for the fast liquidation of collaterals while incurring a high price impact for the collateral, while a low Safe Ratio value enforces liquidations with lower collateral price impact, albeit with slower collateral liquidation.”

The second statement describes a high safe ratio.

Change to “while a high Safe Ratio value enforces liquidations with lower collateral price impact, albeit with slower collateral liquidation.”

NC03: Typos / Grammar

Description

Do a CMD / CTRL + F for the following statements.

  1. form → from
    • // load price form the oracle → // load price from the oracle
    • // load balance form the token contract → // load price from the token contract
  2. WITHDARW → WITHDRAW
    • ALICE CAN ONLY WITHDARW 40 UST → ALICE CAN ONLY WITHDRAW 40 UST
  3. // cannot liquidation collaterals → // cannot liquidate collaterals
  4. // if the token contract is already register we cannot change the address → // if the token contract is already registered we cannot change the address
    • Note the double spacing between is and already
  5. inistantiation → instantiation
    • // cannot register the token at the inistantiation → // cannot register token at instantiation

NC04: Outstanding TODO

Line Reference

https://github.com/code-423n4/2022-02-anchor/blob/main/contracts/money-market-contracts/contracts/overseer/src/contract.rs#L395

Description

There’s 1 remaining TODO that may not have been resolved.

// TODO: Should this become a reply? If so which SubMsg to make reply_on?

Suggestions

S01: Change interest rate model to jump rate model

Reference

https://github.com/code-423n4/2022-02-anchor/blob/main/contracts/money-market-contracts/contracts/interest_model/src/contract.rs#L114-L135

https://github.com/compound-finance/compound-protocol/blob/master/contracts/JumpRateModel.sol

https://docs.benqi.fi/benqi-liquidity-market/protocol-parameters

Description

The current interest rate model used is a simple model that scales linearly with market utilization. A better interest rate model that Compound and its forks are using is the jump rate model, because it is more efficient at incentivizing liquidity at higher utilization rates.

Recommendation

Change the interest rate model to the jump rate model, where a kink is introduced to increase the interest multiplier after a specified market utilization rate.

#0 - GalloDaSballo

2022-08-04T23:39:33Z

L01: CrossAnchorBridge: Decide if whitelisting feature is to be kept or removed -> Dup of #44

#1 - GalloDaSballo

2022-08-04T23:44:27Z

This looks like the most complete report

Findings Information

🌟 Selected for report: csanuragjain

Also found by: 0v3rf10w, IllIllI, WatchPug, defsec, gzeon, hickuphh3, robee

Labels

bug
G (Gas Optimization)

Awards

669.4645 USDC - $669.46

External Links

G01: Define constants for indexes in parseIncomingTokenTransferInfo()

Line References

https://github.com/code-423n4/2022-02-anchor/blob/main/contracts/cross-chain-contracts/ethereum/CrossAnchorBridge.sol#L269-L296

Tools Used

Remix for prototyping + gas cost estimates

Description

Instead of defining and incrementing the index pointer when parsing the token transfer info, it would be ~300 gas cheaper to define and utilize private constants. It also allows the encoded length check to be shifted to the front of the function, thus costing less gas should the check fail.


// start from index 0
uint256 private constant CHAIN_ID_INDEX = 0;
// chainId takes first 2 bytes
uint256 private constant TOKEN_RECIPIENT_INDEX = 2;
// tokenRecipient takes next 32 bytes
uint256 private constant TOKEN_TRANSFER_INDEX = 34;
// tokenTransferSequence takes next 8 bytes
uint256 private constant INSTRUCTION_SEQUENCE_INDEX = 42;
// instructionSequence takes final 8 bytes
uint256 private constant ENCODED_LENGTH = 50;

function parseIncomingTokenTransferInfo(bytes memory encoded)
  public
  pure
  returns (IncomingTokenTransferInfo memory incomingTokenTransferInfo)
{
  require(
    encoded.length == ENCODED_LENGTH,
    "invalid IncomingTokenTransferInfo encoded length"
  );
	
  incomingTokenTransferInfo.chainId = encoded.toUint16(CHAIN_ID_INDEX);

  incomingTokenTransferInfo.tokenRecipientAddress = encoded.toBytes32(
    TOKEN_RECIPIENT_INDEX
  );

  incomingTokenTransferInfo.tokenTransferSequence = encoded.toUint64(
    TOKEN_TRANSFER_INDEX
  );

  incomingTokenTransferInfo.instructionSequence = encoded.toUint64(
    INSTRUCTION_SEQUENCE_INDEX
  );
}

G02: money-market::distribution_model: Simpler half_dec definition in query_anc_emission_rate()

Line References

https://github.com/code-423n4/2022-02-anchor/blob/main/contracts/money-market-contracts/contracts/distribution_model/src/contract.rs#L143

Description

It is perhaps cheaper to define half_dec as Decimal256::percent(200) instead of let half_dec = Decimal256::one() + Decimal256::one();

let half_dec = Decimal256::percent(200);

#0 - GalloDaSballo

2022-08-04T23:48:08Z

Around 300 + minor gas savings, neat that covers both Sol and Rust

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