One Shot: Reloaded

First Flight #47
Beginner FriendlyNFT
100 EXP
Submission Details
Impact: low
Likelihood: high

A single user can only stake one Rapper NFT at a time.

Author Revealed upon completion

Root + Impact

DescriptionThe normal behavior of the streets module's stake function is to allow a user to stake a Rapper NFT. The specific issue is that the function uses move_to(staker, StakeInfo { ... }) to create a StakeInfo resource at the user's address. Since an account can only have one instance of a given resource, a user cannot stake a second Rapper NFT until the first one is unstaked.

// Root cause in the codebase with @> marks to highlight the relevant section
public entry fun stake(staker: &signer, rapper_token: Object<Token>) {
let staker_addr = signer::address_of(staker);
let token_id = object::object_address(&rapper_token);
move_to(staker, StakeInfo { @>
start_time_seconds: timestamp::now_seconds(),
owner: staker_addr,
});
// ...
}

Risk

Likelihood:

  • This will occur whenever a user attempts to stake a second Rapper NFT.

  • The Move VM will revert the transaction with a move_to error, as the resource already exists.

Impact:

  • This limitation is not a security vulnerability but severely restricts the protocol's usability.

  • It prevents users from staking multiple Rapper NFTs, which would be a logical next step for power users.

Proof of Concept

public entry fun stake(staker: &signer, rapper_token: Object<Token>) {
let staker_addr = signer::address_of(staker);
let token_id = object::object_address(&rapper_token);
move_to(staker, StakeInfo { @>
start_time_seconds: timestamp::now_seconds(),
owner: staker_addr,
});
// ...
}

Recommended Mitigation

- move_to(staker, StakeInfo {
- start_time_seconds: timestamp::now_seconds(),
- owner: staker_addr,
- });
+ // In the module's resource
+ struct Stakes has key {
+ info_by_token: table::Table<address, StakeInfo>,
+ }
+
+ // In the stake function
+ let stakes_ref = borrow_global_mut<Stakes>(staker_addr);
+ table::add(&mut stakes_ref.info_by_token, token_id, StakeInfo { ... });

Support

FAQs

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