@@ -16,50 +16,82 @@ blueprint! {
16
16
//At the stage of finalizing the airdrop methode finalize_airdrop the Tweeter_oracle component is used to verify that all tasks have been carried out by subscribers.
17
17
//
18
18
struct AirdropWithTweeterOracle {
19
+ // ResourceAddress of admin_badge : this admin_badge is return to user who instanciate the component (Airdrop maker)
19
20
admin_badge: ResourceAddress ,
21
+ // The vault that contains the tokens to distribute to the participants of the airdrop who have executed all the tasks
20
22
tokens: Vault ,
23
+ // Badge resource address returned to people registered in the airdrop : This will allow them to make the withdrawal
21
24
participant_badge_address: ResourceAddress ,
25
+ // minter of participant_badge vault
22
26
minter_badge_vault: Vault ,
27
+ //Store the airdrop particpant : Tweeter Account by NonFungibleId : [{"12345678901234567890123456789012u128","cyover"},{"12345678901234567890123456788080u128","cyrolsi"}]
23
28
airdrop_participants : HashMap <NonFungibleId , String >,
29
+ //Store participants tweeter_account to avoid multiple participation with the same tweeter account
24
30
participants_tweeter_account : HashSet <String >,
31
+ // Store tweeters accounts to follow
25
32
accounts_to_follow : Vec <String >,
33
+ // Store tweets to retweet
26
34
tweets_to_retweet : Vec <String > ,
35
+ // Store tweets to like
27
36
tweets_to_like : Vec <String >,
37
+ // The oracle tweeter component which makes it possible to verify that all the tasks have been correctly executed
28
38
tweeter_oracle : TweeterOracle ,
39
+ // Store the NonFongibleId of participants who have completed all the tasks and who will receive the airdrop
29
40
recipients : HashSet <NonFungibleId >,
41
+ // Amount per recipient
30
42
amount_per_recipient : Decimal
31
43
}
32
44
33
45
impl AirdropWithTweeterOracle {
34
- pub fn new( token_type: ResourceAddress ,
46
+ // This function instanciate the AirdropWithTweeterOracle
47
+ // #Argumets
48
+ // * `token_type` Tokens resourceAddress to distribute
49
+ // * `accounts_to_follow` tweeters accounts to follow
50
+ // * `tweets_to_retweet` tweets to retweet
51
+ // * `tweets_to_like` tweets to like
52
+ // * `tweeter_oracle_component_address` Address of TweeterOracle component
53
+ pub fn new( token_type: ResourceAddress ,
35
54
accounts_to_follow : Vec <String >,
36
55
tweets_to_retweet : Vec <String > ,
37
56
tweets_to_like : Vec <String >,
38
57
tweeter_oracle_component_address : ComponentAddress ) -> ( ComponentAddress , Bucket ) {
39
58
59
+ // Check that there is at least one task to do
40
60
assert!( accounts_to_follow. len( ) > 0
41
61
|| tweets_to_retweet. len( ) > 0
42
- || tweets_to_like. len( ) > 0 , "you must give at leat 1 condition for the airdrop" ) ;
62
+ || tweets_to_like. len( ) > 0 , "you must give at leat 1 task for the airdrop" ) ;
43
63
64
+ // create admin_badge bucket with one supply
44
65
let admin_badge = ResourceBuilder :: new_fungible( )
45
66
. divisibility( DIVISIBILITY_NONE )
46
67
. initial_supply( Decimal :: one( ) ) ;
47
68
69
+ // create a minter badge
48
70
let minter_badge = ResourceBuilder :: new_fungible( )
49
71
. divisibility( DIVISIBILITY_NONE )
50
72
. metadata( "name" , "minter badge" )
51
73
. initial_supply( Decimal :: one( ) ) ;
52
74
75
+ // Create a participant badge address
53
76
let participant_badge_address = ResourceBuilder :: new_non_fungible( )
54
77
. metadata( "name" , "participant badge" )
55
78
. mintable( rule!( require( minter_badge. resource_address( ) ) ) , LOCKED )
56
79
. updateable_non_fungible_data( rule!( require( minter_badge. resource_address( ) ) ) , LOCKED )
57
80
. no_initial_supply( ) ;
58
81
82
+ // //Definition of the methods which will be accessible only to the administrator of the component
59
83
let access_rules = AccessRules :: new( )
60
84
. method( "finalize_airdrop" , rule!( require( admin_badge. resource_address( ) ) ) )
85
+ . method( "find_and_store_airdrop_recipients" , rule!( require( admin_badge. resource_address( ) ) ) )
61
86
. default ( rule!( allow_all) ) ;
62
87
88
+ let tweeter_oracle : TweeterOracle = tweeter_oracle_component_address. into( ) ;
89
+ tweeter_oracle. add_followers_to_update( accounts_to_follow. clone( ) ) ;
90
+ tweeter_oracle. add_likers_to_update( tweets_to_like. clone( ) ) ;
91
+ tweeter_oracle. add_retweeters_to_update( tweets_to_retweet. clone( ) ) ;
92
+
93
+
94
+ // Instantiate AirdropWithTweeterOracle component and return it with the admin badge to caller
63
95
let component = Self {
64
96
admin_badge: admin_badge. resource_address( ) ,
65
97
tokens: Vault :: new( token_type) ,
@@ -70,7 +102,7 @@ blueprint! {
70
102
accounts_to_follow : accounts_to_follow,
71
103
tweets_to_retweet : tweets_to_retweet,
72
104
tweets_to_like : tweets_to_like,
73
- tweeter_oracle : tweeter_oracle_component_address . into ( ) ,
105
+ tweeter_oracle : tweeter_oracle ,
74
106
recipients : HashSet :: new( ) ,
75
107
amount_per_recipient : Decimal :: zero( )
76
108
}
@@ -81,14 +113,28 @@ blueprint! {
81
113
return ( component, admin_badge) ;
82
114
}
83
115
84
- pub fn register( & mut self , tweeter_account_name: String , participant: ComponentAddress ) {
116
+ //This method allows you to register for an airdrop
117
+ // #Arguments
118
+ // `tweeter_account_name` : tweeter account name
119
+ pub fn register( & mut self , tweeter_account_name: String ) -> Bucket {
85
120
121
+ //Avoid multiple participation with the same tweeter account
86
122
assert!( !self . participants_tweeter_account. contains( & tweeter_account_name) , "already registered to this airdrop" ) ;
123
+
124
+ // Check if the airdrop were already finalize
125
+ assert!(
126
+ self . amount_per_recipient == Decimal :: zero( ) ,
127
+ "The airdrop were already finalize"
128
+ ) ;
129
+ // Generate NonFungibleId for participant
87
130
let id = NonFungibleId :: random( ) ;
88
131
132
+ // Store tweeter account name by NonFungibleId
89
133
self . airdrop_participants. insert( id. clone( ) , tweeter_account_name. to_string( ) ) ;
134
+ // Store tweeter account name
90
135
self . participants_tweeter_account. insert( tweeter_account_name) ;
91
136
137
+ // create participant badge that will allow him to make the withdrawal
92
138
let participant_badge = self . minter_badge_vault. authorize( || {
93
139
borrow_resource_manager!( self . participant_badge_address) . mint_non_fungible(
94
140
& id,
@@ -98,71 +144,115 @@ blueprint! {
98
144
} ,
99
145
)
100
146
} ) ;
147
+
148
+ // return the participant_badge to caller
149
+ return participant_badge;
150
+ }
101
151
102
- borrow_component!( participant) . call:: <( ) >( "deposit" , args![ participant_badge] ) ;
152
+ //This find the participants that have completed the tasks and to store them
153
+ pub fn find_and_store_airdrop_recipients( & mut self ) -> usize
154
+ {
155
+ //find partcipants who made all tasks
156
+ for nft_id in self . airdrop_participants. keys( ) {
157
+ // check if current participant have executed all tasks
158
+ let tweeter_account = self . airdrop_participants. get( & nft_id) . unwrap( ) . clone( ) ;
159
+ if !self . recipients. contains( & nft_id) && self . has_completed_all_tasks( tweeter_account) {
160
+ // store the recipient Nft_id for widhraw
161
+ self . recipients. insert( nft_id. clone( ) ) ;
162
+ }
163
+ }
164
+ // return the number of recipients
165
+ return self . recipients. len( ) ;
103
166
}
104
167
168
+ // this method makes it possible to finalize the airdrop
169
+ // #Arguments
170
+ // * `tokens` Bucket containing the tokens to distribute
105
171
pub fn finalize_airdrop( & mut self ,
106
- mut tokens: Bucket ) {
172
+ mut tokens: Bucket ) -> Bucket {
107
173
174
+ // check tokens quantity
108
175
assert!(
109
176
tokens. amount( ) > Decimal :: zero( ) ,
110
177
"tokens quantity cannot be 0"
111
178
) ;
112
-
179
+
180
+ // check token address
113
181
assert_eq!(
114
182
tokens. resource_address( ) ,
115
183
self . tokens. resource_address( ) ,
116
184
"token address must match"
117
185
) ;
118
186
187
+ // check recipients
188
+ assert!(
189
+ self . recipients. len( ) > 0 ,
190
+ "there is no recipient for the airdrop"
191
+ ) ;
192
+
193
+ // Check if the airdrop were already finalize
119
194
assert!(
120
195
self . amount_per_recipient == Decimal :: zero( ) ,
121
- "The airdrop is already finalize"
196
+ "The airdrop were already finalize"
122
197
) ;
123
198
124
- //find partcipants who made all taks
125
- for nft_id in self . airdrop_participants. keys( ) {
126
- // check eligibility
127
- let tweeter_account = self . airdrop_participants. get( & nft_id) . unwrap( ) . clone( ) ;
128
- if self . can_receive_airdrop( tweeter_account) {
129
- self . recipients. insert( nft_id. clone( ) ) ;
130
- }
131
- }
132
-
199
+ // check tokens quantity for NonFungible
133
200
assert!(
134
- self . recipients. len( ) > 0 ,
135
- "there is no recipient for the airdrop"
136
- ) ;
201
+ borrow_resource_manager!( tokens. resource_address( ) ) . resource_type( )
202
+ == ResourceType :: NonFungible && tokens. amount( ) >= Decimal :: from( self . recipients. len( ) as i128 ) ,
203
+ "For non-fungible tokens, a number at least equal to the number of recipients is required"
204
+ ) ;
137
205
138
206
// Calculate the amount of tokens each recipient can receive
139
- let amount_per_recipient = tokens. amount( ) / Decimal :: from( self . recipients. len( ) as i128 ) ;
207
+ let mut amount_per_recipient = tokens. amount( ) / Decimal :: from( self . recipients. len( ) as i128 ) ;
208
+
209
+ // Special case for NonFongible Token
210
+ if borrow_resource_manager!( tokens. resource_address( ) ) . resource_type( ) == ResourceType :: NonFungible
211
+ {
212
+ amount_per_recipient = Decimal :: from( amount_per_recipient. round( 18 , RoundingMode :: TowardsZero ) ) ;
213
+ }
214
+
140
215
self . amount_per_recipient = amount_per_recipient;
141
216
142
- self . tokens. put( tokens) ;
217
+ // Take necessary amount from tokens bucket
218
+ self . tokens. put( tokens. take( amount_per_recipient * Decimal :: from( self . recipients. len( ) as i128 ) ) ) ;
219
+
220
+ // return tokens bucket to caller
221
+ return tokens;
143
222
}
144
223
224
+ //This method allows recipients to withdraw their tokens
225
+ //#Arguments
226
+ //* `auth` Aidrop registration proof
227
+ //#Return
228
+ // This function return a bucket containing the quantity of tokens to be distributed
145
229
pub fn withdraw( & mut self , auth: Proof ) -> Bucket {
146
230
231
+ //checking if airdrop is filnalize
147
232
assert!( self . amount_per_recipient > Decimal :: zero( ) , "impossible withdraw : the airdrop is in progress" ) ;
233
+ // checking participant badge
148
234
assert_eq!( auth. resource_address( ) , self . participant_badge_address, "Invalid Badge Provided" ) ;
235
+ // checking badge amount
149
236
assert_eq!( auth. amount( ) , dec!( "1" ) , "Invalid Badge Provided" ) ;
150
237
let nft_id = auth. non_fungible:: <AirdropWithTweeterOracleData >( ) . id( ) ;
238
+ // checking if current user completed all tasks
151
239
assert!( self . recipients. contains( & nft_id) , "you cannot receive the airdrop" ) ;
152
-
153
240
let mut nft_data = auth. non_fungible:: <AirdropWithTweeterOracleData >( ) . data( ) ;
241
+ // checking if withdrawal is already done
154
242
assert!( !nft_data. is_collected, "withdraw already done" ) ;
155
243
nft_data. is_collected = true ;
156
244
let amount = self . amount_per_recipient;
245
+ // update nft data
157
246
self . minter_badge_vault. authorize( { || {
158
247
auth. non_fungible( ) . update_data( nft_data) ;
159
248
}
160
249
} ) ;
161
250
info!( "withdraw_token : {}" , amount) ;
251
+ // return tokens to caller
162
252
return self . tokens. take( amount) ;
163
253
}
164
254
165
- fn can_receive_airdrop ( & self , participant_tweeter_account : String ) -> bool {
255
+ fn has_completed_all_tasks ( & self , participant_tweeter_account : String ) -> bool {
166
256
167
257
let is_follower = self . accounts_to_follow. len( ) == 0 || self . accounts_to_follow. clone( ) . into_iter( )
168
258
. all( |x| self . tweeter_oracle. is_account_follower( x, participant_tweeter_account. to_string( ) ) ) ;
0 commit comments