├── .gitignore
├── Move.toml
├── LICENSE
└── sources
├── birthday_bot.move
└── birthday_bot_test.move
/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 | storage/
3 | .idea/
4 | .aptos/
--------------------------------------------------------------------------------
/Move.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "birthday_bot"
3 | version = "1.0.0"
4 |
5 | [addresses]
6 | std = "0x1"
7 | overmind = "0x1337"
8 |
9 | [dependencies]
10 | MoveStdlib = { git = "https://github.com/aptos-labs/aptos-core.git", subdir = "aptos-move/framework/move-stdlib", rev = "093b497d1267715a222845aad4fd3ca59da90e8d" }
11 | AptosFramework = { git = "https://github.com/aptos-labs/aptos-core.git", subdir = "aptos-move/framework/aptos-framework", rev = "093b497d1267715a222845aad4fd3ca59da90e8d" }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Overmind
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/sources/birthday_bot.move:
--------------------------------------------------------------------------------
1 | module overmind::birthday_bot {
2 | use aptos_std::table::Table;
3 | use std::signer;
4 | use std::error;
5 | use aptos_framework::account;
6 | use std::vector;
7 | use aptos_framework::coin;
8 | use aptos_framework::aptos_coin::AptosCoin;
9 | use aptos_std::table;
10 | use aptos_framework::timestamp;
11 |
12 | //
13 | // Errors
14 | //
15 | const ERROR_DISTRIBUTION_STORE_EXIST: u64 = 0;
16 | const ERROR_DISTRIBUTION_STORE_DOES_NOT_EXIST: u64 = 1;
17 | const ERROR_LENGTHS_NOT_EQUAL: u64 = 2;
18 | const ERROR_BIRTHDAY_GIFT_DOES_NOT_EXIST: u64 = 3;
19 | const ERROR_BIRTHDAY_TIMESTAMP_SECONDS_HAS_NOT_PASSED: u64 = 4;
20 |
21 | //
22 | // Data structures
23 | //
24 | struct BirthdayGift has drop, store {
25 | amount: u64,
26 | birthday_timestamp_seconds: u64,
27 | }
28 |
29 | struct DistributionStore has key {
30 | birthday_gifts: Table
,
31 | signer_capability: account::SignerCapability,
32 | }
33 |
34 | //
35 | // Assert functions
36 | //
37 | public fun assert_distribution_store_exists(
38 | account_address: address,
39 | ) {
40 | // TODO: assert that `DistributionStore` exists
41 | }
42 |
43 | public fun assert_distribution_store_does_not_exist(
44 | account_address: address,
45 | ) {
46 | // TODO: assert that `DistributionStore` does not exist
47 | }
48 |
49 | public fun assert_lengths_are_equal(
50 | addresses: vector,
51 | amounts: vector,
52 | timestamps: vector
53 | ) {
54 | // TODO: assert that the lengths of `addresses`, `amounts`, and `timestamps` are all equal
55 | }
56 |
57 | public fun assert_birthday_gift_exists(
58 | distribution_address: address,
59 | address: address,
60 | ) acquires DistributionStore {
61 | // TODO: assert that `birthday_gifts` exists
62 | }
63 |
64 | public fun assert_birthday_timestamp_seconds_has_passed(
65 | distribution_address: address,
66 | address: address,
67 | ) acquires DistributionStore {
68 | // TODO: assert that the current timestamp is greater than or equal to `birthday_timestamp_seconds`
69 | }
70 |
71 | //
72 | // Entry functions
73 | //
74 | /**
75 | * Initializes birthday gift distribution contract
76 | * @param account - account signer executing the function
77 | * @param addresses - list of addresses that can claim their birthday gifts
78 | * @param amounts - list of amounts for birthday gifts
79 | * @param birthday_timestamps - list of birthday timestamps in seconds (only claimable after this timestamp has passed)
80 | **/
81 | public entry fun initialize_distribution(
82 | account: &signer,
83 | addresses: vector,
84 | amounts: vector,
85 | birthday_timestamps: vector
86 | ) {
87 | // TODO: check `DistributionStore` does not exist
88 |
89 | // TODO: check all lengths of `addresses`, `amounts`, and `birthday_timestamps` are equal
90 |
91 | // TODO: create resource account
92 |
93 | // TODO: register Aptos coin to resource account
94 |
95 | // TODO: loop through the lists and push items to birthday_gifts table
96 |
97 | // TODO: transfer the sum of all items in `amounts` from initiator to resource account
98 |
99 | // TODO: move_to resource `DistributionStore` to account signer
100 | }
101 |
102 | /**
103 | * Add birthday gift to `DistributionStore.birthday_gifts`
104 | * @param account - account signer executing the function
105 | * @param address - address that can claim the birthday gift
106 | * @param amount - amount for the birthday gift
107 | * @param birthday_timestamp_seconds - birthday timestamp in seconds (only claimable after this timestamp has passed)
108 | **/
109 | public entry fun add_birthday_gift(
110 | account: &signer,
111 | address: address,
112 | amount: u64,
113 | birthday_timestamp_seconds: u64
114 | ) acquires DistributionStore {
115 | // TODO: check that the distribution store exists
116 |
117 | // TODO: set new birthday gift to new `amount` and `birthday_timestamp_seconds` (birthday_gift already exists, sum `amounts` and override the `birthday_timestamp_seconds`
118 |
119 | // TODO: transfer the `amount` from initiator to resource account
120 | }
121 |
122 | /**
123 | * Remove birthday gift from `DistributionStore.birthday_gifts`
124 | * @param account - account signer executing the function
125 | * @param address - `birthday_gifts` address
126 | **/
127 | public entry fun remove_birthday_gift(
128 | account: &signer,
129 | address: address,
130 | ) acquires DistributionStore {
131 | // TODO: check that the distribution store exists
132 |
133 | // TODO: if `birthday_gifts` exists, remove `birthday_gift` from table and transfer `amount` from resource account to initiator
134 | }
135 |
136 | /**
137 | * Claim birthday gift from `DistributionStore.birthday_gifts`
138 | * @param account - account signer executing the function
139 | * @param distribution_address - distribution contract address
140 | **/
141 | public entry fun claim_birthday_gift(
142 | account: &signer,
143 | distribution_address: address,
144 | ) acquires DistributionStore {
145 | // TODO: check that the distribution store exists
146 |
147 | // TODO: check that the `birthday_gift` exists
148 |
149 | // TODO: check that the `birthday_timestamp_seconds` has passed
150 |
151 | // TODO: remove `birthday_gift` from table and transfer `amount` from resource account to initiator
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/sources/birthday_bot_test.move:
--------------------------------------------------------------------------------
1 | module overmind::birthday_bot_test {
2 | #[test_only]
3 | use aptos_framework::timestamp;
4 | #[test_only]
5 | use std::signer;
6 | #[test_only]
7 | use aptos_framework::account;
8 | #[test_only]
9 | use aptos_framework::coin;
10 | #[test_only]
11 | use aptos_framework::aptos_coin::AptosCoin;
12 | #[test_only]
13 | use aptos_framework::aptos_coin;
14 | #[test_only]
15 | use std::vector;
16 | #[test_only]
17 | use overmind::birthday_bot::{initialize_distribution, assert_distribution_store_exists, add_birthday_gift, remove_birthday_gift, claim_birthday_gift};
18 |
19 | //
20 | // Test functions
21 | // Searching == test_[function_name]_[success || failture]_[reason]
22 | //
23 | #[test(aptos_framework = @0x1, account = @0xCAFE, test_one = @0x12, test_two = @0x34)]
24 | fun test_initialize_distribution_success(
25 | aptos_framework: &signer,
26 | account: &signer,
27 | test_one: &signer,
28 | test_two: &signer
29 | ) {
30 | timestamp::set_time_has_started_for_testing(aptos_framework);
31 | let (burn_cap, mint_cap) = aptos_framework::aptos_coin::initialize_for_test(aptos_framework);
32 |
33 | let aptos_framework_address = signer::address_of(aptos_framework);
34 | let account_address = signer::address_of(account);
35 | let test_one_address = signer::address_of(test_one);
36 | let test_two_address = signer::address_of(test_two);
37 |
38 | account::create_account_for_test(aptos_framework_address);
39 | account::create_account_for_test(account_address);
40 |
41 | coin::register(account);
42 | aptos_coin::mint(aptos_framework, account_address, 3000000);
43 |
44 | let addresses: vector = vector::empty();
45 | let amounts: vector = vector::empty();
46 | let birthday_timestamps: vector = vector::empty();
47 |
48 | vector::push_back(&mut addresses, test_one_address);
49 | vector::push_back(&mut addresses, test_two_address);
50 |
51 | vector::push_back(&mut amounts, 1000000);
52 | vector::push_back(&mut amounts, 2000000);
53 |
54 | vector::push_back(&mut birthday_timestamps, timestamp::now_seconds());
55 | vector::push_back(&mut birthday_timestamps, timestamp::now_seconds());
56 |
57 | initialize_distribution(
58 | account,
59 | addresses,
60 | amounts,
61 | birthday_timestamps
62 | );
63 |
64 | assert_distribution_store_exists(account_address);
65 | assert!(coin::balance(account_address) == 0, 0);
66 |
67 | coin::destroy_burn_cap(burn_cap);
68 | coin::destroy_mint_cap(mint_cap);
69 | }
70 |
71 | #[test(aptos_framework = @0x1, account = @0xCAFE, test_one = @0x12, test_two = @0x34)]
72 | #[expected_failure(abort_code = 196608, location = overmind::birthday_bot)]
73 | fun test_initialize_distribution_failure_distribution_store_already_exists(
74 | aptos_framework: &signer,
75 | account: &signer,
76 | test_one: &signer,
77 | test_two: &signer
78 | ) {
79 | timestamp::set_time_has_started_for_testing(aptos_framework);
80 | let (burn_cap, mint_cap) = aptos_framework::aptos_coin::initialize_for_test(aptos_framework);
81 |
82 | let aptos_framework_address = signer::address_of(aptos_framework);
83 | let account_address = signer::address_of(account);
84 | let test_one_address = signer::address_of(test_one);
85 | let test_two_address = signer::address_of(test_two);
86 |
87 | account::create_account_for_test(aptos_framework_address);
88 | account::create_account_for_test(account_address);
89 |
90 | coin::register(account);
91 | aptos_coin::mint(aptos_framework, account_address, 3000000);
92 |
93 | let addresses: vector = vector::empty();
94 | let amounts: vector = vector::empty();
95 | let birthday_timestamps: vector = vector::empty();
96 |
97 | vector::push_back(&mut addresses, test_one_address);
98 | vector::push_back(&mut addresses, test_two_address);
99 |
100 | vector::push_back(&mut amounts, 1000000);
101 | vector::push_back(&mut amounts, 2000000);
102 |
103 | vector::push_back(&mut birthday_timestamps, timestamp::now_seconds());
104 | vector::push_back(&mut birthday_timestamps, timestamp::now_seconds());
105 |
106 | initialize_distribution(
107 | account,
108 | addresses,
109 | amounts,
110 | birthday_timestamps
111 | );
112 |
113 | initialize_distribution(
114 | account,
115 | addresses,
116 | amounts,
117 | birthday_timestamps
118 | );
119 |
120 | coin::destroy_burn_cap(burn_cap);
121 | coin::destroy_mint_cap(mint_cap);
122 | }
123 |
124 | #[test(aptos_framework = @0x1, account = @0xCAFE, test_one = @0x12, test_two = @0x34)]
125 | #[expected_failure(abort_code = 196610, location = overmind::birthday_bot)]
126 | fun test_initialize_distribution_failure_not_equal_lengths(
127 | aptos_framework: &signer,
128 | account: &signer,
129 | test_one: &signer,
130 | test_two: &signer
131 | ) {
132 | timestamp::set_time_has_started_for_testing(aptos_framework);
133 | let (burn_cap, mint_cap) = aptos_framework::aptos_coin::initialize_for_test(aptos_framework);
134 |
135 | let aptos_framework_address = signer::address_of(aptos_framework);
136 | let account_address = signer::address_of(account);
137 | let test_one_address = signer::address_of(test_one);
138 | let test_two_address = signer::address_of(test_two);
139 |
140 | account::create_account_for_test(aptos_framework_address);
141 | account::create_account_for_test(account_address);
142 |
143 | coin::register(account);
144 | aptos_coin::mint(aptos_framework, account_address, 3000000);
145 |
146 | let addresses: vector = vector::empty();
147 | let amounts: vector = vector::empty();
148 | let birthday_timestamps: vector = vector::empty();
149 |
150 | vector::push_back(&mut addresses, test_one_address);
151 | vector::push_back(&mut addresses, test_two_address);
152 |
153 | vector::push_back(&mut amounts, 1000000);
154 | vector::push_back(&mut amounts, 2000000);
155 |
156 | vector::push_back(&mut birthday_timestamps, timestamp::now_seconds());
157 | vector::push_back(&mut birthday_timestamps, timestamp::now_seconds());
158 | vector::push_back(&mut birthday_timestamps, timestamp::now_seconds());
159 |
160 | initialize_distribution(
161 | account,
162 | addresses,
163 | amounts,
164 | birthday_timestamps
165 | );
166 |
167 | coin::destroy_burn_cap(burn_cap);
168 | coin::destroy_mint_cap(mint_cap);
169 | }
170 |
171 | #[test(aptos_framework = @0x1, account = @0xCAFE, test_one = @0x12, test_two = @0x34, test_three = @0x56)]
172 | fun test_add_birthday_gift_success(
173 | aptos_framework: &signer,
174 | account: &signer,
175 | test_one: &signer,
176 | test_two: &signer,
177 | test_three: &signer
178 | ) {
179 | timestamp::set_time_has_started_for_testing(aptos_framework);
180 | let (burn_cap, mint_cap) = aptos_framework::aptos_coin::initialize_for_test(aptos_framework);
181 |
182 | let aptos_framework_address = signer::address_of(aptos_framework);
183 | let account_address = signer::address_of(account);
184 | let test_one_address = signer::address_of(test_one);
185 | let test_two_address = signer::address_of(test_two);
186 | let test_three_address = signer::address_of(test_three);
187 |
188 | account::create_account_for_test(aptos_framework_address);
189 | account::create_account_for_test(account_address);
190 |
191 | coin::register(account);
192 | aptos_coin::mint(aptos_framework, account_address, 4000000);
193 |
194 | let addresses: vector = vector::empty();
195 | let amounts: vector = vector::empty();
196 | let birthday_timestamps: vector = vector::empty();
197 |
198 | vector::push_back(&mut addresses, test_one_address);
199 | vector::push_back(&mut addresses, test_two_address);
200 |
201 | vector::push_back(&mut amounts, 1000000);
202 | vector::push_back(&mut amounts, 2000000);
203 |
204 | vector::push_back(&mut birthday_timestamps, timestamp::now_seconds());
205 | vector::push_back(&mut birthday_timestamps, timestamp::now_seconds());
206 |
207 | initialize_distribution(
208 | account,
209 | addresses,
210 | amounts,
211 | birthday_timestamps
212 | );
213 |
214 | add_birthday_gift(account, test_three_address, 1000000, timestamp::now_seconds());
215 |
216 | assert!(coin::balance(account_address) == 0, 0);
217 |
218 | coin::destroy_burn_cap(burn_cap);
219 | coin::destroy_mint_cap(mint_cap);
220 | }
221 |
222 | #[test(aptos_framework = @0x1, account = @0xCAFE, test_one = @0x12, test_two = @0x34)]
223 | fun test_remove_birthday_gift_success(
224 | aptos_framework: &signer,
225 | account: &signer,
226 | test_one: &signer,
227 | test_two: &signer,
228 | ) {
229 | timestamp::set_time_has_started_for_testing(aptos_framework);
230 | let (burn_cap, mint_cap) = aptos_framework::aptos_coin::initialize_for_test(aptos_framework);
231 |
232 | let aptos_framework_address = signer::address_of(aptos_framework);
233 | let account_address = signer::address_of(account);
234 | let test_one_address = signer::address_of(test_one);
235 | let test_two_address = signer::address_of(test_two);
236 |
237 | account::create_account_for_test(aptos_framework_address);
238 | account::create_account_for_test(account_address);
239 |
240 | coin::register(account);
241 | aptos_coin::mint(aptos_framework, account_address, 3000000);
242 |
243 | let addresses: vector = vector::empty();
244 | let amounts: vector = vector::empty();
245 | let birthday_timestamps: vector = vector::empty();
246 |
247 | vector::push_back(&mut addresses, test_one_address);
248 | vector::push_back(&mut addresses, test_two_address);
249 |
250 | vector::push_back(&mut amounts, 1000000);
251 | vector::push_back(&mut amounts, 2000000);
252 |
253 | vector::push_back(&mut birthday_timestamps, timestamp::now_seconds());
254 | vector::push_back(&mut birthday_timestamps, timestamp::now_seconds());
255 |
256 | initialize_distribution(
257 | account,
258 | addresses,
259 | amounts,
260 | birthday_timestamps
261 | );
262 |
263 | remove_birthday_gift(account, test_two_address);
264 |
265 | assert!(coin::balance(account_address) == 2000000, 0);
266 |
267 | coin::destroy_burn_cap(burn_cap);
268 | coin::destroy_mint_cap(mint_cap);
269 | }
270 |
271 | #[test(aptos_framework = @0x1, account = @0xCAFE, test_one = @0x12, test_two = @0x34)]
272 | fun test_claim_birthday_gift_success(
273 | aptos_framework: &signer,
274 | account: &signer,
275 | test_one: &signer,
276 | test_two: &signer,
277 | ) {
278 | timestamp::set_time_has_started_for_testing(aptos_framework);
279 | let (burn_cap, mint_cap) = aptos_framework::aptos_coin::initialize_for_test(aptos_framework);
280 |
281 | let aptos_framework_address = signer::address_of(aptos_framework);
282 | let account_address = signer::address_of(account);
283 | let test_one_address = signer::address_of(test_one);
284 | let test_two_address = signer::address_of(test_two);
285 |
286 | account::create_account_for_test(aptos_framework_address);
287 | account::create_account_for_test(account_address);
288 | account::create_account_for_test(test_one_address);
289 |
290 | coin::register(account);
291 | coin::register(test_one);
292 | aptos_coin::mint(aptos_framework, account_address, 3000000);
293 |
294 |
295 | let addresses: vector = vector::empty();
296 | let amounts: vector = vector::empty();
297 | let birthday_timestamps: vector = vector::empty();
298 |
299 | vector::push_back(&mut addresses, test_one_address);
300 | vector::push_back(&mut addresses, test_two_address);
301 |
302 | vector::push_back(&mut amounts, 1000000);
303 | vector::push_back(&mut amounts, 2000000);
304 |
305 | vector::push_back(&mut birthday_timestamps, timestamp::now_seconds());
306 | vector::push_back(&mut birthday_timestamps, timestamp::now_seconds());
307 |
308 | initialize_distribution(
309 | account,
310 | addresses,
311 | amounts,
312 | birthday_timestamps
313 | );
314 |
315 | timestamp::fast_forward_seconds(1);
316 |
317 | claim_birthday_gift(test_one, account_address);
318 |
319 | assert!(coin::balance(test_one_address) == 1000000, 0);
320 |
321 | coin::destroy_burn_cap(burn_cap);
322 | coin::destroy_mint_cap(mint_cap);
323 | }
324 | }
--------------------------------------------------------------------------------