Algo Ssstablecoinsss

AI First Flight #2
Beginner FriendlyDeFi
EXP
View results
Submission Details
Impact: medium
Likelihood: medium
Invalid

Duplicate Collateral Addresses in Constructor Allow Double-Counting of Collateral and Over-Minting of DSC

Root + Impact

Description

  • Normal Behavior:
    The dsc_engine.vy constructor stores the input collateral addresses into the collateral_tokens array. During account valuation, the engine iterates over this array and sums up each collateral balance to compute the total collateral value.


  • Specific Issue:
    The constructor does not check for duplicate collateral addresses. If the same address appears twice in the input array, the same balance is counted twice during valuation. A user can then mint more DSC than their actual collateral supports. While the current deployment script passes WBTC and WETH (two distinct addresses), a misconfigured or malicious deployment could introduce duplicates and break the protocol's solvency.

# dsc_engine.vy:66 — no duplicate check
self.collateral_tokens = collateral_tokens
​
# dsc_engine.vy:338 — iterates over all entries, including duplicates
for token in self.collateral_tokens:
total_value += self._get_collateral_value(token, balance)

Risk

Likelihood:

  • The current deployment uses two distinct addresses, so the bug is not triggered today.

  • However, the constructor does not validate input, so any future deployment or upgrade could introduce duplicates.

  • A compromised owner could deliberately deploy with duplicates.

Impact:

  • Duplicate addresses cause collateral to be double-counted.

  • Users can mint DSC exceeding their real collateral backing.

  • The protocol can become insolvent if the owner is compromised or the deployment script is misconfigured.

Proof of Concept

The test deploys the engine with weth.address passed twice in the token_addresses array. The user deposits 1 WETH worth $100,000. Because the engine iterates over the array and sums each entry, the same balance is counted twice, making the collateral appear worth $200,000. The user can then mint 200,000 DSC against only 100,000 worth of real collateral. The assertion value == 200_000 * 10**18 confirms the double-counting.

import boa
from eth_utils import to_wei
​
from src import decentralized_stable_coin, dsc_engine
from src.mocks import MockV3Aggregator, mock_token
​
​
def test_duplicate_collateral_double_counts():
dsc = decentralized_stable_coin.deploy()
weth = mock_token.deploy()
eth_usd = MockV3Aggregator.deploy(8, 100_000 * 10**8)
​
# Deploy engine with WETH listed twice
engine = dsc_engine.deploy(
[weth.address, weth.address], # duplicate!
[eth_usd.address, eth_usd.address],
dsc.address,
)
dsc.set_minter(engine.address, True)
dsc.transfer_ownership(engine.address)
​
user = boa.env.generate_address()
one_weth = to_wei(1, "ether")
​
# User deposits 1 WETH
with boa.env.prank(user):
weth.mint_amount(one_weth)
weth.approve(engine.address, one_weth)
engine.deposit_collateral(weth.address, one_weth)
​
# The engine values 1 WETH as if it were 2 WETH
value = engine.get_usd_value(weth.address, one_weth)
print("Value of 1 WETH with duplicate entry:", value)
assert value == 200_000 * 10**18 # Should be 100,000, but is 200,000
​
# User can mint 200,000 DSC against only 100,000 worth of collateral
with boa.env.prank(user):
engine.mint_dsc(to_wei(200_000, "ether"))
​
print("User minted 200,000 DSC against 100,000 collateral")

Recommended Mitigation

Since the constructor takes exactly two collateral addresses, a single assert is enough to reject duplicates. This prevents the same token from being counted twice during valuation and ensures the protocol's solvency.

@deploy
def __init__(
token_addresses: address[2],
price_feed_addresses: address[2],
dsc_address: address,
):
+ assert token_addresses[0] != token_addresses[1], "Duplicate collateral address"
DSC = i_decentralized_stable_coin(dsc_address)
COLLATERAL_TOKENS = token_addresses
self.token_address_to_price_feed[token_addresses[0]] = price_feed_addresses[0]
self.token_address_to_price_feed[token_addresses[1]] = price_feed_addresses[1]
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 1 hour ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!