near_contract_standards/storage_management/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use near_sdk::{ext_contract, near, AccountId, NearToken};

#[near(serializers=[borsh, json])]
pub struct StorageBalance {
    pub total: NearToken,
    pub available: NearToken,
}

#[near(serializers=[borsh, json])]
pub struct StorageBalanceBounds {
    pub min: NearToken,
    pub max: Option<NearToken>,
}

/// Ensures that when fungible token storage grows by collections adding entries,
/// the storage is be paid by the caller. This ensures that storage cannot grow to a point
/// that the FT contract runs out of Ⓝ.
/// Takes name of the Contract struct, the inner field for the token and optional method name to
/// call when the account was closed.
///
/// # Examples
///
/// ```
/// use near_sdk::{near, PanicOnDefault, AccountId, NearToken, log};
/// use near_sdk::collections::LazyOption;
/// use near_sdk::json_types::U128;
/// use near_contract_standards::fungible_token::FungibleToken;
/// use near_contract_standards::storage_management::{
///     StorageBalance, StorageBalanceBounds, StorageManagement,
/// };
/// use near_contract_standards::fungible_token::metadata::FungibleTokenMetadata;
///
/// #[near(contract_state)]
/// #[derive(PanicOnDefault)]
/// pub struct Contract {
///     token: FungibleToken,
///     metadata: LazyOption<FungibleTokenMetadata>,
/// }
///
/// #[near]
/// impl StorageManagement for Contract {
///     #[payable]
///     fn storage_deposit(
///         &mut self,
///         account_id: Option<AccountId>,
///         registration_only: Option<bool>,
///     ) -> StorageBalance {
///         self.token.storage_deposit(account_id, registration_only)
///     }
///
///     #[payable]
///     fn storage_withdraw(&mut self, amount: Option<NearToken>) -> StorageBalance {
///         self.token.storage_withdraw(amount)
///     }
///
///     #[payable]
///     fn storage_unregister(&mut self, force: Option<bool>) -> bool {
///         #[allow(unused_variables)]
///         if let Some((account_id, balance)) = self.token.internal_storage_unregister(force) {
///             log!("Closed @{} with {}", account_id, balance);
///             true
///         } else {
///             false
///         }
///     }
///
///     fn storage_balance_bounds(&self) -> StorageBalanceBounds {
///         self.token.storage_balance_bounds()
///     }
///
///     fn storage_balance_of(&self, account_id: AccountId) -> Option<StorageBalance> {
///         self.token.storage_balance_of(account_id)
///     }
/// }
///
/// ```
///
#[ext_contract(ext_storage_management)]
pub trait StorageManagement {
    // if `registration_only=true` MUST refund above the minimum balance if the account didn't exist and
    //     refund full deposit if the account exists.
    fn storage_deposit(
        &mut self,
        account_id: Option<AccountId>,
        registration_only: Option<bool>,
    ) -> StorageBalance;

    /// Withdraw specified amount of available Ⓝ for predecessor account.
    ///
    /// This method is safe to call. It MUST NOT remove data.
    ///
    /// `amount` is sent as a string representing an unsigned 128-bit integer. If
    /// omitted, contract MUST refund full `available` balance. If `amount` exceeds
    /// predecessor account's available balance, contract MUST panic.
    ///
    /// If predecessor account not registered, contract MUST panic.
    ///
    /// MUST require exactly 1 yoctoNEAR attached balance to prevent restricted
    /// function-call access-key call (UX wallet security)
    ///
    /// Returns the StorageBalance structure showing updated balances.
    fn storage_withdraw(&mut self, amount: Option<NearToken>) -> StorageBalance;

    /// Unregisters the predecessor account and returns the storage NEAR deposit back.
    ///
    /// If the predecessor account is not registered, the function MUST return `false` without panic.
    ///
    /// If `force=true` the function SHOULD ignore account balances (burn them) and close the account.
    /// Otherwise, MUST panic if caller has a positive registered balance (eg token holdings) or
    /// the contract doesn't support force deregistration.
    /// MUST require exactly 1 yoctoNEAR attached balance to prevent restricted function-call access-key call
    /// (UX wallet security)
    /// Returns `true` iff the account was unregistered.
    /// Returns `false` iff account was not registered before.
    fn storage_unregister(&mut self, force: Option<bool>) -> bool;

    fn storage_balance_bounds(&self) -> StorageBalanceBounds;

    fn storage_balance_of(&self, account_id: AccountId) -> Option<StorageBalance>;
}