-
Notifications
You must be signed in to change notification settings - Fork 94
/
internal.rs
160 lines (139 loc) · 6.08 KB
/
internal.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use crate::*;
use near_sdk::{CryptoHash};
use std::mem::size_of;
//used to generate a unique prefix in our storage collections (this is to avoid data collisions)
pub(crate) fn hash_account_id(account_id: &AccountId) -> CryptoHash {
//get the default hash
let mut hash = CryptoHash::default();
//we hash the account ID and return it
hash.copy_from_slice(&env::sha256(account_id.as_bytes()));
hash
}
//used to make sure the user attached exactly 1 yoctoNEAR
pub(crate) fn assert_one_yocto() {
assert_eq!(
env::attached_deposit(),
NearToken::from_yoctonear(1),
"Requires attached deposit of exactly 1 yoctoNEAR",
)
}
//refund the initial deposit based on the amount of storage that was used up
pub(crate) fn refund_deposit(storage_used: u128) {
//get how much it would cost to store the information
let required_cost = env::storage_byte_cost().saturating_mul(storage_used);
//get the attached deposit
let attached_deposit = env::attached_deposit();
//make sure that the attached deposit is greater than or equal to the required cost
assert!(
required_cost <= attached_deposit,
"Must attach {} yoctoNEAR to cover storage",
required_cost,
);
//get the refund amount from the attached deposit - required cost
let refund = attached_deposit.saturating_sub(required_cost);
//if the refund is greater than 1 yocto NEAR, we refund the predecessor that amount
if refund.gt(&ONE_YOCTONEAR) {
Promise::new(env::predecessor_account_id()).transfer(refund);
}
}
impl Contract {
//add a token to the set of tokens an owner has
pub(crate) fn internal_add_token_to_owner(
&mut self,
account_id: &AccountId,
token_id: &TokenId,
) {
//get the set of tokens for the given account
let mut tokens_set = self.tokens_per_owner.get(account_id).unwrap_or_else(|| {
//if the account doesn't have any tokens, we create a new unordered set
UnorderedSet::new(
StorageKey::TokenPerOwnerInner {
//we get a new unique prefix for the collection
account_id_hash: hash_account_id(&account_id),
},
)
});
//we insert the token ID into the set
tokens_set.insert(token_id);
//we insert that set for the given account ID.
self.tokens_per_owner.insert(account_id, &tokens_set);
}
//remove a token from an owner (internal method and can't be called directly via CLI).
pub(crate) fn internal_remove_token_from_owner(
&mut self,
account_id: &AccountId,
token_id: &TokenId,
) {
//we get the set of tokens that the owner has
let mut tokens_set = self
.tokens_per_owner
.get(account_id)
//if there is no set of tokens for the owner, we panic with the following message:
.expect("Token should be owned by the sender");
//we remove the the token_id from the set of tokens
tokens_set.remove(token_id);
//if the token set is now empty, we remove the owner from the tokens_per_owner collection
if tokens_set.is_empty() {
self.tokens_per_owner.remove(account_id);
} else {
//if the token set is not empty, we simply insert it back for the account ID.
self.tokens_per_owner.insert(account_id, &tokens_set);
}
}
//transfers the NFT to the receiver_id (internal method and can't be called directly via CLI).
pub(crate) fn internal_transfer(
&mut self,
sender_id: &AccountId,
receiver_id: &AccountId,
token_id: &TokenId,
memo: Option<String>,
) -> Token {
//get the token object by passing in the token_id
let token = self.tokens_by_id.get(token_id).expect("No token");
//we make sure that the sender isn't sending the token to themselves
assert_ne!(
&token.owner_id, receiver_id,
"The token owner and the receiver should be different"
);
//we remove the token from it's current owner's set
self.internal_remove_token_from_owner(&token.owner_id, token_id);
//we then add the token to the receiver_id's set
self.internal_add_token_to_owner(receiver_id, token_id);
//we create a new token struct
let new_token = Token {
owner_id: receiver_id.clone(),
};
//insert that new token into the tokens_by_id, replacing the old entry
self.tokens_by_id.insert(token_id, &new_token);
//if there was some memo attached, we log it.
if let Some(memo) = memo.as_ref() {
env::log_str(&format!("Memo: {}", memo).to_string());
}
// Default the authorized ID to be None for the logs. // We will return here in the future when we study the approval functionality
let mut authorized_id = None;
// Construct the transfer log as per the events standard.
let nft_transfer_log: EventLog = EventLog {
// Standard name ("nep171").
standard: NFT_STANDARD_NAME.to_string(),
// Version of the standard ("nft-1.0.0").
version: NFT_METADATA_SPEC.to_string(),
// The data related with the event stored in a vector.
event: EventLogVariant::NftTransfer(vec![NftTransferLog {
// The optional authorized account ID to transfer the token on behalf of the old owner.
authorized_id,
// The old owner's account ID.
old_owner_id: token.owner_id.to_string(),
// The account ID of the new owner of the token.
new_owner_id: receiver_id.to_string(),
// A vector containing the token IDs as strings.
token_ids: vec![token_id.to_string()],
// An optional memo to include.
memo,
}]),
};
// Log the serialized json.
env::log_str(&nft_transfer_log.to_string());
//return the previous token object that was transferred.
token
}
}