-
Notifications
You must be signed in to change notification settings - Fork 251
/
lib.rs
146 lines (125 loc) · 4.37 KB
/
lib.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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
use near_sdk::store::UnorderedMap;
use near_sdk::{env, log, near, AccountId, NearToken};
/// An example of a versioned contract. This is a simple contract that tracks how much
/// each account deposits into the contract. In v1, a nonce is added to state which increments
/// after each successful deposit.
#[near(contract_state)]
pub enum VersionedContract {
V0(ContractV0),
V1(Contract),
}
impl VersionedContract {
fn contract_mut(&mut self) -> &mut Contract {
let old_contract = match self {
Self::V1(contract) => return contract,
Self::V0(contract) => {
// Contract state is old version, take old state to upgrade.
core::mem::take(contract)
}
};
// Upgrade state of self and return mutable reference to it.
*self = Self::V1(Contract { funders: old_contract.funders, nonce: 0 });
if let Self::V1(contract) = self {
contract
} else {
// Variant is constructed above, this is unreachable
env::abort()
}
}
fn funders(&self) -> &UnorderedMap<AccountId, NearToken> {
match self {
Self::V0(contract) => &contract.funders,
Self::V1(contract) => &contract.funders,
}
}
}
impl Default for VersionedContract {
fn default() -> Self {
VersionedContract::V1(Contract::default())
}
}
#[near]
pub struct ContractV0 {
funders: UnorderedMap<AccountId, NearToken>,
}
impl Default for ContractV0 {
fn default() -> Self {
Self { funders: UnorderedMap::new(b"f") }
}
}
#[near]
pub struct Contract {
funders: UnorderedMap<AccountId, NearToken>,
nonce: u64,
}
impl Default for Contract {
fn default() -> Self {
Self { funders: UnorderedMap::new(b"f"), nonce: 0 }
}
}
#[near]
impl VersionedContract {
#[payable]
pub fn deposit(&mut self) {
let account_id = env::predecessor_account_id();
let deposit = env::attached_deposit();
log!("{} deposited {} yNEAR", account_id, deposit);
let contract = self.contract_mut();
let res = contract.funders.entry(account_id.clone()).or_default();
let res = res.saturating_add(deposit);
contract.funders.insert(account_id, res);
contract.nonce += 1;
}
pub fn get_nonce(&self) -> u64 {
match self {
Self::V0(_) => 0,
Self::V1(contract) => contract.nonce,
}
}
pub fn get_deposit(&self, account_id: &AccountId) -> Option<&NearToken> {
self.funders().get(account_id)
}
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(test)]
mod tests {
use super::*;
use near_sdk::test_utils::test_env::{alice, bob};
use near_sdk::test_utils::VMContextBuilder;
use near_sdk::testing_env;
fn set_predecessor_and_deposit(predecessor: AccountId, deposit: NearToken) {
testing_env!(VMContextBuilder::new()
.predecessor_account_id(predecessor)
.attached_deposit(deposit)
.build())
}
#[test]
fn basic() {
let mut contract = VersionedContract::default();
set_predecessor_and_deposit(bob(), NearToken::from_yoctonear(8));
contract.deposit();
set_predecessor_and_deposit(alice(), NearToken::from_yoctonear(10));
contract.deposit();
set_predecessor_and_deposit(bob(), NearToken::from_yoctonear(20));
contract.deposit();
assert_eq!(contract.get_deposit(&alice()), Some(&NearToken::from_yoctonear(10)));
assert_eq!(contract.get_deposit(&bob()), Some(&NearToken::from_yoctonear(28)));
assert_eq!(contract.get_nonce(), 3);
}
#[test]
fn contract_v0_interactions() {
let mut contract = {
let mut funders = UnorderedMap::new(b"f");
funders.insert(bob(), NearToken::from_yoctonear(8));
VersionedContract::V0(ContractV0 { funders })
};
assert_eq!(contract.get_nonce(), 0);
assert!(matches!(contract, VersionedContract::V0(_)));
set_predecessor_and_deposit(alice(), NearToken::from_yoctonear(1000));
contract.deposit();
assert!(matches!(contract, VersionedContract::V1(_)));
assert_eq!(contract.get_nonce(), 1);
assert_eq!(contract.get_deposit(&alice()), Some(&NearToken::from_yoctonear(1000)));
assert_eq!(contract.get_deposit(&bob()), Some(&NearToken::from_yoctonear(8)));
}
}