Starknet Auction

First Flight #26
Beginner FriendlyNFT
100 EXP
View results
Submission Details
Severity: low
Valid

Incorrect event bid amount emitted in `StarknetAuction::bid` function creating confusion and impact downstream/front-end process that depends on this event for rendering information related to bid transaction

Summary

The event in StarknetAuction::bid function was found emitted before the highest_bid was updated with latest user bid amount. This causes the bid amount emitted by the event is still the previous bid amount and not the user bid amount, resulting incorrect information in the emitted event upon the successful of bid transaction.

https://github.com/SiewLin-AIDeaBuddy/first-flight-2024-10-starknet-auction/blob/04f8a8b7f16edc7aeba06a3a6195e0257ddbefd0/src/starknet_auction.cairo#L109

Vulnerability Details

In StarknetAuction::bid function, the event was found emitted before the highest_bid was updated with latest user bid amount.

fn bid(ref self: ContractState, amount: u64) {
let time = get_block_timestamp();
let erc20_dispatcher = IERC20Dispatcher { contract_address: self.erc20_token.read() };
let sender = get_caller_address();
let receiver = get_contract_address();
let current_bid = self.highest_bid.read();
assert(self.started.read(), 'Auction is not started');
assert(time < self.bidding_end.read(), 'Auction ended');
assert(amount > current_bid, 'The bid is not sufficient');
self.bid_values.entry(sender).write(amount);
<@@>! self.emit(NewHighestBid {amount: self.highest_bid.read(), sender: sender});
self.highest_bidder.write(sender);
self.highest_bid.write(amount);
erc20_dispatcher.transfer(receiver, amount.into());
}

At the end of a successful bid process, the event will emit the previous bid amount and not the latest user bid amount. This will create confusion and impact any downstream/front-end process that relies on the emitted event for proper rendering of the bid transaction information.

Proof of Concept:

Step 1: Update the visibility of the following event and struct in src\starknet_auction.cairo

#[event]
#[derive(Drop, starknet::Event)]
- enum Event {
+ pub enum Event {
Started: Started,
NewHighestBid: NewHighestBid,
Withdraw: Withdraw,
End: End,
}
#[derive(Drop, starknet::Event)]
- struct NewHighestBid {
- amount: u64,
- sender: ContractAddress,
+ pub struct NewHighestBid {
+ pub amount: u64,
+ pub sender: ContractAddress,
}

Step 2: In tests\test_contract.cairo, add the following import and test

// add these imports at the top
use snforge_std::{spy_events, EventSpyAssertionsTrait};
use starknet_auction::starknet_auction::StarknetAuction;
// tapout new test
#[test]
fn test_audit_bid_event_issue() {
let (auction_dispatcher, auction_contract, erc20_contract_address, _) = deploy_auction_contract();
let erc20_dispatcher = IMockERC20TokenDispatcher { contract_address: erc20_contract_address };
let bidder: ContractAddress = 123.try_into().unwrap();
let mut spy = spy_events();
// The owner calls start function.
auction_dispatcher.start(86400, 10);
// User (bidder) executes the bid process
start_cheat_caller_address_global(bidder);
erc20_dispatcher.mint(bidder, 15);
erc20_dispatcher.token_approve(auction_contract, 15);
// user calls the bid function with amount of 15.
auction_dispatcher.bid(15);
// check bidder 15 tokens has been transferred following the success of the bid process
let bidder_balance = erc20_dispatcher.token_balance_of(bidder);
assert(bidder_balance == 0, 'bidder balance should be zero');
// wrong event bid amount which is still the previous setup bid amount at 10
let wrong_event_bid_amount = StarknetAuction::Event::NewHighestBid(StarknetAuction::NewHighestBid {amount: 10, sender: bidder});
// wrong event is indeed emitted, indicating the event isn't emitted with the latest bid amount
spy.assert_emitted(@array![(auction_contract, wrong_event_bid_amount)]);
stop_cheat_caller_address_global();
}

Step 3: Run the test snforge test --features enable_for_tests test_audit_bid_event_issue

...
Collected 1 test(s) from starknet_auction package
Running 0 test(s) from src/
Running 1 test(s) from tests/
[PASS] starknet_auction_integrationtest::test_contract::test_audit_bid_event_issue (gas: ~1962)
Tests: 1 passed, 0 failed, 0 skipped, 0 ignored, 16 filtered out

The test passes indicating that the event is wrongly emitted with previous setup bid instead of the user latest bid amount

Impact

Misleading information that confuse user and impact downstream/front-end process that relies on the event for rendering bid transaction information

Tools Used

Manual review with test

Recommendations

Make amendment to StarknetAuction::bid function by shifting the event emit after the update of highest_bid

fn bid(ref self: ContractState, amount: u64) {
let time = get_block_timestamp();
let erc20_dispatcher = IERC20Dispatcher { contract_address: self.erc20_token.read() };
let sender = get_caller_address();
let receiver = get_contract_address();
let current_bid = self.highest_bid.read();
assert(self.started.read(), 'Auction is not started');
assert(time < self.bidding_end.read(), 'Auction ended');
assert(amount > current_bid, 'The bid is not sufficient');
self.bid_values.entry(sender).write(amount);
- self.emit(NewHighestBid {amount: self.highest_bid.read(), sender: sender});
self.highest_bidder.write(sender);
self.highest_bid.write(amount);
+ self.emit(NewHighestBid {amount: self.highest_bid.read(), sender: sender});
erc20_dispatcher.transfer(receiver, amount.into());
}
Updates

Lead Judging Commences

bube Lead Judge 8 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Incorrectly emitted parameter in `NewHighestBid` event

The `bid` function emits `NewHighestBid` event with wrong parameter. The `amount` parameter is `self.highest_bid.read()` that is called before the update of the `highest_bid` variable.

Support

FAQs

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