Back to Blog

NearOinDao için Güvenlik Denetim Raporu

Code Auditing
December 10, 2021
31 min read

Rapor Manifestosu

Öğe Açıklama
Müşteri Oinfinance
Hedef NearOinDao

Sürüm Geçmişi

Sürüm Tarih Açıklama
1.0 04 Ara, 2021 İlk Yayın

1. Giriş

1.1 Hedef Sözleşmeler Hakkında

Hedef sözleşmeler bir kararlı para modülü içermektedir. Bunun yanı sıra, Staking ve Farming dahil diğer modülleri de uygulamaktadır. Bu modüller, kararlı para birimi olan USDO'nun stabilizasyonu için pozitif bir geri besleme döngüsü oluşturur.

Bilgi Açıklama
Tür Akıllı Sözleşme
Dil Rust
Yaklaşım Yarı-otomatik ve manuel doğrulama

Denetlenen depolar NearOinDao'yu içermektedir ^1

Denetim süreci yinelemeli bir yapıdadır. Özellikle, tespit edilen sorunları gideren commit'leri de denetlemeye devam edeceğiz. Yeni sorunlar ortaya çıkarsa bu süreci sürdüreceğiz. Bu nedenle bu raporda birden fazla commit SHA değerine atıfta bulunulmaktadır. Denetim öncesi ve sonrasındaki commit SHA değerleri aşağıda gösterilmektedir.

Denetim öncesi ve denetim sırasında

Sonrasında

Proje Commit SHA
NearOinDao 3bd117606c753d3c2f66b6dcddd1ae18ea47a20a

1.2 Güvenlik Modeli

Riski değerlendirmek için, OWASP Risk Derecelendirme Metodolojisi ^2 ve Ortak Zayıflık Sayımı ^3 dahil olmak üzere hem sektör hem de akademi tarafından yaygın olarak benimsenen standart veya önerileri takip ediyoruz. Buna göre, bu raporda ölçülen önem dereceleri dört kategoriye ayrılmaktadır: Yüksek, Orta, Düşük ve Belirsiz.

2. Bulgular

Toplamda, akıllı sözleşmede 22 potansiyel sorun tespit ettik. Ayrıca 12 önerimiz bulunmaktadır:

  • Yüksek Risk: 19

  • Orta Risk: 2

  • Düşük Risk: 1

  • Öneriler: 12

Ayrıntılar aşağıdaki bölümlerde sunulmaktadır.

ID Önem Derecesi Açıklama Kategori Durum
1 Yüksek self.liquidation_line değiştirilirken mantık hatası Yazılım Güvenliği Onaylandı ve düzeltildi
2 Yüksek liquidation fonksiyonu çalışmayabilir Yazılım Güvenliği Onaylandı ve düzeltildi
3 Yüksek Sözleşme açılırken zaman damgası ayarlanırken mantık hatası Yazılım Güvenliği Onaylandı ve düzeltildi
4 Yüksek Çapraz sözleşme işlemi başarısız olursa sözleşme durumu geri alınmıyor Yazılım Güvenliği Onaylandı ve düzeltildi
5 Yüksek Herkes ödül bakiyesi ekleyebilir DeFi Güvenliği Onaylandı ve düzeltildi
6 Yüksek Herkes kararlı havuz ödül bakiyesi ekleyebilir DeFi Güvenliği Onaylandı ve düzeltildi
7 Yüksek Herkes diğer kullanıcıların paralarını yakabilir DeFi Güvenliği Onaylandı ve düzeltildi
8 Yüksek Herkes kendi hesabına bakiye ekleyebilir DeFi Güvenliği Onaylandı ve düzeltildi
9 Yüksek Oracle zaman aralığını kontrol etmiyor DeFi Güvenliği Onaylandı ve düzeltildi
10 Yüksek Oracle zaman aralığı çok uzun DeFi Güvenliği Onaylandı ve düzeltildi
11 Yüksek Oin fiyatı için oracle yok DeFi Güvenliği Onaylandı ve düzeltildi
12 Yüksek Kullanıcılar ekstra ödül kazanabilir DeFi Güvenliği Onaylandı ve düzeltildi
13 Yüksek Kullanıcılar daha az kararlı ücret ödeyebilir DeFi Güvenliği Onaylandı ve düzeltildi
14 Orta Çoklu imzalı istek nispeten düşük onay oranıyla onaylanabilir DeFi Güvenliği Onaylandı ve düzeltildi
15 Orta Yıl başına blok sayısı hatalı DeFi Güvenliği Onaylandı ve düzeltildi
16 Yüksek Basılabilir para miktarı hatalı DeFi Güvenliği Onaylandı ve düzeltildi
17 Yüksek Kararlı ücret ödemesi kullanıcının yatırdığı token'ların kaybına yol açabilir DeFi Güvenliği Onaylandı ve düzeltildi
18 Yüksek Yanlış staking oranı DeFi Güvenliği Onaylandı ve düzeltildi
19 Düşük Ödül paraları sınırı aşabilir DeFi Güvenliği Onaylandı ve düzeltildi
20 Yüksek Farklı ayrıcalıklardaki kullanıcılar için aynı beyaz liste DeFi Güvenliği Onaylandı ve düzeltildi
21 Yüksek Kararlı ücret adresi üzerinde kontrol yok DeFi Güvenliği Onaylandı ve düzeltildi
22 Yüksek Ödül parasının total_reward değeri çoklu imza yöneticileri tarafından değiştirilebilir DeFi Güvenliği Onaylandı ve düzeltildi
23 - Gereksiz assertion Öneri Onaylandı ve düzeltildi
24 - Tasfiye sınırının tekrarlanan değerlendirmesi Öneri Onaylandı ve düzeltildi
25 - Gereksiz beyaz liste kontrolü Öneri Onaylandı ve düzeltildi
26 - Kullanılmayan fonksiyon Öneri Onaylandı ve düzeltildi
27 - Gereksiz Kod Öneri Onaylandı ve düzeltildi
28 - Fonksiyon adı ile uygulama çelişiyor Öneri Onaylandı ve düzeltildi
29 - Gereksiz Kod Öneri Onaylandı ve düzeltildi
30 - Hesaplama hassasiyeti artırılabilir Öneri Onaylandı ve düzeltildi
31 - Sistem daha önce işaretlenmiş fiyatı kaydetmeyebilir Öneri Onaylandı ve düzeltildi
32 - Tasfiyede teminat token'ının süreksiz dağılımı Öneri Onaylandı ve düzeltildi
33 - Hesaplama hassasiyetinin optimize edilmesi gerekli değil Öneri Onaylandı ve düzeltildi
34 - Merkezi tasarımın riski Öneri Kabul edildi

2.1 Yazılım Güvenliği

2.1.1 Potansiyel Sorun 1: Aynı kullanım için iki farklı öznitelik

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-1'de veya öncesinde ortaya çıkmıştır. İki öznitelik (yani self.cost ve self.liquidation_line) aynı sözleşme durumunu, yani kullanıcının tasfiye sınırını temsil etmektedir. Bunlar, sözleşmenin farklı fonksiyonlarında kullanılmaktadır (Liste 2.1 ve Liste 2.2). Ancak self.liquidation_line, set_liquidation_line fonksiyonu ile değiştirilebilirken self.cost değiştirilemez. Bu durumda, self.liquidation_line değiştirilirse, self.cost orijinal değerini korur. Bu durum, assert_user_ratio fonksiyonunun mantığını etkileyebilir (Liste 2.1).

pub(crate) fn assert_user_ratio(&self) {
        let user_ratio = self.internal_user_ratio(env::predecessor_account_id());
        if user_ratio != 0 {
            assert!(user_ratio >= self.cost, "User ratio less than standard.");
        }
    }

Liste 2.1: assert_user_ratio:lib.rs

// TODO liquidation
    #[payable]
    pub fn liquidation(&mut self, account: AccountId) {
        assert!(self.is_liquidation_paused(), "{}", SYSTEM_PAUSE);
        let ratio = self.internal_user_ratio(account.clone());
        assert!(ratio > 0, "No current pledge");
        assert!(ratio <= self.liquidation_line, "Not at the clearing line");
        ...

Liste 2.2: internal_can_mint_amount:lib.rs

Etki Kullanıcıların tasfiye sınırı, sözleşmenin farklı fonksiyonlarında tutarsız olup tüm sözleşmenin mantığını etkilemektedir.

Öneri I Bu iki özniteliğin kullanımını, kullanıcının staking oranını hesaplarken ve sistemin tasfiye sınırıyla karşılaştırırken birleştirebiliriz.

2.1.2 Potansiyel Sorun 2: Tasfiye ödülünün geçersiz dağılımı

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-4'te veya öncesinde ortaya çıkmıştır. Tasfiye göndericisinin hesabı ve sözleşme sahibinin hesabı kayıtlı olmayabilir (Liste 2.3'ün 193. ve 206. satırları). Bu durumda, gönderici tasfiye işlemi gerçekleştirmeye çalıştığında, hesapların kayıtlı olmadığına dair istisna fırlatılacağından işlem başarıyla yürütülemez.

pub(crate) fn personal_liquidation_token(&mut self, send_id: AccountId, account_id: AccountId, liquidation_gas: Balance, surplus_token: Balance, liquidation_fee: Balance) {
        //self.owner_id
        let coin_id = ST_NEAR.to_string();
        let mut sys_reward_coin = self.internal_get_reward_coin(coin_id.clone());
        
        let account_reward_key_o = self.get_staker_reward_key(send_id.clone(), coin_id.clone());
        let user_reward_coin_o = self.internal_get_account_reward(send_id.clone(), coin_id.clone());
        
        self.account_reward.insert(
            &account_reward_key_o,
            &UserReward {
                index:  user_reward_coin_o.index,
                reward: user_reward_coin_o.reward.checked_add(liquidation_gas).expect(ERR_ADD),
            },
        );
        
        let account_reward_key_t = self.get_staker_reward_key(account_id.clone(), coin_id.clone());
        let user_reward_coin_t = self.internal_get_account_reward(account_id.clone(), coin_id.clone());

        if surplus_token > 0 {
            self.account_reward.insert(
                &account_reward_key_t,
                &UserReward {
                    index:  user_reward_coin_t.index,
                    reward: user_reward_coin_t.reward.checked_add(surplus_token).expect(ERR_ADD),
                },
            );
        }

        let account_reward_key_s = self.get_staker_reward_key(self.owner_id.clone(), coin_id.clone());
        let user_reward_coin_s = self.internal_get_account_reward(self.owner_id.clone(), coin_id.clone());

        self.account_reward.insert(
            &account_reward_key_s,
            &UserReward {
                index:  user_reward_coin_s.index,
                reward: user_reward_coin_s.reward.checked_add(liquidation_fee).expect(ERR_ADD),
            },
        );
       
        sys_reward_coin.total_reward = sys_reward_coin
            .total_reward
            .checked_add(liquidation_gas).expect(ERR_ADD)
            .checked_add(liquidation_fee).expect(ERR_ADD)
            .checked_add(surplus_token).expect(ERR_ADD);

        self.reward_coins.insert(&coin_id, &sys_reward_coin);
    }

}

Liste 2.3: personal_liquidation_token:reward.rs

Etki Hesapların kayıtlı olmadığına dair istisna fırlatıldığından liquidation fonksiyonu başarıyla yürütülememektedir.

Öneri I Tasfiye göndericisinin hesabının ve sözleşme sahibinin hesabının varlığını, liquidation fonksiyonunun başında doğrulayın.

2.1.3 Potansiyel Sorun 3: Sistem açılırken Block_timestamp, closed_time'a kaydediliyor

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-3'te veya öncesinde ortaya çıkmıştır. internal_open fonksiyonu çağrılırken env::block_time_stamp() değeri self.closed_time'a kaydedilmemelidir.

#[private]
    pub fn internal_open(&mut self) {
        self.closed_time = env::block_timestamp();
        self.open_stake();
        self.open_redeem();
        self.open_claim_reward();
        self.open_liquidation();
        self.open_stable();
        log!(
            "{} open sys in {}",
            env::predecessor_account_id(),
            self.closed_time
        );
    }

Liste 2.4: internal_open:esm.rs

Etki Sözleşmenin açılış ve kapanış zamanı tamamen yanlıştır. Zaman bilgisine bağlı sonraki güncellemeler mantık hatası içerebilir.

Öneri I self.opening_time adında yeni bir sözleşme durumu oluşturulmasını ve sözleşme açılırken env::block_timestamp() değerinin bu değere atanmasını öneririz.

2.1.4 Potansiyel Sorun 4: Çapraz fonksiyon çağrıları başarısız olduğunda sözleşme durumu geri alınmıyor

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-3'te veya öncesinde ortaya çıkmıştır. Çapraz sözleşme fonksiyon çağrıları sırasında storage_deposit ve ft_transfer işlemleri başarısız olabilir. Transferin her zaman doğru şekilde gerçekleştirileceğini garanti edemeyiz. Çağrı başarısız olduğunda geri çağırma fonksiyonu sözleşme durumunu geri almamaktadır.

#[private]
    pub fn storage_deposit_callback(&mut self) {
        match env::promise_result(0) {
            PromiseResult::NotReady => unreachable!(),
            PromiseResult::Successful(_) => {
                log!("Transfer success");
            }
            PromiseResult::Failed => {
                log!("Transfer failed");
            }
        }
    }

Liste 2.5: storage_deposit_callback:ft.rs

#[private]
    pub fn liquidation_transfer_callback(&mut self) {
        match env::promise_result(0) {
            PromiseResult::NotReady => unreachable!(),
            PromiseResult::Successful(_) => {
                log!("Transfer success");
            }
            PromiseResult::Failed => {
                log!("Transfer failed");
            }
        }
    }

Liste 2.6: liquidation_transfer_callback:ft.rs

Etki Geri çağırma fonksiyonu sözleşme durumunu geri almadığından, işlemler başarısız olduğunda kullanıcılar varlıklarını kaybedebilir.

Öneri I Çapraz sözleşme fonksiyon çağrılarının geri çağırma fonksiyonunda (transfer başarısız olduğunda) sözleşme durumunu geri almamız gerekmektedir.

2.2 DeFi Güvenliği

2.2.1 Potansiyel Sorun 5: inject_reward erişim kontrolünden yoksun

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-1'de veya öncesinde ortaya çıkmıştır. inject_reward fonksiyonu herkese açıktır. Herkes bu fonksiyonu çağırarak sözleşmedeki ödül bakiyesini artırabilir.

pub fn inject_reward(&mut self, amount: U128, reward_coin: AccountId) {
        // self.assert_owner();

        if reward_coin == String::from("NEAR") {
            assert!(
                amount.0 == env::attached_deposit(),
                "Amount not equal transfer_amount"
            );
        }
        ...
    }

Liste 2.7: inject_reward:pool.rs

Etki Herkes sözleşmenin ödülü üzerinde keyfi bakiye ekleyebilir.

Öneri I Bu fonksiyon, aktarılan ödülü aldıktan sonra dahili olarak çağrıldığından özel bir fonksiyon olarak değiştirilmelidir.

2.2.2 Potansiyel Sorun 6: inject_sp_reward erişim kontrolünden yoksun

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-1'de veya öncesinde ortaya çıkmıştır. inject_sp_reward fonksiyonu herkese açıktır. Herkes bu fonksiyonu çağırarak sözleşmedeki kararlı havuz ödül bakiyesini artırabilir.

pub fn inject_sp_reward(&mut self, _amount: U128, sender_id: ValidAccountId) {
        self.reward_sp = self.reward_sp + u128::from(_amount);

        log!(
            "{} add sp_reward  {} cur amount{}",
            sender_id,
            u128::from(_amount),
            self.reward_sp
        );
    }

Liste 2.8: inject_sp_reward:stablepool.rs

Etki Herkes sözleşmenin kararlı havuz ödülü üzerinde keyfi bakiye ekleyebilir.

Öneri I Bu fonksiyon, aktarılan kararlı havuz ödülünü aldıktan sonra dahili olarak çağrıldığından özel bir fonksiyon olarak değiştirilmelidir.

2.2.3 Potansiyel Sorun 7: burn_coin erişim kontrolünden yoksun

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-1'de veya öncesinde ortaya çıkmıştır. burn_coin fonksiyonu herkese açıktır. Herkes bu fonksiyonu çağırarak herhangi birinin parasını yakabilir.

pub fn burn_coin(&mut self, amount: U128, fee: Balance, sender_id: ValidAccountId) -> Balance{
        assert!(self.is_redeem_paused(), "{}", SYSTEM_PAUSE);
        let sender_id = AccountId::from(sender_id);
        self.assert_is_poked();
        self.accured_token(sender_id.clone());
        ...
    }

Liste 2.9: burn_coin:lib.rs

Etki Herkes bu fonksiyonu kullanarak herhangi birinin parasını yakabilir, bu da kullanıcıların varlıklarının kaybına yol açar.

Öneri I Bu fonksiyon, para yakmak için kararlı ücretin aktarılmasının ardından dahili olarak çağrıldığından özel bir fonksiyon olarak değiştirilmelidir.

2.2.4 Potansiyel Sorun 8: deposit_token erişim kontrolünden yoksun

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-1'de veya öncesinde ortaya çıkmıştır. deposit_token fonksiyonu herkese açıktır. Herkes bu fonksiyonu çağırarak kendi hesabına bakiye ekleyebilir.

pub fn deposit_token(&mut self, amount: u128, _sender_id: ValidAccountId) {
        self.assert_is_poked();
        assert!(self.is_stake_paused(), "{}", SYSTEM_PAUSE);
        let _amount = u128::from(amount);
        let sender_id = AccountId::from(_sender_id);
        . . .
    }

Liste 2.10: deposit_token:lib.rs

Etki Saldırganlar bu fonksiyonu çağırarak kendi hesaplarına bakiye ekleyebilir.

Öneri I Bu fonksiyon, yatırılan token'ları aldıktan sonra dahili olarak çağrıldığından özel bir fonksiyon olarak değiştirilmelidir.

2.2.5 Potansiyel Sorun 9: Oracle zaman kontrolünden yoksun

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-1'de veya öncesinde ortaya çıkmıştır. oracle.rs dosyasındaki assert_is_poked fonksiyonu yalnızca token fiyatının sıfır olup olmadığını kontrol etmektedir. Token fiyatı sürekli değiştiğinden bu durum mantıklı değildir.

pub(crate) fn assert_is_poked(&self) {
        assert!(self.token_price != 0, "Oracle price isn't poked.");
    }

Liste 2.11: assert_is_poked:oracle.rs

Etki Bu sorun fiyat oracle'larını etkiler. Token fiyatı uzun süredir güncellenmemişse assertion yine de geçilebilir ve ilgili işlem güncel olmayan bir fiyatla yürütülebilir.

Öneri I Sözleşme, işaretlenmiş fiyat için geçerli bir zaman dilimi belirlemelidir.

2.2.6 Potansiyel Sorun 10: Uygunsuz oracle işaretleme aralık süresi

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-3'te veya öncesinde ortaya çıkmıştır. types.rs dosyasında tanımlanan POKE_INTERVAL_TIME sabiti şu an 1000 günü temsil etmektedir. Bu zaman aralığı çok uzun görünmektedir. Makul bir değer gereklidir.

pub const POKE_INTERVAL_TIME: u64 = 86_400_000_000_000_000;

Liste 2.12: types.rs

Etki İşaretlenmiş fiyat için zaman aralığı uygunsuzdur.

Öneri I İşaretlenmiş fiyat için aralık süresini makul bir değerle yeniden ayarlayın.

2.2.7 Potansiyel Sorun 11: Oin_Price için Eksik Doğrulama

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-1'de veya öncesinde ortaya çıkmıştır. Kullanıcının kararlı ücreti self.oin_price ile hesaplandığından, bu fonksiyon oin_token fiyatının işaretlenip işaretlenmediğini kontrol etmemektedir.

pub fn internal_user_stable(&self, account: AccountId) -> u128 {
        let user_stable = self.account_stable.get(&account).expect("error");
        let allot = self.get_account_allot(account.clone()); 
        let coin = self
            .account_coin
            .get(&account)
            .expect("error")
            .checked_add(allot.0)
            .expect(ERR_ADD);
        let current_block_number = env::block_timestamp().checked_div(INIT_BLOCK_TIME).expect(ERR_DIV);
        user_stable
            .saved_stable
            .checked_add(
                self.stable_fee_rate//16
                    .checked_div(BLOCK_PER_YEAR)
                    .expect(ERR_DIV)
                    .checked_mul(current_block_number as u128 - user_stable.block)
                    .expect(ERR_MUL)
                    .checked_mul(coin)//8
                    .expect(ERR_MUL)
                    .checked_div(self.oin_price)//8
                    .expect(ERR_DIV)
                    .checked_div(ONE_COIN)//8
                    .expect(ERR_DIV),
            )
            .expect(ERR_ADD)
    }

Liste 2.13: internal_user_stable:lib.rs

Etki Oracle tarafından işaretlenen fiyatın güncelliğinin kontrol edilmemesi, eski OIN fiyatının fiyat manipülasyonuna yol açmasına neden olabilir.

Öneri I Kullanıcının kararlı ücretini hesaplamadan önce self.assert_is_poked(); doğrulamasını ekleyin.

2.2.8 Potansiyel Sorun 12: Kullanıcılar token stake ederek daha fazla madencilik ödülü kazanabilir

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-1'de veya öncesinde ortaya çıkmıştır. Talep edilen ödül doğru hesaplanmamaktadır. internal_get_saved_reward fonksiyonu, kullanıcının t0'dan t1'e kadar olan belirli madencilik ödülünü aşağıdaki formülle hesaplamak için çağrılır:

account_allot.token'ın, diğer kullanıcıların tasfiyesiyle eklenen teminat ödülü olduğuna dikkat edin. Ancak tasfiye, t0'dan t1'e kadar herhangi bir zamanda gerçekleşebilir. Örneğin, bir kullanıcı 0. günde 100 token yatırdı. 999. günde, başka bir kullanıcı için tasfiye tetiklendiğinden account_allot.token değeri 1000'e yükselmiş olabilir.

Kullanıcı 1000. günde ödülünü talep ettiğinde, 999. günde gerçekleşen tasfiyeden kaynaklanan 1000 token yalnızca bir gün için madencilik sayılmalıdır. Ancak sözleşme, teminat ödülü için madencilik ödülünü 0. günden 1000. güne kadar hesaplamaktadır.

// TODO[OK] Calculation of reward
    pub(crate) fn internal_get_saved_reward(
        &self,
        staker: AccountId,      
        reward_coin: AccountId, 
    ) -> u128 {
        let reward_coin_ins = self.internal_get_reward_coin(reward_coin.clone());
        let (stake_token_num, _) = self.staker_debt_of(staker.clone());

        if let Some(user_reward) = self
            .account_reward
            .get(&self.get_staker_reward_key(staker.clone(), reward_coin.clone()))
        {
            user_reward
                .reward
                .checked_add(
                    U256::from(
                        reward_coin_ins
                            .index
                            .checked_sub(user_reward.index)
                            .expect(ERR_SUB),
                    )
                    .checked_mul(U256::from(stake_token_num))
                    .expect(ERR_MUL)
                    .checked_div(U256::from(reward_coin_ins.double_scale))
                    .expect(ERR_DIV)
                    .as_u128(),
                )
                .expect(ERR_ADD)
        } else {
            0
        }
    }

Liste 2.14: internal_get_saved_reward:views.rs

pub fn staker_debt_of(&self, staker: AccountId) -> (u128, u128) {
        if let Some(token) = self.account_token.get(&staker) {
            let coin = self.account_coin.get(&staker).expect(ERR_NOT_REGISTER);
            let allot = self.get_account_allot(staker.clone());
            (token + allot.1, coin + allot.0)
        } else {
            (0, 0)
        }
    }

Liste 2.15: staker_debt_of:views.rs

Etki Kullanıcılar ekstra ödül kazanabilir.

Öneri I Madencilik ödülü hesaplanırken yeni tahsis edilen teminat bölümünü kaldırın. Madencilik ödülünü yalnızca kullanıcının yatırdığı token miktarıyla ilişkilendirin.

2.2.9 Potansiyel Sorun 13: Kullanıcılar daha az kararlı ücret ödeyebilir

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-1'de veya öncesinde ortaya çıkmıştır. Bir kullanıcının 0. günde 1000 USDO bastığını ve o andaki stable_fee_rate değerinin 0,01oin/coin/gün olduğunu varsayalım. Kullanıcı 1000 USDO'yu 100. günde iade ederse ve son 100 gün içinde kararlı ücret oranı değişmediyse, ödemesi gereken kararlı ücret 0,01 Oin/coin/gün * 1000 Coin * 100 Gün = 1000 Oin olur. Ancak, sahip stable_fee_rate değerini 99. günde 0,005 oin/coin/gün olarak ayarlarsa, kullanıcı yalnızca 0,005 Oin/Coin/Gün * 1000 Coin * 100 Gün = 500 Oin ödemesi gerekir. Gerçekte doğru ücret şöyle olmalıdır: (0,01 Oin/Coin/Gün * 1000 Coin * 99 Gün) + (0,005 Oin/Coin/Gün * 1000 Coin * 1 Gün) = 990 Oin + 5 Oin = 995 Oin.

Bu durumda, 495 Oin kullanıcılar tarafından ödenmek zorunda değildir.

// TODO [OK]
    pub fn set_stable_fee_rate(&mut self, fee_rate: U128) {
        self.assert_param_white();
        self.update_stable_index();
        assert!(fee_rate.0 <= INIT_MAX_STABLE_FEE_RATE, "Exceeding the maximum setting");
        self.stable_fee_rate = fee_rate.into();
        log!("Set stable fee rate {}", fee_rate.0);
    }

Liste 2.16: set_stable_fee_rate:dparam.rs

pub fn update_stable_index(&mut self) {
    }

Liste 2.17: update_stable_index:stablefee.rs

Etki Sözleşme kullanıcıları kararlı ücret için daha az ücretlendirilebilir.

Öneri I Bu sözleşmedeki reward_coin hesaplamasına benzer şekilde kararlı ücretin sistem indeksini uygulayın. Kararlı ücretin sistem indeksinin, set_stable_fee_rate, liquidation ve update_stable_fee sözleşme kullanıcıları tarafından çağrıldığında güncellendiğinden emin olun.

2.2.10 Potansiyel Sorun 14: Makul olmayan çoklu imzalı istek onay oranı

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-1'de veya öncesinde ortaya çıkmıştır. Çoklu imzalı istek onay oranı, istek oluşturulduğundaki çoklu imza yöneticisi sayısına göre hesaplanmaktadır. Ancak çoklu imza yöneticisi sayısı daha sonra değişebilir. Bu durumda, yönetici sayısı artarsa istek düşük onay oranıyla onaylanabilir.

pub(crate) fn is_num_enough(&self, request_id: RequestId) -> bool {
        let request = self.requests.get(&request_id).unwrap();
        let confirmations = self.confirmations.get(&request_id).unwrap();

        let num_confirmrations = request.num_confirm_ratio * (request.mul_white_num);
        log!(
            "confim num is {} num needed is {} ",
            confirmations.len() as u32 * 100,
            num_confirmrations
        );

        (confirmations.len() as u64) * 100 >= num_confirmrations
    }

Liste 2.18: is_num_enough:multisign.rs

pub fn add_request_only(&mut self, request: MultiSigRequest) -> RequestId {
        self.assert_mul_white();
        ...

        let request_added = MultiSigRequestWithSigner {
            signer_pk: env::signer_account_pk(),
            added_timestamp: env::block_timestamp(),
            confirmed_timestamp: 0,
            request: request,
            is_executed: false,
            cool_down: self.request_cooldown,
            mul_white_num: self.mul_white_num(),
            num_confirm_ratio: self.num_confirm_ratio,
        };

        self.requests.insert(&self.request_nonce, &request_added);
        ...
    }

Liste 2.19: add_request_only:multisign.rs

Etki Sözleşme yalnızca istek oluşturulduğundaki yönetici sayısını dikkate aldığından, çoklu imzalı istekler düşük onay oranıyla onaylanabilir.

Öneri I Çoklu imzalı istek onay oranını hesaplamak için mevcut sözleşme durumundaki çoklu imzalı kullanıcı sayısını kullanmayı düşünün.

2.2.11 Potansiyel Sorun 15: Yıl başına hatalı blok sayısı

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-1'de veya öncesinde ortaya çıkmıştır. NEAR protokolünün ana ağında her saniyede bir blok oluşturulduğu göz önüne alındığında, yıl başına oluşturulan blok sayısı 31104000 (360 gün) yerine 31536000 (365 gün) olmalıdır.

pub const BLOCK_PER_YEAR: u128 = 31104000;

Liste 2.20: types.rs

Etki BLOCK_PER_YEAR için hatalı sabit, bu sabiti kullanan hesaplamaların sonuçlarını gerçeklikle tutarsız kılacaktır.

Öneri I BLOCK_PER_YEAR değerini 31536000 olarak değiştirin.

2.2.12 Potansiyel Sorun 16: Basılabilecek maksimum USDO miktarının hatalı hesaplanması

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-1'de veya öncesinde ortaya çıkmıştır. allot_token.0 tahsis edilmiş borcu temsil etmektedir. USDO için kullanılabilir basım miktarı hesaplanırken tahsis edilmiş borç sayılmamalıdır. Aksi takdirde, çok yüksek borca sahip bir kullanıcı büyük miktarda USDO basabilir.

pub(crate) fn internal_can_mint_amount(&self, account: AccountId) -> u128 {
        self.assert_is_poked();
        let token = self.account_token.get(&account).expect(ERR_NOT_REGISTER);
        let guarantee = self.guarantee.get(&account).expect(ERR_NOT_REGISTER);
        let allot_token = self.get_account_allot(account.clone());

        let max_usdo = (U256::from(token)
            .checked_add(U256::from(allot_token.1))
            .expect(ERR_ADD))
        .checked_mul(U256::from(self.token_price))
        .expect(ERR_MUL)
        .checked_div(U256::from(self.liquidation_line))
        .expect(ERR_DIV)
        .checked_div(U256::from(INIT_STABLE_INDEX))
        .expect(ERR_DIV)
        .checked_add(U256::from(allot_token.0))
        .expect(ERR_ADD)
        .checked_sub(U256::from(guarantee))
        .unwrap_or(U256::from(0))
        .as_u128();
        
        ...
    }

Liste 2.21: internal_can_mint_amount:lib.rs

Etki Kullanıcılar mint_coin fonksiyonunu çağırırken ek USDO basabilir.

Öneri I Tahsis edilmiş borcu temsil eden allot_token.0, kullanılabilir basılmış USDO olarak sayılmamalıdır.

2.2.13 Potansiyel Sorun 17: Kullanıcının kararlı ücretinin hatalı işlenmesi

Öğe Açıklama
Durum Onaylandı ve düzeltildi (İlgili mantık artık kaldırılmıştır)

Açıklama Bu sorun Commit-1'de veya öncesinde ortaya çıkmıştır. Kullanıcılar burn_coin fonksiyonunu çağırdığında, kararlı ücret 'ST_NEAR' yerine 'OIN' token ile ödenmektedir. Ancak sözleşme, kullanıcının staking token bakiyesini azaltacaktır ki bu doğru değildir.

pub(crate) fn burn_coin(&mut self, amount: U128, fee: Balance, sender_id: ValidAccountId) -> Balance{
        ...
            assert!(usdo >= amount.into(), "Insufficient amount");
            let token = self.account_token.get(&sender_id.clone()).expect(ERR_NOT_REGISTER);
            self.internal_burn(sender_id.clone(), amount.into());
   
            self.total_token = self.total_token.checked_sub(unpaid_fee.into()).expect(ERR_SUB);
            self.account_token.insert(
                &sender_id.clone(),
                &token.checked_sub(unpaid_fee.into()).expect(ERR_SUB),
            );
        ...
        
    }

Liste 2.22: burn_coin:lib.rs

Etki Kullanıcının kararlı ücretinin hatalı işlenmesi nedeniyle kullanıcıların staking token'ları azaltılabilir.

Öneri I Kararlı ücretleri ödemek için doğru token'ı kullanın.

2.2.14 Potansiyel Sorun 18: Hatalı sistem oranı

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-1'de veya öncesinde ortaya çıkmıştır. total_coin = 0 ise oran +∞ olmalıdır. Bunu 0 olarak ayarlamak hatalıdır.

pub(crate) fn internal_sys_ratio(&self) -> u128 {
        self.assert_is_poked();
        let token_usd = U256::from(self.total_token)
            .checked_mul(U256::from(self.token_price))
            .expect(ERR_MUL); /* 32 */
        let total_coin = self.total_coin + self.total_guarantee;
        if total_coin == 0 {
            0
        } else {
            token_usd
                .checked_div(U256::from(STAKE_RATIO_BASE))
                .expect(ERR_DIV)
                .checked_div(U256::from(total_coin))
                .expect(ERR_DIV)
                .as_u128()
        }
    }

Liste 2.23: internal_sys_ratio:lib.rs

Etki Hatalı oran nedeniyle sistem büyük olasılıkla kapanacaktır.

Öneri I total_coin = 0 koşulunu token_usd = 0 olarak değiştirin.

2.2.15 Potansiyel Sorun 19: Ödül parası sayısı üst sınırı aşabilir

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-1'de veya öncesinde ortaya çıkmıştır. Şu anda 20 ödül parası olduğunda, Liste 2.24'ün 131. satırındaki assertion geçilebilir. Bu durumda, bir ödül parası daha eklenebilir ve toplam ödül parası sayısı REWARD_UPPER_BOUND'u aşabilir.

pub(crate) fn internal_add_reward_coin(&mut self, coin: RewardCoin) {
        assert!(
            self.reward_coins.len() <= REWARD_UPPER_BOUND,
            "The currency slot has been used up, please modify other currency information as appropriate",
        );

        match self.reward_coins.get(&coin.token) {
            Some(_) => {
                env::panic(b"The current currency has been added, please add a new currency.");
            }
            None => {}
        }
        self.reward_coins.insert(&coin.token, &coin);

        log!(
            "{} add the RewardCoin=> {:?}",
            env::predecessor_account_id(),
            coin
        )
    }

Liste 2.24: internal_add_reward_coin:pool.rs

Etki Eklenebilecek ödül parası sayısı sistemin tasarımıyla çelişmektedir.

Öneri I Assertion'ı self.reward_coins.len() < REWARD_UPPER_BOUND olarak değiştirin.

2.2.16 Potansiyel Sorun 20: Farklı ayrıcalıklardaki kullanıcılar aynı beyaz listeyi kullanıyor

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-1'de veya öncesinde ortaya çıkmıştır. assert_param_white, assert_white, assert_esm_white ve assert_oracle_white fonksiyonları farklı ayrıcalıklar için kullanılmaktadır. Ancak hepsi aynı beyaz listeyi paylaşmaktadır.

pub(crate) fn assert_esm_white(&self) {
        self.assert_white()
    }

Liste 2.25: assert_esm_white:esm.rs

pub(crate) fn assert_param_white(&self) {
        self.assert_white();
    }

Liste 2.26: assert_param_white:dparam.rs

pub(crate) fn assert_oracle_white(&self) {
        self.assert_white();
    }

Liste 2.27: assert_oracle_white:oracle.rs

Etki Farklı ayrıcalıklardaki kullanıcılar aynı beyaz listeyi paylaşmaktadır.

Öneri I Farklı ayrıcalıklara sahip kullanıcılar için ayrı beyaz listeler uygulayın.

2.2.17 Potansiyel Sorun 21: burn_coin token türünü kontrol etmiyor

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-1'de veya öncesinde ortaya çıkmıştır. burn_coin fonksiyonu token türünü kontrol etmemektedir. Bu durumda saldırganlar, kararlı ücreti ödemek için belirli bir miktarda keyfi token aktarabilir.

pub fn burn_coin(&mut self, amount: U128, fee: Balance, sender_id: ValidAccountId) -> Balance{
        assert!(self.is_redeem_paused(), "{}", SYSTEM_PAUSE);
        let sender_id = AccountId::from(sender_id);

Liste 2.28: assert_esm_white:esm.rs

Etki Kullanıcıların Oin token ödemesine gerek kalmaz. Bunun yerine, gerekli miktarda keyfi token aktararak kararlı ücretini ödeyebilirler.

Öneri I Alınan token'ın adresini kontrol edin.

2.2.18 Potansiyel Sorun 22: Ödül parasının total_reward değeri çoklu imza yöneticileri tarafından değiştirilebilir

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-3'te veya öncesinde ortaya çıkmıştır. inject_reward fonksiyonu #[private] ile işaretlenmiştir. Bu nedenle, çoklu imza yöneticileri bu fonksiyonu çoklu imza istekleri aracılığıyla çağırabilir ve ödül enjekte etmeden toplam ödül üzerinde keyfi miktarda artış yapabilir.

#[payable]
    #[private]
    pub fn inject_reward(&mut self, amount: U128, reward_coin: AccountId) {
        // self.assert_owner();

        if reward_coin == String::from("NEAR") {
            assert!(
                amount.0 == env::attached_deposit(),
                "Amount not equal transfer_amount"
            );
        }

        if let Some(reward_coin_ins) = self.get_reward_coin(reward_coin.clone()) {
            let mut reward_coin_ins = reward_coin_ins;
            reward_coin_ins.total_reward = reward_coin_ins
                .total_reward
                .checked_add(amount.into())
                .expect(ERR_SUB);
            self.reward_coins.insert(&reward_coin, &reward_coin_ins);

            if reward_coin == String::from("NEAR") {
            
            } else {
                log!("Transfer is not required for post-processing");
            }
        } else {
            env::panic(b"No the reward coin.");
        }
    }

Liste 2.29: ainject_reward:pool.rs

Öneri I #[private] dekoratörünü kaldırın ve inject_reward fonksiyonunun görünürlüğünü özel olarak değiştirin.

2.3 Ek Öneriler

2.3.1 Gereksiz assertion

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-2'de veya öncesinde ortaya çıkmıştır. inject_reward fonksiyonu yalnızca ft_on_transfer tarafından dahili olarak çağrılmalıdır. Ödül parasının adresi ft_on_transfer içinde kontrol edilmektedir. Bu durumda, inject_reward fonksiyonunun başında ödül parasının adını kontrol etmemize gerek yoktur.

#[payable]
    #[private]
    pub  fn inject_reward(&mut self, amount: U128, reward_coin: AccountId) {
        // self.assert_owner();

        if reward_coin == String::from("NEAR") {
            assert!(
                amount.0 == env::attached_deposit(),
                "Amount not equal transfer_amount"
            );
        }

    ...
    }

Liste 2.30: inject_reward:pool.rs


    pub fn ft_on_transfer(
        &mut self,
        sender_id: ValidAccountId,
        amount: U128,
        msg: String, /* token */
    ) -> PromiseOrValue<U128> {
    ...
            FtOnTransferArgs::InjectReward => {
                assert_eq!(sender_id.to_string(), self.owner_id, "ERR_NOT_ALLOWED");

                assert!(
                    self.reward_coins.get(&token_account_id).is_some(),
                    "Invalid reward coin"
                );

                self.inject_reward(amount, token_account_id);
                amount_return = 0;
            }
    ...
    }

Liste 2.31: ft_on_transfer:lib.rs

Öneri I inject_reward içindeki ödül parası adı kontrolünü kaldırın.

2.3.2 Kullanıcının tasfiye oranı için tekrarlanan assertion

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-1'de veya öncesinde ortaya çıkmıştır. Tasfiye sınırı zaten internal_avaliable_token fonksiyonunda dikkate alındığından, daha sonra kullanıcı oranının tasfiye sınırına ulaşıp ulaşmadığını kontrol etmeye gerek yoktur.

#[payable]
    pub fn withdraw_token(&mut self, amount: U128) {
        assert!(self.is_stake_paused(), "{}", SYSTEM_PAUSE);
        let mut amount = amount.0;

        let token = self.internal_avaliable_token(env::predecessor_account_id());
        let debt = self.get_dept(env::predecessor_account_id());

        log!("token :{} amount: {}", token, amount);
        assert!(token >= amount, "Insufficient avaliable token.");
        if debt.0 - debt.2 == 0 {
            if token - amount < self._min_amount_token() {
                amount = token;
            }
        } else {
            self.assert_user_ratio();
            if token - amount < self._min_amount_token() {
                env::panic(b"Please return all coins first");
            }
        }

Liste 2.32: withdraw_token:lib.rs

Öneri I Liste 2.32'nin 559. satırındaki gereksiz assertion'ı kaldırın.

2.3.3 Gereksiz beyaz liste kontrolü

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-1'de veya öncesinde ortaya çıkmıştır. set_reward_speed fonksiyonu, ayrıcalığı kontrol etmek için assert_param_white fonksiyonunu çağırmaktadır. Bu arada, set_reward_speed tarafından çağrılan internal_set_reward_speed, assert_white'ı tekrar çağırmaktadır. assert_white, assert_param_white ile aynı beyaz listeye sahiptir.

pub fn set_reward_speed(&mut self, reward_coin: AccountId, speed: U128) {
        self.assert_param_white();
        self.internal_set_reward_speed(reward_coin, speed);
    }

Liste 2.33: set_reward_speed:dparam.rs

pub(crate) fn internal_set_reward_speed(&mut self, reward_coin: AccountId, speed: U128) {
        self.assert_white();
        self.update_index();
        . . .
    }

Liste 2.34: internal_set_reward_speed:pool.rs

Öneri I internal_set_reward_speed fonksiyonunun içindeki assert_white'ı kaldırın.

2.3.4 Kullanılmayan fonksiyon

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-3'te veya öncesinde ortaya çıkmıştır. on_inject_reward fonksiyonu başka hiçbir fonksiyon tarafından kullanılmamaktadır. Bu nedenle kaldırılabilir.

#[private]
    pub fn on_inject_reward(&mut self, reward_coin: AccountId, amount: U128) {
        match env::promise_result(0) {
            PromiseResult::NotReady => unreachable!(),
            PromiseResult::Successful(_) => {}
            PromiseResult::Failed => {
                let mut reward_coin_ins = self.internal_get_reward_coin(reward_coin.clone());
                reward_coin_ins.total_reward = reward_coin_ins
                    .total_reward
                    .checked_sub(amount.into())
                    .expect(ERR_ADD);
                self.reward_coins.insert(&reward_coin, &reward_coin_ins);
            }
        };
    }

Liste 2.35: on_inject_reward:pool.rs

Öneri I on_inject_reward fonksiyonunu kaldırın.

2.3.5 Gereksiz Kod

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-3'te veya öncesinde ortaya çıkmıştır. account_allot.get() fonksiyonu tahsis edilmiş ödülü ve borcu almak için kullanılmaktadır. set_account_allot fonksiyonunun içinde bu fonksiyonun çağrılması gerekmemektedir.

pub(crate) fn set_account_allot(&mut self,account_id: AccountId){
        //Update [personally assigned debt, personally assigned pledge] to system value
        let (allot_debt, allot_token) = self.get_account_allot(account_id.clone());
        let token = self.account_token.get(&account_id).expect(ERR_NOT_REGISTER);
        let coin = self.account_coin.get(&account_id).expect(ERR_NOT_REGISTER);

        self.account_allot.get(&account_id);

        self.account_allot.insert(
            &account_id, 
            &AccountAllot{
                account_allot_debt: self.sys_allot_debt,
                account_allot_token: self.sys_allot_token,
            }
        );
        self.account_coin.insert(&account_id, &coin.checked_add(allot_debt).expect(ERR_ADD));
        self.account_token.insert(&account_id, &token.checked_add(allot_token).expect(ERR_ADD));       
    }

Liste 2.36: set_account_allot:allot.rs

Öneri I 42. satırdaki account_allot.get() çağrısını kaldırın.

2.3.6 Fonksiyon adı ile uygulama zıt yönde

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-3'te veya öncesinde ortaya çıkmıştır. is_stake_paused, is_redeem_paused, is_claim_reward_paused, is_liquidation_paused ve is_stable_paused fonksiyonları, ilgili fonksiyonun duraklatılıp duraklatılmadığını temsil etmek için tanımlanmıştır. Ancak belirli öznitelik aktif olduğunda True döndürmektedir.

// TODO [OK]
    pub(crate) fn is_stake_paused(&self) -> bool {
        self.stake_live == 1
    }

    // TODO [OK]
    pub(crate) fn is_redeem_paused(&self) -> bool {
        self.redeem_live == 1
    }

    // TODO [OK]
    pub(crate) fn is_claim_reward_paused(&self) -> bool {
        self.claim_live == 1
    }

    // TODO [OK]
    pub(crate) fn is_liquidation_paused(&self) -> bool {
        self.liquidation_live == 1
    }

    // TODO [OK]
    pub(crate) fn is_stable_paused(&self) -> bool {
        self.stable_live == 1
    }

Liste 2.37: is_{stake|redeem|claim_reward|liquidation|stable}_paused:esm.rs

Öneri I is_{stake|redeem|claim_reward|liquidation|stable}_paused fonksiyon adlarını is_{stake|redeem|claim_reward|liquidation|stable}_live olarak değiştirin.

2.3.7 Gereksiz Kod

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-3'te veya öncesinde ortaya çıkmıştır. update_stable_fee fonksiyonu, gerekli kararlı ücretleri güncellemek için kullanılmaktadır. Kararlı ücretler, stake edilmiş token'larla ilgili değildir. Bu nedenle, kullanıcılar için token bakiyesini değiştirmek kararlı ücretlerin güncellenmesini gerektirmez.

pub(crate) fn deposit_token(&mut self, _amount: u128, _sender_id: ValidAccountId) {
        self.assert_is_poked();
        assert!(self.is_stake_paused(), "{}", SYSTEM_PAUSE);
        let sender_id = AccountId::from(_sender_id);
        assert!(_amount > 0, "Deposit token amount must greater than zero.");

        if let Some(0) = self.guarantee.get(&sender_id) {
            assert!(
                _amount >= self._min_amount_token(),
                "Deposit token amount must greater the minimum deposit token."
            );
        }
        self.update_personal_token(sender_id.clone());
        self.update_stable_fee(sender_id.clone());
        self.set_account_allot(sender_id.clone());
        . . .
    }

Liste 2.38: deposit_token:lib.rs

Öneri I 344. satırdaki update_stable_fee çağrısını kaldırın.

2.3.8 Hesaplama hassasiyeti artırılabilir

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-3'te veya öncesinde ortaya çıkmıştır. internal_user_stable fonksiyonu kararlı ücreti hesaplamayı amaçlamaktadır. Bölmeden önce çarpma işlemi yapılarak hesaplama hassasiyeti artırılabilir.

pub(crate) fn update_stable_fee(&mut self, account: AccountId) {
        if let Some(mut user_stable) = self.account_stable.get(&account) {
            let allot = self.get_account_allot(account.clone());
            let debt = allot.0;
            let current_block_number = self.to_nano( env::block_timestamp()) as u128;

            let coin = self.account_coin.get(&account).expect(ERR_NOT_REGISTER).checked_add(debt).expect(ERR_ADD);
            let delta_block = current_block_number.checked_sub(user_stable.block).expect(ERR_SUB);
            if delta_block > 0 && coin > 0 {
                let fee = self.stable_fee_rate//16
                        .checked_mul(delta_block).expect(ERR_MUL)
                        .checked_mul(coin).expect(ERR_MUL)//8
                        .checked_div(BLOCK_PER_YEAR).expect(ERR_DIV)
                        .checked_div(self.oin_price).expect(ERR_DIV)//8
                        .checked_div(ONE_COIN).expect(ERR_DIV);//8
                        
                self.saved_stable = self.saved_stable
                        .checked_add(fee).expect(ERR_ADD);

                user_stable.saved_stable = user_stable.saved_stable
                        .checked_add(fee).expect(ERR_ADD); 
            }
            
            user_stable.block = current_block_number;
            self.account_stable.insert(&account, &user_stable);    
            log!("Current stabilization fee: {:?}",self.account_stable.get(&account));
        } else {
            env::panic(b"Not register")
        }
    }

Liste 2.39: update_stable_fee:stablefee.rs

Öneri I 25. satırdan 30. satıra kadar olan hesaplama için bölmeden önce çarpma işlemi yapın.

2.3.9 Sistem daha önce işaretlenmiş fiyatı kaydetmeyebilir

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-1'de veya öncesinde ortaya çıkmıştır. Fonksiyon doğru uygulanmamıştır. Sözleşmede yatırılan toplam token sayısı çoğu durumda 0'dan büyük olduğundan, sistem işaretlenmiş fiyatı kaydetmeyebilir.

pub fn poke(&mut self, token_price: U128) {
    ...
       if self.total_token > 0 {
           if self.internal_sys_ratio() <= INIT_MIN_RATIO_LINE {
                self.internal_shutdown();
           }
       }else {
            log!(
                "{} poke price {} successfully.",
                env::predecessor_account_id(),
                token_price.0
            );
        }
    }

Liste 2.40: poke:oracle.rs

Öneri I Token fiyatının işaretlenmesi davranışının kaydedilmesi, sözleşmedeki yatırılmış token sayısından etkilenmemelidir.

2.3.10 Tasfiyede teminat token'ının süreksiz dağılımı

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-4'te veya öncesinde ortaya çıkmıştır. Kullanıcının staking oranı %108,5'ten büyük veya eşit olduğunda, kullanıcıların allot_debt'in %2'sine karşılık gelen liquidation_fee ödemesi gerekmektedir. Ancak kullanıcının staking oranı %108,5'ten düşükse liquidation fee ödemesi gerekmemektedir. Bu durum, daha yüksek staking oranına sahip kullanıcının tasfiye sonrasında havuza daha az staking token tahsis edebileceği sonucunu doğurmaktadır.

#[payable]
    pub fn liquidation(&mut self, account: AccountId) {
        ...
        if ratio >= INIT_NO_LIQUIDATION_FEE_RATE {
            liquidation_fee = _allot_debt
                            .checked_mul(self.liquidation_fee_ratio).expect(ERR_MUL)
                            .checked_mul(STAKE_RATIO_BASE).expect(ERR_MUL)//16
                            .checked_div(self.token_price).expect(ERR_DIV);
        }else{
            allot_ratio = ratio
                .checked_sub(self.gas_compensation_ratio).expect(ERR_SUB)
                .checked_add(1).expect(ERR_ADD);
        }
        ...

Liste 2.41: liquidation:lib.rs

Öneri I Staking oranı %108,5 ile %110,5 arasında olan kullanıcılar için tasfiye ücretinin (staking oranı - %108,5) olması önerilir.

2.3.11 Hesaplama hassasiyetinin optimize edilmesi gerekli değil

Öğe Açıklama
Durum Onaylandı ve düzeltildi

Açıklama Bu sorun Commit-4'te veya öncesinde ortaya çıkmıştır. Liste 2.42'nin 832. satırındaki 1 eklenmesi, self.gas_compensation_ratio oldukça büyük olduğundan hesaplama hassasiyetini artırmamaktadır.

#[payable]
    pub fn liquidation(&mut self, account: AccountId) {
        ...
        if ratio >= INIT_NO_LIQUIDATION_FEE_RATE {
            liquidation_fee = _allot_debt
                            .checked_mul(self.liquidation_fee_ratio).expect(ERR_MUL)
                            .checked_mul(STAKE_RATIO_BASE).expect(ERR_MUL)//16
                            .checked_div(self.token_price).expect(ERR_DIV);
        }else{
            allot_ratio = ratio
                .checked_sub(self.gas_compensation_ratio).expect(ERR_SUB)
                .checked_add(1).expect(ERR_ADD);
        }
        ...

Liste 2.42: liquidation:lib.rs

Öneri I Liste 2.42'nin 831. satırındaki eklenen "1"i kaldırın.

2.3.12 Merkezi Tasarımın Riski

Durum Kabul edildi

Açıklama Projenin oldukça merkezi bir tasarımı bulunmaktadır. Sözleşme sahibi, çoklu imza yöneticilerini ekleyip silebilen ve tasfiye ücretini ile ödülü çekebilen çok yüksek ayrıcalığa sahiptir. Bu tür bir mekanizma tamamen merkezidir ve tüm token'lar üzerinde tam kontrol yetkisi vermektedir. Proje sahibinin, sözleşmeleri yönetmek için sözleşme sahibinin özel anahtarlarını korumak amacıyla güvenlik mekanizmaları uygulamasını şiddetle öneririz.

3. Notlar ve Açıklamalar

3.1 Sorumluluk Reddi

Bu denetim raporu, yatırım tavsiyesi veya kişisel bir öneri niteliği taşımamaktadır. Bir token, token satışı veya herhangi bir ürün, hizmet ya da diğer varlıkların potansiyel ekonomisini dikkate almaz ve bu şekilde yorumlanamaz. Herhangi bir kuruluş bu rapora herhangi bir amaçla, herhangi bir token, ürün, hizmet veya diğer varlıkları satın alma veya satma kararları vermek dahil olmak üzere, güvenmemelidir.

Bu denetim raporu, belirli bir projeyi veya ekibi onaylamaz; rapor herhangi bir projenin güvenliğini garanti etmez. Bu denetim, akıllı sözleşmelerin tüm güvenlik sorunlarını keşfetme konusunda herhangi bir garanti vermez; yani değerlendirme sonucu, başka güvenlik sorunlarının bulunmadığını garanti etmez. Tek bir denetim kapsamlı kabul edilemeyeceğinden, akıllı sözleşmelerin güvenliğini sağlamak için bağımsız denetimlere ve kamuya açık hata ödül programına başvurulmasını her zaman öneririz.

Bu denetimin kapsamı, Bölüm 1.1'de belirtilen kodla sınırlıdır. Açıkça belirtilmediği sürece, dilin kendisinin güvenliği (örneğin, Rust dili), temel derleme araç zinciri ve bilişim altyapısı kapsam dışındadır.

3.2 Denetim Prosedürü

Denetimi aşağıdaki prosedüre göre gerçekleştiriyoruz.

  • Güvenlik Açığı Tespiti Önce akıllı sözleşmeleri otomatik kod analizörleriyle tarıyor, ardından bildirilen sorunları manuel olarak doğruluyoruz (reddediyor veya onaylıyoruz).

  • Semantik Analiz Akıllı sözleşmelerin iş mantığını inceliyor ve araştırma ekibimiz tarafından geliştirilen otomatik bir bulanıklık aracı kullanarak olası güvenlik açıkları üzerinde daha fazla araştırma yürütüyoruz. Ayrıca sonuçları çapraz kontrol etmek için bağımsız denetçilerle birlikte olası saldırı senaryolarını manuel olarak analiz ediyoruz.

  • Öneri Geliştiricilere gaz optimizasyonu, kod stili vb. dahil olmak üzere iyi programlama uygulamaları perspektifinden faydalı tavsiyeler sunuyoruz.

Aşağıda ana somut kontrol noktalarını gösteriyoruz.

3.2.1 Yazılım Güvenliği

  • Yeniden giriş (Reentrancy)

  • Hizmet Reddi (DoS)

  • Erişim kontrolü

  • Veri işleme ve veri akışı

  • İstisna işleme

  • Güvenilmez harici çağrı ve kontrol akışı

  • Başlatma tutarlılığı

  • Olay işlemleri

  • Hata eğilimli rastgelelik

  • Proxy sisteminin hatalı kullanımı

3.2.2 DeFi Güvenliği

  • Semantik tutarlılık

  • İşlevsellik tutarlılığı

  • Erişim kontrolü

  • İş mantığı

  • Token işlemi

  • Acil durum mekanizması

  • Oracle güvenliği

  • Beyaz liste ve kara liste

  • Ekonomik etki

  • Toplu transfer

3.2.3 NFT Güvenliği

  • Yinelenen öğe

  • Token alıcısının doğrulanması

  • Zincir dışı meta veri güvenliği

3.2.4 Ek Öneri

  • Gaz optimizasyonu

  • Kod kalitesi ve stili

Önceki kontrol noktaları ana olanlardır. Denetim sürecinde projenin işlevselliğine göre daha fazla kontrol noktası kullanabiliriz.

Best Security Auditor for Web3

Validate design, code, and business logic before launch. Aligned with the highest industry security standards.

BlockSec Audit