Bericht-Manifest
| Element | Beschreibung |
|---|---|
| Kunde | Oinfinance |
| Ziel | NearOinDao |
Versionsverlauf
| Version | Datum | Beschreibung |
|---|---|---|
| 1.0 | 04. Dez. 2021 | Erste Veröffentlichung |
1. Einleitung
1.1 Über die Ziel-Verträge
Die Ziel-Verträge enthalten ein Stablecoin-Modul. Darum herum implementieren sie weitere Module, einschließlich Staking und Farming. Diese Module erzeugen eine positive Rückkopplungsschleife zur Stabilisierung des Stablecoins, d. h. USDO.
| Information | Beschreibung |
|---|---|
| Typ | Smart Contract |
| Sprache | Rust |
| Ansatz | Halbautomatische und manuelle Überprüfung |
Die Repositories, die geprüft wurden, beinhalten NearOinDao ^1
Der Prüfungsprozess ist iterativ. Insbesondere werden wir die Commits, die die gefundenen Probleme beheben, weiter prüfen. Sollten neue Probleme auftreten, werden wir diesen Prozess fortsetzen. Daher werden in diesem Bericht mehrere Commit-SHA-Werte referenziert. Die Commit-SHA-Werte vor und nach der Prüfung sind im Folgenden dargestellt.
Vor und während der Prüfung

Nachher
| Projekt | Commit SHA |
|---|---|
| NearOinDao | 3bd117606c753d3c2f66b6dcddd1ae18ea47a20a |
1.2 Sicherheitsmodell
Zur Risikobewertung folgen wir Standards oder Vorschlägen, die sowohl in der Industrie als auch in der Wissenschaft weit verbreitet sind, einschließlich der OWASP Risk Rating Methodology ^2 und der Common Weakness Enumeration ^3. Dementsprechend ist die in diesem Bericht gemessene Schwere in vier Kategorien unterteilt: Hoch, Mittel, Niedrig und Unbestimmt.
2. Ergebnisse
Insgesamt haben wir 22 potenzielle Probleme im Smart Contract gefunden. Wir haben zudem 12 Empfehlungen wie folgt:
-
Hohes Risiko: 19
-
Mittleres Risiko: 2
-
Niedriges Risiko: 1
-
Empfehlungen: 12
Die Details werden in den folgenden Abschnitten bereitgestellt.
| ID | Schweregrad | Beschreibung | Kategorie | Status |
|---|---|---|---|---|
| 1 | Hoch | Logikfehler bei der Änderung von self.liquidation_line | Softwaresicherheit | Bestätigt und behoben |
| 2 | Hoch | Funktion liquidation funktioniert möglicherweise nicht | Softwaresicherheit | Bestätigt und behoben |
| 3 | Hoch | Logikfehler beim Setzen des Zeitstempels zum Öffnen des Vertrags | Softwaresicherheit | Bestätigt und behoben |
| 4 | Hoch | Vertragsstatus nicht zurückgesetzt bei gescheiterter Cross-Contract-Transaktion | Softwaresicherheit | Bestätigt und behoben |
| 5 | Hoch | Jeder kann das Belohnungsguthaben hinzufügen | DeFi-Sicherheit | Bestätigt und behoben |
| 6 | Hoch | Jeder kann das Belohnungsguthaben des stabilen Pools hinzufügen | DeFi-Sicherheit | Bestätigt und behoben |
| 7 | Hoch | Jeder kann die Coins anderer Benutzer verbrennen | DeFi-Sicherheit | Bestätigt und behoben |
| 8 | Hoch | Jeder kann das Guthaben seines Kontos hinzufügen | DeFi-Sicherheit | Bestätigt und behoben |
| 9 | Hoch | Orakel prüft das Zeitintervall nicht | DeFi-Sicherheit | Bestätigt und behoben |
| 10 | Hoch | Orakel-Zeitintervall ist zu lang | DeFi-Sicherheit | Bestätigt und behoben |
| 11 | Hoch | Kein Orakel für Oin-Preis | DeFi-Sicherheit | Bestätigt und behoben |
| 12 | Hoch | Benutzer können zusätzliche Belohnungen erhalten | DeFi-Sicherheit | Bestätigt und behoben |
| 13 | Hoch | Benutzer können weniger Stabilitätsgebühr zahlen | DeFi-Sicherheit | Bestätigt und behoben |
| 14 | Mittel | Multi-Sign-Anfrage kann mit relativ niedrigem Bestätigungsverhältnis bestätigt werden | DeFi-Sicherheit | Bestätigt und behoben |
| 15 | Mittel | Blocknummer pro Jahr ist ungenau | DeFi-Sicherheit | Bestätigt und behoben |
| 16 | Hoch | Verfügbare gemintete Coins sind nicht korrekt | DeFi-Sicherheit | Bestätigt und behoben |
| 17 | Hoch | Zahlung der Stabilitätsgebühr kann zum Verlust hinterlegter Token führen | DeFi-Sicherheit | Bestätigt und behoben |
| 18 | Hoch | Inkorrektes Staking-Verhältnis | DeFi-Sicherheit | Bestätigt und behoben |
| 19 | Niedrig | Belohnungs-Coins können Begrenzung überschreiten | DeFi-Sicherheit | Bestätigt und behoben |
| 20 | Hoch | Gleiche Whitelist für Benutzer mit unterschiedlichen Privilegien | DeFi-Sicherheit | Bestätigt und behoben |
| 21 | Hoch | Keine Prüfung der Adresse der Stabilitätsgebühr | DeFi-Sicherheit | Bestätigt und behoben |
| 22 | Hoch | total_reward der Belohnungs-Coins kann von Multi-Signatur-Managern geändert werden | DeFi-Sicherheit | Bestätigt und behoben |
| 23 | - | Überflüssige Assertion | Empfehlung | Bestätigt und behoben |
| 24 | - | Wiederholte Berücksichtigung der Liquidationslinie | Empfehlung | Bestätigt und behoben |
| 25 | - | Überflüssige Whitelist-Prüfung | Empfehlung | Bestätigt und behoben |
| 26 | - | Nicht verwendete Funktion | Empfehlung | Bestätigt und behoben |
| 27 | - | Redundanter Code | Empfehlung | Bestätigt und behoben |
| 28 | - | Funktionsname und Implementierung stehen im Widerspruch | Empfehlung | Bestätigt und behoben |
| 29 | - | Redundanter Code | Empfehlung | Bestätigt und behoben |
| 30 | - | Rechenpräzision kann verbessert werden | Empfehlung | Bestätigt und behoben |
| 31 | - | System zeichnet möglicherweise zuvor abgefragten Preis nicht auf | Empfehlung | Bestätigt und behoben |
| 32 | - | Diskontinuierliche Verteilung von Collateral-Token bei Liquidation | Empfehlung | Bestätigt und behoben |
| 33 | - | Optimierung der Rechenpräzision nicht notwendig | Empfehlung | Bestätigt und behoben |
| 34 | - | Risiko durch zentralisiertes Design | Empfehlung | Anerkannt |
2.1 Softwaresicherheit
2.1.1 Potenzielles Problem 1: Zwei verschiedene Attribute für die gleiche Verwendung
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-1 eingeführt. Zwei Attribute (d.h. self.cost und self.liquidation_line) repräsentieren den gleichen Vertragsstatus, nämlich die Liquidationslinie des Benutzers. Sie werden in verschiedenen Funktionen des Vertrags verwendet (Auflistung 2.1 und 2.2). Allerdings kann self.liquidation_line mit der Funktion set_liquidation_line geändert werden, während self.cost nicht geändert werden kann. In diesem Fall, wenn self.liquidation_line geändert wird, behält self.cost den ursprünglichen Wert bei. Dies kann die Logik der Funktion assert_user_ratio beeinflussen (Auflistung 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.");
}
}
Auflistung 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");
...
Auflistung 2.2: internal_can_mint_amount:lib.rs
Auswirkung Die Liquidationslinie der Benutzer ist in verschiedenen Funktionen des Vertrags inkonsistent, was die Logik des gesamten Vertrags beeinflusst.
Vorschlag I Wir können die Verwendung dieser beiden Attribute vereinheitlichen, wenn das Staking-Verhältnis des Benutzers berechnet und mit der Liquidationslinie des Systems verglichen wird.
2.1.2 Potenzielles Problem 2: Ungültige Verteilung der Liquidationsbelohnung
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-4 eingeführt. Das Konto des Liquidationssenders und das Konto des Vertragsinhabers sind möglicherweise nicht registriert (Zeile 193 und 206 von Auflistung 2.3). In diesem Fall kann die Transaktion, wenn der Sender eine Liquidationsaktion durchführen möchte, aufgrund der ausgelösten Ausnahme, dass die Konten nicht registriert sind, nicht erfolgreich ausgeführt werden.
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);
}
}
Auflistung 2.3: personal_liquidation_token:reward.rs
Auswirkung Die Liquidationsfunktion kann aufgrund der ausgelösten Ausnahme, dass die Konten nicht registriert sind, nicht erfolgreich ausgeführt werden.
Vorschlag I Überprüfen Sie den Bestand des Kontos des Liquidationssenders und des Vertragsinhabers zu Beginn der Liquidationsfunktion.
2.1.3 Potenzielles Problem 3: Block_timestamp wird beim Öffnen des Systems in closed_time gespeichert
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-3 eingeführt. env::block_time_stamp() sollte nicht in self.closed_time gespeichert werden, wenn die Funktion internal_open aufgerufen wird.
#[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
);
}
Auflistung 2.4: internal_open:esm.rs
Auswirkung Die Öffnungszeit und die Schließzeit des Vertrags sind völlig falsch. Weitere Aktualisierungen, die von Zeitinformationen abhängen, können Logikfehler aufweisen.
Vorschlag I Wir schlagen vor, einen neuen Vertragsstatus namens self.opening_time zu erstellen und env::block_timestamp() diesem Wert zuzuweisen, während das Öffnen des Vertrags aufgerufen wird.
2.1.4 Potenzielles Problem 4: Vertragsstatus wird nicht zurückgesetzt, wenn Cross-Contract-Aufrufe fehlschlagen
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-3 eingeführt. Der Prozess von storage_deposit und ft_transfer kann während der Cross-Contract-Funktionsaufrufe fehlschlagen. Wir können nicht garantieren, dass der Transfer immer korrekt ausgeführt wird. Die Callback-Funktion setzt den Vertragsstatus nicht zurück, wenn der Aufruf fehlschlägt.
#[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");
}
}
}
Auflistung 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");
}
}
}
Auflistung 2.6: liquidation_transfer_callback:ft.rs
Auswirkung Benutzer könnten ihre Vermögenswerte verlieren, wenn Transaktionen fehlschlagen, da die Callback-Funktion den Vertragsstatus nicht zurücksetzt.
Vorschlag I Wir müssen den Vertragsstatus (wenn der Transfer fehlschlägt) in der Callback-Funktion der Cross-Contract-Funktionsaufrufe zurücksetzen.
2.2 DeFi-Sicherheit
2.2.1 Potenzielles Problem 5: inject_reward fehlt Zugriffskontrolle
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-1 eingeführt. Die Funktion inject_reward ist öffentlich. Jeder kann diese Funktion aufrufen, um das Belohnungsguthaben im Vertrag zu erhöhen.
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"
);
}
...
}
Auflistung 2.7: inject_reward:pool.rs
Auswirkung Jeder kann ein beliebiges Guthaben auf die Belohnung des Vertrags hinzufügen.
Vorschlag I Diese Funktion sollte in eine private geändert werden, da sie intern aufgerufen wird, nachdem die übertragene Belohnung empfangen wurde.
2.2.2 Potenzielles Problem 6: inject_sp_reward fehlt Zugriffskontrolle
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-1 eingeführt. Die Funktion inject_sp_reward ist öffentlich. Jeder kann diese Funktion aufrufen, um das Guthaben der stabilen Pool-Belohnung im Vertrag zu erhöhen.
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
);
}
Auflistung 2.8: inject_sp_reward:stablepool.rs
Auswirkung Jeder kann ein beliebiges Guthaben auf die stabile Pool-Belohnung des Vertrags hinzufügen.
Vorschlag I Diese Funktion sollte in eine private geändert werden, da sie intern aufgerufen wird, nachdem die übertragene stabile Pool-Belohnung empfangen wurde.
2.2.3 Potenzielles Problem 7: burn_coin fehlt Zugriffskontrolle
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-1 eingeführt. Die Funktion burn_coin ist öffentlich. Jeder kann diese Funktion aufrufen, um die Coins von jemand anderem zu verbrennen.
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());
...
}
Auflistung 2.9: burn_coin:lib.rs
Auswirkung Jeder kann diese Funktion nutzen, um Coins von jemand anderem zu verbrennen, was zum Verlust von Vermögenswerten der Benutzer führt.
Vorschlag I Diese Funktion sollte in eine private geändert werden, da sie intern aufgerufen wird, nachdem die übertragene Stabilitätsgebühr zum Verbrennen von Coins empfangen wurde.
2.2.4 Potenzielles Problem 8: deposit_token fehlt Zugriffskontrolle
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-1 eingeführt. Die Funktion deposit_token ist öffentlich. Jeder kann diese Funktion aufrufen, um das Guthaben seines Kontos zu erhöhen.
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);
. . .
}
Auflistung 2.10: deposit_token:lib.rs
Auswirkung Angreifer können diese Funktion aufrufen, um das Guthaben ihres Kontos zu erhöhen.
Vorschlag I Diese Funktion sollte in eine private geändert werden, da sie intern aufgerufen wird, nachdem die hinterlegten Token empfangen wurden.
2.2.5 Potenzielles Problem 9: Orakel fehlt Zeitprüfung
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-1 eingeführt. Die Funktion assert_is_poked in oracle.rs prüft nur, ob der Wert des Token-Preises null ist. Das ergibt keinen Sinn, da sich der Token-Preis ständig ändert.
pub(crate) fn assert_is_poked(&self) {
assert!(self.token_price != 0, "Oracle price isn't poked.");
}
Auflistung 2.11: assert_is_poked:oracle.rs
Auswirkung Dieses Problem betrifft Preis-Orakel. Wenn der Token-Preis für eine lange Zeit nicht aktualisiert (poked) wurde, kann die Assertion immer noch bestanden werden, und die zugehörige Transaktion kann mit einem veralteten Preis ausgeführt werden.
Vorschlag I Der Vertrag sollte ein gültiges Zeitfenster für den abgefragten Preis festlegen.
2.2.6 Potenzielles Problem 10: Unangemessenes Orakel-Abfrageintervall
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-3 eingeführt. Die in types.rs definierte Konstante POKE_INTERVAL_TIME bedeutet derzeit 1000 Tage. Dieses Zeitintervall scheint zu lang zu sein. Ein angemessener Wert ist erforderlich.
pub const POKE_INTERVAL_TIME: u64 = 86_400_000_000_000_000;
Auflistung 2.12: types.rs
Auswirkung Das Zeitintervall für abgefragte Preise ist unangemessen.
Vorschlag I Setzen Sie das Intervall für abgefragte Preise auf einen angemessenen Wert zurück.
2.2.7 Potenzielles Problem 11: Fehlende Assertion für Oin_Price
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-1 eingeführt. Diese Funktion prüft nicht, ob der Wert des oin_token Preises abgefragt wurde, da die Stabilitätsgebühr des Benutzers durch self.oin_price berechnet wird.
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)
}
Auflistung 2.13: internal_user_stable:lib.rs
Auswirkung Der veraltete OIN-Preis kann zu Preismanipulation führen, ohne die Aktualität des vom Orakel abgefragten Preises zu prüfen.
Vorschlag I Fügen Sie eine self.assert_is_poked(); Assertion vor der Berechnung der Stabilitätsgebühr des Benutzers hinzu.
2.2.8 Potenzielles Problem 12: Benutzer erhalten möglicherweise mehr Mining-Belohnungen durch Staking von Token
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-1 eingeführt. Die beanspruchte Belohnung wird nicht genau berechnet. Die Funktion internal_get_saved_reward wird aufgerufen, um die spezifische Mining-Belohnung des Benutzers von t0 bis t1 mit der folgenden Formel zu berechnen:

Beachten Sie, dass account_allot.token die Sicherheiten-Belohnung ist, die durch die Liquidation eines anderen Benutzers hinzugefügt wurde. Jedoch kann eine Liquidation jederzeit von t0 bis t1 auftreten. Zum Beispiel hat ein Benutzer am Tag 0 100 Token hinterlegt. Am Tag 999 wird eine Liquidation für einen anderen Benutzer ausgelöst, sodass account_allot.token auf 1000 steigen könnte.
Wenn der Benutzer seine Belohnung am Tag 1000 beansprucht, sollten die 1000 Token, die aus der Liquidation am Tag 999 resultieren, nur für einen Tag für das Mining gezählt werden. Der Vertrag berechnet jedoch tatsächlich die Mining-Belohnung für die Sicherheiten-Belohnung von Tag 0 bis Tag 1000.
// 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
}
}
Auflistung 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)
}
}
Auflistung 2.15: staker_debt_of:views.rs
Auswirkung Benutzer können zusätzliche Belohnungen erhalten.
Vorschlag I Entfernen Sie die Partitionierung der neu zugewiesenen Sicherheiten bei der Berechnung der Mining-Belohnung. Wir können die Mining-Belohnung nur auf die Menge der vom Benutzer eingezahlten Token beziehen.
2.2.9 Potenzielles Problem 13: Benutzer zahlen möglicherweise weniger Stabilitätsgebühr
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-1 eingeführt. Angenommen, ein Benutzer mint 1000 USDO am Tag 0, und die stable_fee_rate zu diesem Zeitpunkt beträgt 0,01 oin/coin/tag. Wenn der Benutzer die 1000 USDO am Tag 100 zurückgibt und sich die stable_fee_rate während der letzten 100 Tage nicht ändert, beträgt die zu zahlende Stabilitätsgebühr 0,01 oin/coin/tag * 1000 Coin * 100 Tage = 1000 Oin. Wenn der Eigentümer jedoch am Tag 99 die stable_fee_rate auf 0,005 oin/coin/tag setzt, muss der Benutzer nur 0,005 oin/coin/tag * 1000 Coin * 100 Tage = 500 Oin zahlen. Tatsächlich sollte die korrekte Gebühr sein: (0,01 oin/coin/tag * 1000 Coin * 99 Tage) + (0,005 oin/coin/tag * 1000 Coin * 1 Tag) = 990 Oin + 5 Oin = 995 Oin.
In diesem Fall müssen die 495 Oin nicht von den Benutzern bezahlt werden.
// 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);
}
Auflistung 2.16: set_stable_fee_rate:dparam.rs
pub fn update_stable_index(&mut self) {
}
Auflistung 2.17: update_stable_index:stablefee.rs
Auswirkung Vertragsbenutzern wird möglicherweise weniger Stabilitätsgebühr in Rechnung gestellt.
Vorschlag I Implementieren Sie das System-Index der Stabilitätsgebühr wie die Berechnung von reward_coin in diesem Vertrag. Stellen Sie sicher, dass das System-Index der Stabilitätsgebühr aktualisiert wird, wann immer set_stable_fee_rate, Liquidation und update_stable_fee von Vertragsbenutzern abgerufen werden.
2.2.10 Potenzielles Problem 14: Unangemessene Bestätigungsrate für Multi-Signatur-Anfragen
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-1 eingeführt. Die Bestätigungsrate für Multi-Signatur-Anfragen wird durch die Anzahl der Multi-Signatur-Manager berechnet, als die Anfrage erstellt wurde. Die Anzahl der Multi-Signatur-Manager kann sich jedoch später ändern. Wenn die Anzahl der Manager steigt, kann die Anfrage mit einem niedrigen Bestätigungsverhältnis bestätigt werden.
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
}
Auflistung 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);
...
}
Auflistung 2.19: add_request_only:multisign.rs
Auswirkung Multi-Signatur-Anfragen könnten mit einer niedrigen Bestätigungsrate bestätigt werden, da der Vertrag nur die Anzahl der Manager berücksichtigt, als die Anfrage erstellt wurde.
Vorschlag I Erwägen Sie die Verwendung der Anzahl der Multi-Signatur-Benutzer im aktuellen Vertragsstatus, um die Bestätigungsrate für Multi-Signatur-Anfragen zu berechnen.
2.2.11 Potenzielles Problem 15: Inkorrekte Blocknummer pro Jahr
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-1 eingeführt. Da auf dem NEAR-Mainnet jede Sekunde ein Block generiert wird, sollte die generierte Blocknummer pro Jahr 31536000 (365 Tage) betragen, anstatt 31104000 (360 Tage).
pub const BLOCK_PER_YEAR: u128 = 31104000;
Auflistung 2.20: types.rs
Auswirkung Eine ungenaue Konstante für BLOCK_PER_YEAR macht die Ergebnisse von Berechnungen inconsistent mit der Realität.
Vorschlag I Ändern Sie die BLOCK_PER_YEAR auf 31536000.
2.2.12 Potenzielles Problem 16: Inkorrekte Berechnung der maximalen USDO-Mint-Menge
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-1 eingeführt. allot_token.0 repräsentiert die zugewiesene Schuld. Bei der Berechnung der verfügbaren Mint-Menge für USDO sollte die zugewiesene Schuld nicht mitgezählt werden. Andernfalls könnte ein Benutzer mit sehr hohen Schulden eine riesige Menge an USDO minten.
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();
...
}
Auflistung 2.21: internal_can_mint_amount:lib.rs
Auswirkung Benutzer können zusätzliche USDOs minten, wenn sie die Funktion mint_coin aufrufen.
Vorschlag I Die allot_token.0, die die zugewiesene Schuld repräsentiert, sollte nicht als verfügbare gemintete USDOs gezählt werden.
2.2.13 Potenzielles Problem 17: Inkorrekter Umgang mit der Stabilitätsgebühr des Benutzers
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben (die zugehörige Logik wurde entfernt) |
Beschreibung Dieses Problem wurde in oder vor Commit-1 eingeführt. Wenn Benutzer die Funktion burn_coin aufrufen, wird die Stabilitätsgebühr mit dem 'OIN' Token anstelle von 'ST_NEAR' bezahlt. Der Vertrag reduziert jedoch das Guthaben des Staking-Tokens des Benutzers, was nicht genau ist.
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),
);
...
}
Auflistung 2.22: burn_coin:lib.rs
Auswirkung Das Staking-Token der Benutzer kann aufgrund des inkorrekten Umgangs mit der Stabilitätsgebühr reduziert werden.
Vorschlag I Verwenden Sie das korrekte Token zur Zahlung der Stabilitätsgebühren.
2.2.14 Potenzielles Problem 18: Inkorrektes Systemverhältnis
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-1 eingeführt. Wenn total_coin = 0, sollte das Verhältnis +∞ sein. Es auf 0 zu setzen, ist inkorrekt.
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()
}
}
Auflistung 2.23: internal_sys_ratio:lib.rs
Auswirkung Das System wird wahrscheinlich aufgrund des inkorrekten Verhältnisses herunterfahren.
Vorschlag I Ändern Sie die if-Bedingung auf total_coin = 0.
2.2.15 Potenzielles Problem 19: Die Anzahl der Belohnungs-Coins kann den oberen Wert überschreiten
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-1 eingeführt. Wenn es aktuell 20 Belohnungs-Coins gibt, kann die Assertion in Zeile 131 von Auflistung 2.24 bestanden werden. In diesem Fall kann ein weiterer Belohnungs-Coin hinzugefügt werden, und die Gesamtzahl der Belohnungs-Coins kann den REWARD_UPPER_BOUND überschreiten.
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
)
}
Auflistung 2.24: internal_add_reward_coin:pool.rs
Auswirkung Die verfügbare Anzahl von Belohnungs-Coins steht im Widerspruch zum Systemdesign.
Vorschlag I Ändern Sie die Assertion in self.reward_coins.len() < REWARD_UPPER_BOUND.
2.2.16 Potenzielles Problem 20: Benutzer mit unterschiedlichen Privilegien verwenden dieselbe Whitelist
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-1 eingeführt. Die Funktionen assert_param_white, assert_white, assert_esm_white, assert_oracle_white werden für unterschiedliche Privilegien verwendet. Sie teilen sich jedoch dieselbe Whitelist.
pub(crate) fn assert_esm_white(&self) {
self.assert_white()
}
Auflistung 2.25: assert_esm_white:esm.rs
pub(crate) fn assert_param_white(&self) {
self.assert_white();
}
Auflistung 2.26: assert_param_white:dparam.rs
pub(crate) fn assert_oracle_white(&self) {
self.assert_white();
}
Auflistung 2.27: assert_oracle_white:oracle.rs
Auswirkung Benutzer mit unterschiedlichen Privilegien teilen sich dieselbe Whitelist.
Vorschlag I Implementieren Sie unterschiedliche Whitelists für Benutzer mit unterschiedlichen Privilegien.
2.2.17 Potenzielles Problem 21: burn_coin prüft den Token-Typ nicht
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-1 eingeführt. Die Funktion burn_coin prüft den Token-Typ nicht. In diesem Fall können Angreifer beliebige Token in spezifizierter Menge zur Zahlung der Stabilitätsgebühr übertragen.
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);
Auflistung 2.28: assert_esm_white:esm.rs
Auswirkung Benutzer müssen den Oin-Token nicht verwenden. Stattdessen können sie die Stabilitätsgebühr durch Transferieren eines beliebigen Tokens mit der erforderlichen Menge zahlen.
Vorschlag I Prüfen Sie die Adresse des empfangenen Tokens.
2.2.18 Potenzielles Problem 22: total_reward der Belohnungs-Coins kann von Multi-Signatur-Managern modifiziert werden
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-3 eingeführt. Die Funktion inject_reward ist mit #[private] dekoriert. Daher können Multi-Signatur-Manager diese Funktion durch Multi-Signatur-Anfragen aufrufen und einen beliebigen Betrag der Gesamtbelohnung hinzufügen, ohne die Belohnung tatsächlich einzuzahlen.
#[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.");
}
}
Auflistung 2.29: inject_reward:pool.rs
Vorschlag I Entfernen Sie den Dekorator #[private] und ändern Sie die Sichtbarkeit der Funktion inject_reward in privat.
2.3 Zusätzliche Empfehlung
2.3.1 Überflüssige Assertion
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-2 eingeführt. Die Funktion inject_reward sollte nur intern von ft_on_transfer aufgerufen werden. Die Adresse des Belohnungs-Tokens wird bereits in ft_on_transfer geprüft. In diesem Fall ist es nicht notwendig, den Namen des Belohnungs-Tokens zu Beginn der Funktion inject_reward erneut zu prüfen.
#[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"
);
}
...
}
Auflistung 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;
}
...
}
Auflistung 2.31: ft_on_transfer:lib.rs
Vorschlag I Entfernen Sie die Prüfung auf den Namen des Belohnungs-Tokens in inject_reward.
2.3.2 Wiederholte Assertion für das Liquidationsverhältnis des Benutzers
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-1 eingeführt. Die Liquidationslinie wird bereits in der Funktion internal_avaliable_token berücksichtigt, daher ist es nicht nötig, später erneut zu prüfen, ob das user_ratio die Liquidationslinie erreicht hat.
#[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");
}
}
Auflistung 2.32: withdraw_token:lib.rs
Vorschlag I Entfernen Sie die überflüssige Assertion in Zeile 559 der Auflistung 2.32.
2.3.3 Überflüssige Whitelist-Prüfung
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-1 eingeführt. Die Funktion set_reward_speed ruft die Funktion assert_param_white auf, um das Privileg zu prüfen. Unterdessen ruft internal_set_reward_speed, das von set_reward_speed aufgerufen wird, erneut assert_white auf. assert_white hat dieselbe Whitelist wie assert_param_white.
pub fn set_reward_speed(&mut self, reward_coin: AccountId, speed: U128) {
self.assert_param_white();
self.internal_set_reward_speed(reward_coin, speed);
}
Auflistung 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();
. . .
}
Auflistung 2.34: internal_set_reward_speed:pool.rs
Vorschlag I Entfernen Sie assert_white innerhalb der Funktion internal_set_reward_speed.
2.3.4 Nicht verwendete Funktion
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-3 eingeführt. Die Funktion on_inject_reward wird von keiner anderen Funktion verwendet. Sie kann daher entfernt werden.
#[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);
}
};
}
Auflistung 2.35: on_inject_reward:pool.rs
Vorschlag I Entfernen Sie die Funktion on_inject_reward.
2.3.5 Redundanter Code
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-3 eingeführt. Die Funktion account_allot.get() wird verwendet, um die zugewiesene Belohnung und Schuld abzurufen. Innerhalb der Funktion set_account_allot ist der Aufruf dieser Funktion nicht erforderlich.
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));
}
Auflistung 2.36: set_account_allot:allot.rs
Vorschlag I Entfernen Sie den Aufruf account_allot.get() in Zeile 42.
2.3.6 Funktionsname und Implementierung sind gegensätzlich
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-3 eingeführt. Die Funktionen is_stake_paused, is_redeem_paused, is_claim_reward_paused, is_liquidation_paused, is_stable_paused sind so definiert, dass sie repräsentieren, ob die Funktion pausiert ist oder nicht. Wenn jedoch das spezifische Attribut live ist, gibt es True zurück.
// 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
}
Auflistung 2.37: is_{stake|redeem|claim_reward|liquidation|stable}_paused:esm.rs
Vorschlag I Ändern Sie den Funktionsnamen der is_{stake|redeem|claim_reward|liquidation|stable}paused Funktionen in is{stake|redeem|claim_reward|liquidation|stable}_live.
2.3.7 Redundanter Code
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-3 eingeführt. Die Funktion update_stable_fee wird verwendet, um die erforderlichen Stabilitätsgebühren zu aktualisieren. Stabilitätsgebühren stehen nicht im Zusammenhang mit den hinterlegten Token. Daher ist es nicht nötig, die Stabilitätsgebühren zu aktualisieren, wenn das Token-Guthaben der Benutzer geändert wird.
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());
. . .
}
Auflistung 2.38: deposit_token:lib.rs
Vorschlag I Entfernen Sie den Aufruf update_stable_fee in Zeile 344.
2.3.8 Die Rechenpräzision kann verbessert werden
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-3 eingeführt. Die Funktion internal_user_stable zielt darauf ab, die Stabilitätsgebühr zu berechnen. Die Rechenpräzision kann verbessert werden, indem die Multiplikation vor der Division durchgeführt wird.
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")
}
}
Auflistung 2.39: update_stable_fee:stablefee.rs
Vorschlag I Führen Sie die Multiplikation vor der Division für die Berechnung von Zeile 25 bis 30 durch.
2.3.9 System zeichnet möglicherweise zuvor abgefragten Preis nicht auf
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-1 eingeführt. Die Funktion ist nicht korrekt implementiert. Das System zeichnet den abgefragten Preis möglicherweise nicht auf, da die Anzahl der im Vertrag hinterlegten Token in den meisten Fällen größer als 0 ist.
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
);
}
}
Auflistung 2.40: poke:oracle.rs
Vorschlag I Die Aufzeichnung des Verhaltens des Abfragens des Token-Preises sollte nicht von der Anzahl der im Vertrag hinterlegten Token beeinflusst werden.
2.3.10 Diskontinuierliche Verteilung von Collateral-Token bei Liquidation
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-4 eingeführt. Wenn das Staking-Verhältnis des Benutzers größer oder gleich 108,5 % ist, müssen Benutzer die liquidation_fee zahlen, die 2 % der allot_debt beträgt. Wenn das Staking-Verhältnis des Benutzers jedoch unter 108,5 % liegt, muss er/sie keine Liquidationsgebühr zahlen. Dies führt dazu, dass Benutzer mit einem höheren Staking-Verhältnis nach der Liquidation weniger Staking-Token im Pool zuteilen können.
#[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);
}
...
Auflistung 2.41: liquidation:lib.rs
Vorschlag I Für Benutzer, deren Staking-Verhältnis zwischen 108,5 % und 110,5 % liegt, wird empfohlen, die Liquidationsgebühr als (Staking-Verhältnis - 108,5 %) zu berechnen.
2.3.11 Optimierung der Rechenpräzision nicht notwendig
| Element | Beschreibung |
|---|---|
| Status | Bestätigt und behoben |
Beschreibung Dieses Problem wurde in oder vor Commit-4 eingeführt. Das Hinzufügen von 1 in Zeile 832 in Auflistung 2.42 kann die Rechenpräzision nicht erhöhen, da self.gas_compensation_ratio ziemlich groß ist.
#[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);
}
...
Auflistung 2.42: liquidation:lib.rs
Vorschlag I Entfernen Sie die hinzugefügte "1" in Zeile 831 der Auflistung 2.42.
2.3.12 Risiko durch zentralisiertes Design
Status Anerkannt
Beschreibung Das Projekt hat ein hoch zentralisiertes Design. Der Vertragsinhaber hat sehr hohe Privilegien, kann Multi-Signatur-Manager hinzufügen/löschen und kann die Liquidationsgebühr und Belohnung usw. abheben. Ein solcher Mechanismus ist absolut zentralisiert und hat eine vollständige Kontrolle über alle Token. Wir empfehlen dringend, dass der Projekteigentümer Sicherheitsmechanismen durchsetzt, um die privaten Schlüssel des Vertragsinhabers zur Verwaltung der Verträge zu schützen.
3. Hinweise und Anmerkungen
3.1 Haftungsausschluss
Dieser Prüfbericht stellt keine Anlageberatung oder persönliche Empfehlung dar. Er berücksichtigt nicht und sollte nicht so interpretiert werden, dass er die potenzielle Wirtschaftlichkeit eines Tokens, eines Token-Verkaufs oder eines anderen Produkts, einer Dienstleistung oder eines anderen Vermögenswerts berücksichtigt oder irgendeinen Einfluss darauf hat. Jede Einheit sollte sich in keiner Weise auf diesen Bericht verlassen, einschließlich für den Zweck, Entscheidungen zum Kauf oder Verkauf eines Tokens, Produkts, einer Dienstleistung oder eines anderen Vermögenswerts zu treffen.
Dieser Prüfbericht ist keine Billigung eines bestimmten Projekts oder Teams, und der Bericht garantiert nicht die Sicherheit eines bestimmten Projekts. Diese Prüfung gibt keine Garantien, dass alle Sicherheitsprobleme der Smart Contracts entdeckt wurden, d. h. das Bewertungsergebnis garantiert nicht das Nichtvorhandensein weiterer gefundener Sicherheitsprobleme. Da eine Prüfung nicht als umfassend angesehen werden kann, empfehlen wir immer, unabhängige Prüfungen und ein öffentliches Bug-Bounty-Programm fortzusetzen, um die Sicherheit der Smart Contracts zu gewährleisten.
Der Umfang dieser Prüfung ist auf den in Abschnitt 1.1 genannten Code begrenzt. Sofern nicht explizit angegeben, ist die Sicherheit der Sprache selbst (z. B. die Rust-Sprache), die zugrunde liegende Kompilierungs-Toolchain und die IT-Infrastruktur außerhalb des Umfangs.
3.2 Vorgehensweise bei der Prüfung
Wir führen die Prüfung nach folgendem Verfahren durch.
-
Schwachstellenerkennung Wir scannen zuerst Smart Contracts mit automatischen Code-Analysatoren und verifizieren dann manuell (Ablehnung oder Bestätigung) die von ihnen gemeldeten Probleme.
-
Semantische Analyse Wir untersuchen die Geschäftslogik von Smart Contracts und führen weitere Untersuchungen zu möglichen Schwachstellen unter Verwendung eines automatischen Fuzzing-Tools durch (entwickelt von unserem Forschungsteam). Wir analysieren auch manuell mögliche Angriffsszenarien mit unabhängigen Prüfern, um das Ergebnis gegenzuprüfen.
-
Empfehlung Wir geben Entwicklern nützliche Ratschläge aus der Perspektive bewährter Programmierpraktiken, einschließlich Gas-Optimierung, Codestil usw.
Die wichtigsten konkreten Checkpunkte zeigen wir im Folgenden.
3.2.1 Softwaresicherheit
-
Wiedereintritt (Reentrancy)
-
DoS
-
Zugriffskontrolle
-
Datenverarbeitung und Datenfluss
-
Ausnahmebehandlung
-
Nicht vertrauenswürdiger externer Aufruf und Kontrollfluss
-
Initialisierungskonsistenz
-
Ereignis-Operation
-
Fehleranfällige Zufälligkeit
-
Unsachgemäße Verwendung des Proxy-Systems
3.2.2 DeFi-Sicherheit
-
Semantische Konsistenz
-
Funktionale Konsistenz
-
Zugriffskontrolle
-
Geschäftslogik
-
Token-Operation
-
Notfallmechanismus
-
Orakel-Sicherheit
-
Whitelist und Blacklist
-
Wirtschaftliche Auswirkungen
-
Batch-Transfer
3.2.3 NFT-Sicherheit
-
Duplizierte Elemente
-
Verifizierung des Token-Empfängers
-
Off-Chain-Metadaten-Sicherheit
3.2.4 Zusätzliche Empfehlung
-
Gas-Optimierung
-
Codequalität und Stil
Die vorherigen Checkpunkte sind die wichtigsten. Wir können während des Prüfungsprozesses je nach Funktionalität des Projekts weitere Checkpunkte verwenden.



