rustnearprotocolnear

Borrowing confusion in rust on the NEAR protocol


I am developing a smart contract on the NEAR platform which is a lottery. the structure I have is a main contract the holds a Vec of lotteries.

#[near_bindgen]
#[derive(BorshDeserialize, BorshSerialize)]
pub struct LottoList{
    owner_id: AccountId,
    lotteries: Vec<NearLotto>,
}

Each lottery is represented by the following struct.

#[derive(BorshDeserialize, BorshSerialize)]
pub struct NearLotto {
    lotto_id: u32,
    owner_id: AccountId,
    entries: Vector<AccountId>, //UnorderedMap<u64, AccountId>,
    entry_fee: Balance,
    prize_pool: Balance, 
    climate_pool: Balance,
    winner: AccountId, 
    closed: bool, 
    rand: u128
}

The init creates an empty array and after that we can start adding new lottories.

#[init]
    pub fn new(owner_id: AccountId) -> Self {
        assert!(env::is_valid_account_id(owner_id.as_bytes()), "Invalid owner account");
        assert!(!env::state_exists(), "Already initialized");
        
        Self {
            owner_id,
            lotteries: Vec::new()
        } 
    }

    pub fn add_lotto(&mut self, owner_id: AccountId, entry_fee:u128, start_pool:u128){
        assert!(self.owner_id == env::signer_account_id(), "Only account owner cna make more lottos");
        let lotto = NearLotto {
            lotto_id: self.lotteries.len() as u32,
            owner_id,
            entries: Vector::new(b'e'),  //UnorderedMap::new(b"entries".to_vec()),
            entry_fee: entry_fee, 
            prize_pool: start_pool,
            climate_pool:0,
            winner: "".to_string(),
            closed: false, 
            rand: 78
        };
        self.lotteries.push(lotto);
    }

The issue I am having is with the function to enter the draw. Which is as follows.

#[payable]
    pub fn enter_draw(&mut self, lotto_id:u32){
        // charge some storage fee 
        let mut lotto = &self.lotteries[lotto_id as usize];
        let attached = env::attached_deposit();
        assert!(attached >= lotto.entry_fee, "Entry fee not enough");
        assert!(lotto.entries.len() < MAX_ENTRIES, "Entries are full");
        env::log(format!("money matches, add entry").as_bytes());
        lotto.prize_pool = lotto.prize_pool + (env::attached_deposit()/4)*3; // 75% of entries fees goes into prize pool 
        lotto.climate_pool = lotto.climate_pool + (env::attached_deposit()/4)*3; //25% of entries go to climate change
        lotto.entries.push(&env::signer_account_id()); //self.entries.insert(&k, &near_sdk::env::signer_account_id());
        env::log(format!("{} Entering the lottery", env::signer_account_id()).as_bytes());
        self.lotteries[lotto_id as usize] = lotto;
    }

The final line gives me the error.

mismatched types

expected struct `NearLotto`, found `&NearLotto` 

I don't have the grounding in rust yet to know the solution to issue. Any ideas gratefully accepted.


Solution

  • There are several issues with your current contract design.

    The first is that you are using in memory data structures. This means when your contract starts it must load the entire state into memory. Want to do draw for a single lotto, but are paying to load all of them. This won't scale. You should use persistent collections see https://www.near-sdk.io/contract-structure/collections.

    As for your type issue.

    let mut lotto = &self.lotteries[lotto_id as usize];
    

    This line creates a mutable references, &NearLotto.

    self.lotteries[lotto_id as usize] = lotto;
    

    Then you try to set a reference when it expects NearLotto.

    Since you are updating a mutable reference to a location in the vector you don't need to write it back since you mutate it in place. Thus this line isn't needed.