/
NFTGallery.move
159 lines (144 loc) · 7.27 KB
/
NFTGallery.move
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
module 0x1::NFTGallery {
use Std::GUID;
use 0x1::NFT::{Self, Token};
use Std::Option::{Self, Option};
use Std::Signer;
use Std::Vector;
/// Gallery holding tokens of `TokenType` as well as information of approved operators.
struct NFTGallery<TokenType: copy + store + drop> has key {
gallery: vector<Token<TokenType>>
}
// Error codes
const EID_NOT_FOUND: u64 = 0;
const EGALLERY_NOT_PUBLISHED: u64 = 1;
const EGALLERY_ALREADY_PUBLISHED: u64 = 2;
const EINVALID_AMOUNT_OF_TRANSFER: u64 = 3;
/// Add a token to the owner's gallery.
/// The specifics of the addition depend on the token data inlining.
/// In case the token data is inlined, the addition is trivial (join / split operations are not allowed).
/// Otherwise, the addition might include joining of the two tokens.
public fun add_to_gallery<TokenType: copy + store + drop>(owner: address, token: Token<TokenType>)
acquires NFTGallery {
assert!(exists<NFTGallery<TokenType>>(owner), EGALLERY_NOT_PUBLISHED);
let gallery = &mut borrow_global_mut<NFTGallery<TokenType>>(owner).gallery;
if (!NFT::is_data_inlined<TokenType>(&token)) {
let index_opt = index_of_token<TokenType>(gallery, &NFT::id<TokenType>(&token));
if (Option::is_some(&index_opt)) {
let prev_token_idx = Option::extract(&mut index_opt);
// The gallery already has the given token: update its balance
NFT::join<TokenType>(Vector::borrow_mut(gallery, prev_token_idx), token);
return
}
};
Vector::push_back(gallery, token)
}
/// Returns whether the owner has a token with given id.
public fun has_token<TokenType: copy + store + drop>(owner: address, token_id: &GUID::ID): bool acquires NFTGallery {
Option::is_some(&index_of_token(&borrow_global<NFTGallery<TokenType>>(owner).gallery, token_id))
}
public fun get_token_balance<TokenType: copy + store + drop>(owner: address, token_id: &GUID::ID
): u64 acquires NFTGallery {
let gallery = &borrow_global<NFTGallery<TokenType>>(owner).gallery;
let index_opt = index_of_token<TokenType>(gallery, token_id);
if (Option::is_none(&index_opt)) {
0
} else {
let token = Vector::borrow(gallery, Option::extract(&mut index_opt));
NFT::get_balance(token)
}
}
/// Returns the overall supply for the given token (across this and potentially other galleries),
// aborts if token with the given ID is not found.
public fun get_token_supply<TokenType: copy + store + drop>(owner: address, token_id: &GUID::ID): u64 acquires NFTGallery {
let gallery = &borrow_global<NFTGallery<TokenType>>(owner).gallery;
let index_opt = index_of_token<TokenType>(gallery, token_id);
assert!(Option::is_some(&index_opt), EID_NOT_FOUND);
let token = Vector::borrow(gallery, Option::extract(&mut index_opt));
NFT::get_supply(token)
}
/// Returns a copy of the token content uri
public fun get_token_content_uri<TokenType: copy + store + drop>(owner: address, token_id: &GUID::ID): vector<u8> acquires NFTGallery {
let gallery = &borrow_global<NFTGallery<TokenType>>(owner).gallery;
let index_opt = index_of_token<TokenType>(gallery, token_id);
assert!(Option::is_some(&index_opt), EID_NOT_FOUND);
let token = Vector::borrow(gallery, Option::extract(&mut index_opt));
NFT::get_content_uri(token)
}
/// Returns a copy of the token metadata
public fun get_token_metadata<TokenType: copy + store + drop>(owner: address, token_id: &GUID::ID): TokenType acquires NFTGallery {
let gallery = &borrow_global<NFTGallery<TokenType>>(owner).gallery;
let index_opt = index_of_token<TokenType>(gallery, token_id);
assert!(Option::is_some(&index_opt), EID_NOT_FOUND);
let token = Vector::borrow(gallery, Option::extract(&mut index_opt));
NFT::get_metadata(token)
}
/// Returns a copy of the token parent id
public fun get_token_parent_id<TokenType: copy + store + drop>(owner: address, token_id: &GUID::ID): Option<GUID::ID> acquires NFTGallery {
let gallery = &borrow_global<NFTGallery<TokenType>>(owner).gallery;
let index_opt = index_of_token<TokenType>(gallery, token_id);
assert!(Option::is_some(&index_opt), EID_NOT_FOUND);
let token = Vector::borrow(gallery, Option::extract(&mut index_opt));
NFT::get_parent_id(token)
}
/// Transfer `amount` of token with id `GUID::id(creator, creation_num)` from `owner`'s
/// balance to `to`'s balance. This operation has to be done by either the owner or an
/// approved operator of the owner.
public(script) fun transfer_token_between_galleries<TokenType: copy + store + drop>(
account: signer,
to: address,
amount: u64,
creator: address,
creation_num: u64
) acquires NFTGallery {
transfer_token_between_galleries_impl<TokenType>(&account, to, amount, creator, creation_num)
}
/// The implementation, which doesn't consume signer, and thus can be used for testing.
public fun transfer_token_between_galleries_impl<TokenType: copy + store + drop>(
account: &signer,
to: address,
amount: u64,
creator: address,
creation_num: u64
) acquires NFTGallery {
let owner = Signer::address_of(account);
assert!(amount > 0, EINVALID_AMOUNT_OF_TRANSFER);
let gallery = &mut borrow_global_mut<NFTGallery<TokenType>>(owner).gallery;
let id = GUID::create_id(creator, creation_num);
let index_opt = index_of_token<TokenType>(gallery, &id);
assert!(Option::is_some(&index_opt), EID_NOT_FOUND);
let from_token_idx = Option::extract(&mut index_opt);
if (NFT::is_data_inlined(Vector::borrow(gallery, from_token_idx)) ||
NFT::get_balance(Vector::borrow(gallery, from_token_idx)) == amount) {
// Move the token from one gallery to another
let token = Vector::remove(gallery, from_token_idx);
add_to_gallery<TokenType>(to, token)
} else {
// Split the original token and add the splitted part to another gallery
let split_out_token = NFT::split_out(Vector::borrow_mut(gallery, from_token_idx), amount);
add_to_gallery<TokenType>(to, split_out_token)
};
// Emit transfer event
NFT::emit_transfer_event(
&id,
account,
to,
amount,
)
}
public fun publish_gallery<TokenType: copy + store + drop>(account: &signer) {
assert!(!exists<NFTGallery<TokenType>>(Signer::address_of(account)), EGALLERY_ALREADY_PUBLISHED);
move_to(account, NFTGallery<TokenType> { gallery: Vector::empty() });
}
/// Finds the index of token with the given id in the gallery.
fun index_of_token<TokenType: copy + store + drop>(gallery: &vector<Token<TokenType>>, id: &GUID::ID): Option<u64> {
let i = 0;
let len = Vector::length(gallery);
while (i < len) {
if (NFT::id<TokenType>(Vector::borrow(gallery, i)) == *id) {
return Option::some(i)
};
i = i + 1;
};
Option::none()
}
}