-
Notifications
You must be signed in to change notification settings - Fork 27
/
DAOSpace.move
2601 lines (2247 loc) · 105 KB
/
DAOSpace.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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
module StarcoinFramework::DAOSpace {
use StarcoinFramework::DAOAccount::{Self, DAOAccountCap};
use StarcoinFramework::Account;
use StarcoinFramework::Vector;
use StarcoinFramework::NFT::{Self, NFT};
use StarcoinFramework::NFTGallery;
use StarcoinFramework::IdentifierNFT;
use StarcoinFramework::Signer;
use StarcoinFramework::DAORegistry;
use StarcoinFramework::Token::{Self, Token};
use StarcoinFramework::Errors;
use StarcoinFramework::Option::{Self, Option};
use StarcoinFramework::STC::{Self, STC};
use StarcoinFramework::Timestamp;
use StarcoinFramework::Config;
use StarcoinFramework::Event;
use StarcoinFramework::Math;
use StarcoinFramework::StarcoinVerifier;
use StarcoinFramework::StarcoinVerifier::StateProof;
use StarcoinFramework::BCS;
use StarcoinFramework::SBTVoteStrategy;
use StarcoinFramework::SnapshotUtil;
use StarcoinFramework::Block;
use StarcoinFramework::DAOPluginMarketplace;
use StarcoinFramework::EventUtil;
use StarcoinFramework::ASCII;
use StarcoinFramework::Offer;
use StarcoinFramework::TypeInfo;
friend StarcoinFramework::StarcoinDAO;
const MAX_PROPOSALS: u64 = 1000;
const ERR_NO_GRANTED: u64 = 100;
const ERR_REPEAT_ELEMENT: u64 = 101;
const ERR_STORAGE_ERROR: u64 = 102;
const ERR_NFT_ERROR: u64 = 103;
const ERR_ALREADY_INIT: u64 = 104;
const ERR_TOKEN_ERROR: u64 = 105;
const ERR_DAO_EXT: u64 = 106;
/// member
const ERR_EXPECT_MEMBER: u64 = 200;
const ERR_EXPECT_NOT_MEMBER: u64 = 201;
/// config or arguments
const ERR_ACTION_DELAY_TOO_SMALL: u64 = 300;
const ERR_CONFIG_PARAM_INVALID: u64 = 301;
const ERR_INVALID_AMOUNT:u64 = 302;
const ERR_TOO_SMALL_TOTAL:u64 = 303;
const ERR_HAVE_SAME_GRANT:u64 = 304;
const ERR_NOT_HAVE_GRANT:u64 = 305;
/// proposal
const ERR_PROPOSAL_STATE_INVALID: u64 = 400;
const ERR_PROPOSAL_ID_MISMATCH: u64 = 401;
const ERR_PROPOSER_MISMATCH: u64 = 402;
const ERR_PROPOSAL_NOT_EXIST: u64 = 403;
const ERR_QUORUM_RATE_INVALID: u64 = 404;
const ERR_PROPOSAL_ACTION_INDEX_NOT_EXIST: u64 = 405;
const ERR_PROPOSAL_OUT_OF_LIMIT: u64 = 406;
/// action
const ERR_ACTION_MUST_EXIST: u64 = 500;
const ERR_ACTION_INDEX_INVALID: u64 = 501;
const ERR_PROPOSAL_ACTIONS_NOT_EXIST: u64 = 502;
/// vote
const ERR_VOTE_STATE_MISMATCH: u64 = 600;
const ERR_VOTED_OTHERS_ALREADY: u64 = 601;
const ERR_VOTED_ALREADY: u64 = 602;
const ERR_VOTE_PARAM_INVALID: u64 = 603;
const ERR_SNAPSHOT_PROOF_PARAM_INVALID: u64 = 604;
const ERR_STATE_PROOF_VERIFY_INVALID: u64 = 605;
/// plugin
const ERR_PLUGIN_NOT_EXIST: u64 = 700;
const ERR_PLUGIN_VERSION_NOT_EXIST: u64 = 701;
const ERR_PLUGIN_HAS_INSTALLED: u64 = 702;
const ERR_PLUGIN_NOT_INSTALLED: u64 = 703;
/// grant
const ERR_OFFER_NOT_EXIST: u64 = 800;
/// DAO resource, every DAO has this resource at it's DAO account
struct DAO has key {
id: u64,
name: vector<u8>,
// description ipfs://xxxxx
description:vector<u8>,
dao_address: address,
next_member_id: u64,
next_proposal_id: u64,
}
/// A custom extension field of the DAO resources
struct DAOExt<DAOT: store> has key {
ext: DAOT,
}
/// Configuration of the DAO.
struct DAOConfig has copy, drop, store {
/// after proposal created, how long use should wait before he can vote (in milliseconds)
voting_delay: u64,
/// how long the voting window is (in milliseconds).
voting_period: u64,
/// the quorum rate to agree on the proposal.
/// if 50% votes needed, then the voting_quorum_rate should be 50.
/// it should between (0, 100].
voting_quorum_rate: u8,
/// how long the proposal should wait before it can be executed (in milliseconds).
min_action_delay: u64,
/// how many STC should be deposited to create a proposal.
min_proposal_deposit: u128,
}
struct DAOCustomConfig<ConfigT> has copy, drop, store {
config: ConfigT
}
struct DAOAccountCapHolder has key {
cap: DAOAccountCap,
}
/// Capability for minting SBT.
struct DAOSBTMintCapHolder<phantom DAOT> has key {
cap: Token::MintCapability<DAOT>,
}
/// Capability for burning SBT.
struct DAOSBTBurnCapHolder<phantom DAOT> has key {
cap: Token::BurnCapability<DAOT>,
}
/// Capability for minting any tokens.
struct DAOTokenMintCapHolder<phantom DAOT, phantom TokenT> has key {
cap: Token::MintCapability<TokenT>,
}
/// Capability for burning any tokens.
struct DAOTokenBurnCapHolder<phantom DAOT, phantom TokenT> has key {
cap: Token::BurnCapability<TokenT>,
}
struct DAONFTMintCapHolder<phantom DAOT> has key {
cap: NFT::MintCapability<DAOMember<DAOT>>,
}
struct DAONFTBurnCapHolder<phantom DAOT> has key {
cap: NFT::BurnCapability<DAOMember<DAOT>>,
}
struct DAONFTUpdateCapHolder<phantom DAOT> has key {
cap: NFT::UpdateCapability<DAOMember<DAOT>>,
}
struct DAOConfigModifyCapHolder has key {
cap: Config::ModifyConfigCapability<DAOConfig>
}
struct DAOCustomConfigModifyCapHolder<phantom DAOT, ConfigT: copy + drop + store> has key {
cap: Config::ModifyConfigCapability<ConfigT>
}
/// A type describing a capability.
struct CapType has copy, drop, store { code: u8 }
/// Create a install plugin capability type.
public fun install_plugin_cap_type(): CapType { CapType{ code: 0 } }
/// Create a upgrade module capability type.
public fun upgrade_module_cap_type(): CapType { CapType{ code: 1 } }
/// Create a modify dao config capability type.
public fun modify_config_cap_type(): CapType { CapType{ code: 2 } }
/// Create a withdraw Token capability type.
public fun withdraw_token_cap_type(): CapType { CapType{ code: 3 } }
/// Create a withdraw NFT capability type.
public fun withdraw_nft_cap_type(): CapType { CapType{ code: 4 } }
/// Create a write data to DAO account capability type.
public fun storage_cap_type(): CapType { CapType{ code: 5 } }
/// Create a member capability type.
/// This cap can issue DAO member NFT or update member's SBT
public fun member_cap_type(): CapType { CapType{ code: 6 } }
/// Create a vote capability type.
public fun proposal_cap_type(): CapType { CapType{ code: 7 } }
/// Create a grant capability type.
public fun grant_cap_type(): CapType { CapType{ code: 8 } }
/// Create a token minting capability type.
public fun token_mint_cap_type(): CapType { CapType{code: 9 } }
/// Create a token burning capability type.
public fun token_burn_cap_type(): CapType { CapType{code: 10 } }
/// Creates a grant capability type.
public fun plugin_event_cap_type(): CapType { CapType{ code: 11 } }
/// Create all capability types.
public fun all_caps(): vector<CapType> {
let caps = Vector::singleton(install_plugin_cap_type());
Vector::push_back(&mut caps, upgrade_module_cap_type());
Vector::push_back(&mut caps, modify_config_cap_type());
Vector::push_back(&mut caps, withdraw_token_cap_type());
Vector::push_back(&mut caps, withdraw_nft_cap_type());
Vector::push_back(&mut caps, storage_cap_type());
Vector::push_back(&mut caps, member_cap_type());
Vector::push_back(&mut caps, proposal_cap_type());
Vector::push_back(&mut caps, grant_cap_type());
Vector::push_back(&mut caps, token_mint_cap_type());
Vector::push_back(&mut caps, token_burn_cap_type());
Vector::push_back(&mut caps, plugin_event_cap_type());
caps
}
/// RootCap only have one instance, and can not been `drop` and `store`
struct DAORootCap<phantom DAOT> {}
struct DAOInstallPluginCap<phantom DAOT, phantom PluginT> has drop {}
struct DAOUpgradeModuleCap<phantom DAOT, phantom PluginT> has drop {}
struct DAOModifyConfigCap<phantom DAOT, phantom PluginT> has drop {}
struct DAOWithdrawTokenCap<phantom DAOT, phantom PluginT> has drop {}
struct DAOWithdrawNFTCap<phantom DAOT, phantom PluginT> has drop {}
struct DAOStorageCap<phantom DAOT, phantom PluginT> has drop {}
struct DAOMemberCap<phantom DAOT, phantom PluginT> has drop {}
struct DAOProposalCap<phantom DAOT, phantom PluginT> has drop {}
struct DAOGrantCap<phantom DAOT> has drop {}
struct DAOPluginEventCap<phantom DAOT, phantom PluginT> has drop {}
struct DAOGrantWithdrawTokenKey<phantom DAOT, phantom PluginT, phantom TokenT> has key, store{
/// The total amount of tokens that can be withdrawn by this capability
total: u128,
/// The amount of tokens that have been withdrawn by this capability
withdraw: u128,
/// The time-based linear release start time, timestamp in seconds.
start_time: u64,
/// The time-based linear release period in seconds
period: u64,
}
/// The info for DAO installed Plugin
struct InstalledPluginInfo<phantom PluginT> has key {
plugin_id: u64, // The plugin id of plugin marketplace
granted_caps: vector<CapType>, // The granted capabilities
}
/// The DAO member NFT metadata
struct DAOMember<phantom DAOT> has copy, store, drop {
//Member id
id: u64,
}
/// The DAO member NFT Body, hold the SBT token
struct DAOMemberBody<phantom DAOT> has store {
sbt: Token<DAOT>,
}
/// Create a dao with a exists DAO account
public fun create_dao<DAOT: store>(cap: DAOAccountCap, name: vector<u8>, image_data:Option::Option<vector<u8>>, image_url:Option::Option<vector<u8>>, description: vector<u8>, ext: DAOT, config: DAOConfig): DAORootCap<DAOT> acquires DAOEvent {
let dao_signer = DAOAccount::dao_signer(&cap);
let dao_address = Signer::address_of(&dao_signer);
let id = DAORegistry::register<DAOT>(dao_address);
ASCII::string(copy name);
let dao = DAO{
id,
name: copy name,
description: copy description,
dao_address,
next_member_id: 1,
next_proposal_id: 1,
};
move_to(&dao_signer, dao);
move_to(&dao_signer, DAOExt{
ext
});
move_to(&dao_signer, DAOAccountCapHolder{
cap
});
Token::register_token<DAOT>(&dao_signer, 1);
let token_mint_cap = Token::remove_mint_capability<DAOT>(&dao_signer);
let token_burn_cap = Token::remove_burn_capability<DAOT>(&dao_signer);
move_to(&dao_signer, DAOSBTMintCapHolder{
cap: token_mint_cap,
});
move_to(&dao_signer, DAOSBTBurnCapHolder{
cap: token_burn_cap,
});
let nft_name = copy name;
let nft_description = copy name;
let basemeta = if(Option::is_some(&image_data)){
NFT::new_meta_with_image_data(nft_name, Option::destroy_some(image_data), nft_description)
}else if(Option::is_some(&image_url)){
NFT::new_meta_with_image(nft_name, Option::destroy_some(image_url), nft_description)
}else{
NFT::new_meta(nft_name, nft_description)
};
NFT::register_v2<DAOMember<DAOT>>(&dao_signer, basemeta);
let nft_mint_cap = NFT::remove_mint_capability<DAOMember<DAOT>>(&dao_signer);
move_to(&dao_signer, DAONFTMintCapHolder{
cap: nft_mint_cap,
});
let nft_burn_cap = NFT::remove_burn_capability<DAOMember<DAOT>>(&dao_signer);
move_to(&dao_signer, DAONFTBurnCapHolder{
cap: nft_burn_cap,
});
let nft_update_cap = NFT::remove_update_capability<DAOMember<DAOT>>(&dao_signer);
move_to(&dao_signer, DAONFTUpdateCapHolder{
cap: nft_update_cap,
});
let config_modify_cap = Config::publish_new_config_with_capability(&dao_signer, config);
move_to(&dao_signer, DAOConfigModifyCapHolder{
cap: config_modify_cap,
});
move_to(&dao_signer, DAOEvent<DAOT> {
dao_create_event: Event::new_event_handle<DAOCreatedEvent>(&dao_signer),
});
move_to(&dao_signer ,MemberEvent{
member_offer_event_handler: Event::new_event_handle<MemberOfferEvent>(&dao_signer),
member_join_event_handler:Event::new_event_handle<MemberJoinEvent>(&dao_signer),
member_quit_event_handler:Event::new_event_handle<MemberQuitEvent>(&dao_signer),
member_revoke_event_handler:Event::new_event_handle<MemberRevokeEvent>(&dao_signer),
member_increase_sbt_event_handler:Event::new_event_handle<MemberIncreaseSBTEvent>(&dao_signer),
member_decrease_sbt_event_handler:Event::new_event_handle<MemberDecreaseSBTEvent>(&dao_signer),
});
move_to(&dao_signer, ProposalEvent<DAOT> {
proposal_create_event: Event::new_event_handle<ProposalCreatedEvent>(&dao_signer),
vote_event: Event::new_event_handle<VotedEvent>(&dao_signer),
proposal_action_event: Event::new_event_handle<ProposalActionEvent>(&dao_signer),
});
// dao event emit
let dao_event = borrow_global_mut<DAOEvent<DAOT> >(dao_address);
Event::emit_event(&mut dao_event.dao_create_event,
DAOCreatedEvent {
id,
name: copy name,
description:copy description,
dao_address,
},
);
DAORootCap<DAOT>{}
}
/// Upgrade account to DAO account and create DAO
public fun upgrade_to_dao<DAOT: store>(sender: signer, name: vector<u8>, image_data:Option::Option<vector<u8>>, image_url:Option::Option<vector<u8>>, description:vector<u8>, ext: DAOT, config: DAOConfig): DAORootCap<DAOT> acquires DAOEvent{
let cap = DAOAccount::upgrade_to_dao(sender);
create_dao<DAOT>(cap, name, image_data, image_url, description, ext, config)
}
/// Take ext from DAOExt
public fun take_ext<DAOT: store>(_witness: &DAOT): DAOT
acquires DAOExt {
let dao_addr = dao_address<DAOT>();
assert!(exists<DAOExt<DAOT>>(dao_addr), Errors::not_published(ERR_DAO_EXT));
let DAOExt<DAOT> { ext } = move_from<DAOExt<DAOT>>(dao_addr);
ext
}
/// Save ext to DAOExt
public fun save_ext<DAOT: store>(ext: DAOT) acquires DAOAccountCapHolder {
let dao_addr = dao_address<DAOT>();
assert!(!exists<DAOExt<DAOT>>(dao_addr), Errors::already_published(ERR_DAO_EXT));
let dao_signer = dao_signer<DAOT>();
move_to(&dao_signer, DAOExt{
ext
});
}
/// Burn the root cap after init the DAO
public fun burn_root_cap<DAOT>(cap: DAORootCap<DAOT>) {
let DAORootCap{} = cap;
}
/// dao event
struct DAOEvent<phantom DAOT> has key, store{
dao_create_event:Event::EventHandle<DAOCreatedEvent>,
}
struct DAOCreatedEvent has drop, store{
id: u64,
name: vector<u8>,
description:vector<u8>,
dao_address: address,
}
/// member event
struct MemberEvent has key, store{
member_offer_event_handler:Event::EventHandle<MemberOfferEvent>,
member_join_event_handler:Event::EventHandle<MemberJoinEvent>,
member_quit_event_handler:Event::EventHandle<MemberQuitEvent>,
member_revoke_event_handler:Event::EventHandle<MemberRevokeEvent>,
member_increase_sbt_event_handler:Event::EventHandle<MemberIncreaseSBTEvent>,
member_decrease_sbt_event_handler:Event::EventHandle<MemberDecreaseSBTEvent>,
}
const MEMBER_OFFER_CREATE: u8 = 0;
const MEMBER_OFFER_REBACK: u8 = 1;
const MEMBER_OFFER_USE: u8 = 2;
struct MemberOfferEvent has drop, store{
/// dao id
dao_id: u64,
//type: Create: 0 / Reback: 1 / Use: 2
type: u8,
//address
addr: address,
//image_data
image_data:Option::Option<vector<u8>>,
//image_url
image_url:Option::Option<vector<u8>>,
// SBT
sbt: u128,
}
const MEMBERJOIN_DIRECT :u8 = 0;
const MEMBERJOIN_OFFER :u8 = 1;
struct MemberJoinEvent has drop, store{
/// dao id
dao_id: u64,
// type: direct/offer
type: u8,
//Member id
member_id : u64,
//address
addr: address,
// SBT
sbt: u128,
}
struct MemberRevokeEvent has drop, store{
/// dao id
dao_id: u64,
//Member id
member_id : u64,
//address
addr: address,
// SBT
sbt: u128,
}
struct MemberQuitEvent has drop, store{
/// dao id
dao_id: u64,
//Member id
member_id : u64,
//address
addr: address,
// SBT
sbt: u128,
}
struct MemberIncreaseSBTEvent has drop, store{
//Member id
member_id : u64,
//address
addr: address,
//increase sbt amount
increase_sbt:u128 ,
// SBT
sbt: u128,
}
struct MemberDecreaseSBTEvent has drop, store{
//Member id
member_id : u64,
//address
addr: address,
//decrease sbt amount
decrease_sbt:u128 ,
// SBT
sbt: u128,
}
// Capability support function
/// Install ToInstallPluginT to DAO and grant the capabilites
public fun install_plugin_with_root_cap<DAOT: store, ToInstallPluginT:store>(_cap: &DAORootCap<DAOT>, granted_caps: vector<CapType>) acquires DAOAccountCapHolder {
do_install_plugin<DAOT, ToInstallPluginT>(granted_caps);
}
/// Install plugin with DAOInstallPluginCap
public fun install_plugin<DAOT: store, PluginT:store, ToInstallPluginT:store>(_cap: &DAOInstallPluginCap<DAOT, PluginT>, granted_caps: vector<CapType>) acquires DAOAccountCapHolder {
do_install_plugin<DAOT, ToInstallPluginT>(granted_caps);
}
fun do_install_plugin<DAOT: store, ToInstallPluginT:store>(granted_caps: vector<CapType>) acquires DAOAccountCapHolder {
assert!(DAOPluginMarketplace::exists_plugin<ToInstallPluginT>(), Errors::invalid_state(ERR_PLUGIN_NOT_EXIST));
assert_no_repeat(&granted_caps);
let dao_signer = dao_signer<DAOT>();
let plugin_id = DAOPluginMarketplace::take_plugin_id<ToInstallPluginT>();
assert!(!exists<InstalledPluginInfo<ToInstallPluginT>>(Signer::address_of(&dao_signer)), Errors::already_published(ERR_PLUGIN_HAS_INSTALLED));
move_to(&dao_signer, InstalledPluginInfo<ToInstallPluginT>{
plugin_id: plugin_id,
granted_caps,
});
}
/// Uninstall plugin with DAORootCap
public fun uninstall_plugin_with_root_cap<DAOT: store, ToInstallPluginT:store>(_cap: &DAORootCap<DAOT>) acquires DAOAccountCapHolder, InstalledPluginInfo {
do_uninstall_plugin<DAOT, ToInstallPluginT>();
}
/// Uninstall plugin with DAOInstallPluginCap
public fun uninstall_plugin<DAOT: store, PluginT:store, ToInstallPluginT:store>(_cap: &DAOInstallPluginCap<DAOT, PluginT>) acquires DAOAccountCapHolder, InstalledPluginInfo {
do_uninstall_plugin<DAOT, ToInstallPluginT>();
}
fun do_uninstall_plugin<DAOT: store, ToInstallPluginT:store>() acquires DAOAccountCapHolder, InstalledPluginInfo {
assert!(DAOPluginMarketplace::exists_plugin<ToInstallPluginT>(), Errors::invalid_state(ERR_PLUGIN_NOT_EXIST));
let dao_signer = dao_signer<DAOT>();
let dao_address = Signer::address_of(&dao_signer);
assert!(exists<InstalledPluginInfo<ToInstallPluginT>>(dao_address), Errors::already_published(ERR_PLUGIN_NOT_INSTALLED));
let installed_plugin = move_from<InstalledPluginInfo<ToInstallPluginT>>(dao_address);
let InstalledPluginInfo<ToInstallPluginT> {
plugin_id:_,
granted_caps:_,
} = installed_plugin;
}
// ModuleUpgrade functions
/// Submit upgrade module plan
public fun submit_upgrade_plan<DAOT: store, PluginT>(_cap: &DAOUpgradeModuleCap<DAOT, PluginT>, package_hash: vector<u8>, version: u64, enforced: bool) acquires DAOAccountCapHolder {
let dao_account_cap = &mut borrow_global_mut<DAOAccountCapHolder>(dao_address<DAOT>()).cap;
DAOAccount::submit_upgrade_plan(dao_account_cap, package_hash, version, enforced);
}
// Storage capability function
struct StorageItem<phantom PluginT, V: store> has key {
item: V,
}
/// Save the item to the storage
public fun save<DAOT: store, PluginT, V: store>(_cap: &DAOStorageCap<DAOT, PluginT>, item: V) acquires DAOAccountCapHolder {
let dao_signer = dao_signer<DAOT>();
assert!(!exists<StorageItem<PluginT, V>>(Signer::address_of(&dao_signer)), Errors::already_published(ERR_STORAGE_ERROR));
move_to(&dao_signer, StorageItem<PluginT, V>{
item
});
}
/// Get the item from the storage
public fun take<DAOT: store, PluginT, V: store>(_cap: &DAOStorageCap<DAOT, PluginT>): V acquires StorageItem {
let dao_address = dao_address<DAOT>();
assert!(exists<StorageItem<PluginT, V>>(dao_address), Errors::not_published(ERR_STORAGE_ERROR));
let StorageItem{ item } = move_from<StorageItem<PluginT, V>>(dao_address);
item
}
/// Check the item has exists in storage
public fun exists_storage<DAOT: store, PluginT, V: store>(): bool {
exists<StorageItem<PluginT, V>>(dao_address<DAOT>())
}
/// borow the item from the storage
public fun borrow_storage<DAOT: store, PluginT, V: store+copy>(_cap: &DAOStorageCap<DAOT, PluginT>): V acquires StorageItem {
let dao_address = dao_address<DAOT>();
assert!(exists<StorageItem<PluginT, V>>(dao_address), Errors::not_published(ERR_STORAGE_ERROR));
let storage_item = borrow_global<StorageItem<PluginT, V>>(dao_address);
*&storage_item.item
}
// Withdraw Token capability function
/// Withdraw the token from the DAO account
public fun withdraw_token<DAOT: store, PluginT, TokenT: store>(_cap: &DAOWithdrawTokenCap<DAOT, PluginT>, amount: u128): Token<TokenT> acquires DAOAccountCapHolder {
let dao_signer = dao_signer<DAOT>();
//we should extract the WithdrawCapability from account, and invoke the withdraw_with_cap ?
Account::withdraw<TokenT>(&dao_signer, amount)
}
// NFT capability function
/// Withdraw the NFT from the DAO account
public fun withdraw_nft<DAOT: store, PluginT, NFTMeta: store + copy + drop, NFTBody: store>(_cap: &DAOWithdrawNFTCap<DAOT, PluginT>, id: u64): NFT<NFTMeta, NFTBody> acquires DAOAccountCapHolder {
let dao_signer = dao_signer<DAOT>();
let nft = NFTGallery::withdraw<NFTMeta, NFTBody>(&dao_signer, id);
assert!(Option::is_some(&nft), Errors::not_published(ERR_NFT_ERROR));
Option::destroy_some(nft)
}
// Membership function
struct OfferMemeber<phantom DAOT> has drop, store{
to_address: address,
image_data:Option::Option<vector<u8>>,
image_url:Option::Option<vector<u8>>,
init_sbt: u128
}
public fun member_offer<DAOT: store, PluginT>(_cap: &DAOMemberCap<DAOT, PluginT>, to_address: address, image_data:Option::Option<vector<u8>>, image_url:Option::Option<vector<u8>>, init_sbt: u128) acquires DAOAccountCapHolder, MemberEvent, DAO {
let dao_address = dao_address<DAOT>();
let dao_signer = dao_signer<DAOT>();
let memeber_event = borrow_global_mut<MemberEvent>(dao_address);
if(is_member<DAOT>(to_address)){
Event::emit_event(&mut memeber_event.member_offer_event_handler, MemberOfferEvent {
dao_id: dao_id(dao_address),
addr:to_address,
type:MEMBER_OFFER_REBACK,
image_data,
image_url,
sbt: init_sbt,
});
return
};
let op_index = Offer::find_offer<OfferMemeber<DAOT>>(dao_address, to_address);
if(Option::is_some(&op_index)){
let OfferMemeber<DAOT>{
to_address,
image_data,
image_url,
init_sbt
} = Offer::retake<OfferMemeber<DAOT>>(&dao_signer, Option::destroy_some(op_index));
Event::emit_event(&mut memeber_event.member_offer_event_handler, MemberOfferEvent {
dao_id: dao_id(dao_address),
addr:to_address,
type:MEMBER_OFFER_REBACK,
image_data,
image_url,
sbt: init_sbt,
});
return
};
let offered = OfferMemeber<DAOT> {
to_address,
image_data: copy image_data,
image_url: copy image_url,
init_sbt
};
Offer::create_v2<OfferMemeber<DAOT>>(&dao_signer, offered, to_address, 0);
Event::emit_event(&mut memeber_event.member_offer_event_handler, MemberOfferEvent {
dao_id: dao_id(dao_address),
addr:to_address,
type:MEMBER_OFFER_CREATE,
image_data,
image_url,
sbt: init_sbt,
});
}
public fun member_offer_revoke<DAOT: store, PluginT>(_cap: &DAOMemberCap<DAOT, PluginT>, to_address: address) acquires DAOAccountCapHolder, MemberEvent, DAO {
let dao_address = dao_address<DAOT>();
let dao_signer = dao_signer<DAOT>();
let op_index = Offer::find_offer<OfferMemeber<DAOT>>(dao_address, to_address);
let memeber_event = borrow_global_mut<MemberEvent>(dao_address);
if(Option::is_some(&op_index)){
let OfferMemeber<DAOT>{
to_address,
image_data,
image_url,
init_sbt
} = Offer::retake<OfferMemeber<DAOT>>(&dao_signer, Option::destroy_some(op_index));
Event::emit_event(&mut memeber_event.member_offer_event_handler, MemberOfferEvent {
dao_id: dao_id(dao_address),
addr:to_address,
type:MEMBER_OFFER_REBACK,
image_data,
image_url,
sbt: init_sbt,
});
}
}
/// Join DAO and get a membership by self
public fun join_member<DAOT: store>(sender: &signer) acquires DAONFTMintCapHolder, DAOSBTMintCapHolder, DAO, MemberEvent, DAONFTUpdateCapHolder {
let dao_address = dao_address<DAOT>();
let op_index = Offer::find_offer<OfferMemeber<DAOT>>(dao_address, Signer::address_of(sender));
assert!(Option::is_some(&op_index),1003);
let memeber_event = borrow_global_mut<MemberEvent>(dao_address);
let OfferMemeber<DAOT> {
to_address,
image_data,
image_url,
init_sbt
}= Offer::redeem_v2<OfferMemeber<DAOT>>(sender, dao_address, Option::destroy_some(op_index));
Event::emit_event(&mut memeber_event.member_offer_event_handler, MemberOfferEvent {
dao_id: dao_id(dao_address),
addr:to_address,
type:MEMBER_OFFER_USE,
image_data: copy image_data,
image_url: copy image_url,
sbt: init_sbt,
});
if(is_member<DAOT>(to_address)){
increase_member_sbt<DAOT, DAOT>(&DAOMemberCap<DAOT, DAOT>{}, to_address, init_sbt);
}else{
do_join_member<DAOT>(to_address, image_data, image_url, init_sbt);
Event::emit_event(&mut memeber_event.member_join_event_handler, MemberJoinEvent {
dao_id: dao_id(dao_address),
type:MEMBERJOIN_OFFER,
member_id:Option::destroy_some(query_member_id<DAOT>(to_address)),
addr:to_address,
sbt: init_sbt,
});
}
}
public (script) fun join_member_entry<DAOT: store>(sender: signer) acquires DAONFTMintCapHolder, DAOSBTMintCapHolder, DAO, MemberEvent , DAONFTUpdateCapHolder {
join_member<DAOT>(&sender)
}
fun do_join_member<DAOT: store>(to_address: address, image_data:Option::Option<vector<u8>>, image_url:Option::Option<vector<u8>>, init_sbt: u128) acquires DAONFTMintCapHolder, DAOSBTMintCapHolder, DAO {
ensure_not_member<DAOT>(to_address);
let member_id = next_member_id<DAOT>();
let meta = DAOMember<DAOT>{
id: member_id,
};
let dao_address = dao_address<DAOT>();
let token_mint_cap = &borrow_global_mut<DAOSBTMintCapHolder<DAOT>>(dao_address).cap;
let sbt = Token::mint_with_capability<DAOT>(token_mint_cap, init_sbt);
let body = DAOMemberBody<DAOT>{
sbt,
};
let dao = borrow_global<DAO>(dao_address);
let nft_name = *&dao.name;
let nft_description = *&dao.name;
let basemeta = if(Option::is_some(&image_data)){
NFT::new_meta_with_image_data(nft_name, Option::destroy_some(image_data), nft_description)
}else if(Option::is_some(&image_url)){
NFT::new_meta_with_image(nft_name, Option::destroy_some(image_url), nft_description)
}else{
NFT::new_meta(nft_name, nft_description)
};
let nft_mint_cap = &mut borrow_global_mut<DAONFTMintCapHolder<DAOT>>(dao_address).cap;
let nft = NFT::mint_with_cap_v2(dao_address, nft_mint_cap, basemeta, meta, body);
IdentifierNFT::grant_to(nft_mint_cap, to_address, nft);
}
/// Member quit DAO by self
public (script) fun quit_member_entry<DAOT: store>(sender: signer) acquires DAONFTBurnCapHolder, DAOSBTBurnCapHolder, MemberEvent, DAO, DAOAccountCapHolder {
quit_member<DAOT>(&sender);
}
/// Member quit DAO by self
public fun quit_member<DAOT: store>(sender: &signer) acquires DAONFTBurnCapHolder, DAOSBTBurnCapHolder, MemberEvent, DAO, DAOAccountCapHolder {
let member_addr = Signer::address_of(sender);
if(!is_member<DAOT>(member_addr)){
let dao_address = dao_address<DAOT>();
let dao_signer = dao_signer<DAOT>();
let op_index = Offer::find_offer<OfferMemeber<DAOT>>(dao_address, member_addr);
if(Option::is_some(&op_index)){
Offer::retake<OfferMemeber<DAOT>>(&dao_signer, Option::destroy_some(op_index));
};
return
};
let (member_id , sbt) = do_remove_member<DAOT>(member_addr);
let dao_address = dao_address<DAOT>();
let memeber_event = borrow_global_mut<MemberEvent>(dao_address);
Event::emit_event(&mut memeber_event.member_quit_event_handler, MemberQuitEvent {
dao_id: dao_id(dao_address),
member_id,
addr:member_addr,
sbt,
});
}
/// Revoke membership with cap
public fun revoke_member<DAOT: store, PluginT>(_cap: &DAOMemberCap<DAOT, PluginT>, member_addr: address) acquires DAONFTBurnCapHolder, DAOSBTBurnCapHolder, MemberEvent, DAO {
let (member_id , sbt) = do_remove_member<DAOT>(member_addr);
let dao_address = dao_address<DAOT>();
let memeber_event = borrow_global_mut<MemberEvent>(dao_address);
Event::emit_event(&mut memeber_event.member_revoke_event_handler, MemberRevokeEvent {
dao_id: dao_id(dao_address),
member_id,
addr:member_addr,
sbt: sbt,
});
}
public fun ensure_member<DAOT: store>(member_addr:address){
assert!(is_member<DAOT>(member_addr), Errors::invalid_state(ERR_EXPECT_MEMBER));
}
public fun ensure_not_member<DAOT: store>(member_addr:address){
assert!(!is_member<DAOT>(member_addr), Errors::invalid_state(ERR_EXPECT_NOT_MEMBER));
}
fun do_remove_member<DAOT: store>(member_addr: address):(u64,u128) acquires DAONFTBurnCapHolder, DAOSBTBurnCapHolder {
ensure_member<DAOT>(member_addr);
let dao_address = dao_address<DAOT>();
let nft_burn_cap = &mut borrow_global_mut<DAONFTBurnCapHolder<DAOT>>(dao_address).cap;
let nft = IdentifierNFT::revoke<DAOMember<DAOT>, DAOMemberBody<DAOT>>(nft_burn_cap, member_addr);
let member_id = NFT::get_type_meta<DAOMember<DAOT>, DAOMemberBody<DAOT>>(&nft).id;
let DAOMemberBody<DAOT>{ sbt } = NFT::burn_with_cap(nft_burn_cap, nft);
let sbt_amount = Token::value<DAOT>(&sbt);
let token_burn_cap = &mut borrow_global_mut<DAOSBTBurnCapHolder<DAOT>>(dao_address).cap;
Token::burn_with_capability(token_burn_cap, sbt);
(member_id, sbt_amount)
}
public fun join_member_with_root_cap<DAOT: store>(_cap: &DAORootCap<DAOT>, to_address: address, image_data:Option::Option<vector<u8>>, image_url:Option::Option<vector<u8>>, init_sbt: u128) acquires DAONFTMintCapHolder, DAOSBTMintCapHolder, DAO, MemberEvent {
let dao_address = dao_address<DAOT>();
let memeber_event = borrow_global_mut<MemberEvent>(dao_address);
do_join_member<DAOT>(to_address, image_data, image_url, init_sbt);
Event::emit_event(&mut memeber_event.member_join_event_handler, MemberJoinEvent {
dao_id: dao_id(dao_address),
type:MEMBERJOIN_DIRECT,
member_id:Option::destroy_some(query_member_id<DAOT>(to_address)),
addr:to_address,
sbt: init_sbt,
});
}
public fun join_member_with_member_cap<DAOT: store, Plugin>(_cap: &DAOMemberCap<DAOT, Plugin>, to_address: address, image_data:Option::Option<vector<u8>>, image_url:Option::Option<vector<u8>>, init_sbt: u128) acquires DAONFTMintCapHolder, DAOSBTMintCapHolder, DAO, MemberEvent {
let dao_address = dao_address<DAOT>();
let memeber_event = borrow_global_mut<MemberEvent>(dao_address);
do_join_member<DAOT>(to_address, image_data, image_url, init_sbt);
Event::emit_event(&mut memeber_event.member_join_event_handler, MemberJoinEvent {
dao_id: dao_id(dao_address),
type:MEMBERJOIN_DIRECT,
member_id:Option::destroy_some(query_member_id<DAOT>(to_address)),
addr:to_address,
sbt: init_sbt,
});
}
/// Increment the member SBT
public fun increase_member_sbt<DAOT: store, PluginT>(_cap: &DAOMemberCap<DAOT, PluginT>, member_addr: address, amount: u128) acquires DAONFTUpdateCapHolder, DAOSBTMintCapHolder, MemberEvent {
ensure_member<DAOT>(member_addr);
let dao_address = dao_address<DAOT>();
let nft_update_cap = &mut borrow_global_mut<DAONFTUpdateCapHolder<DAOT>>(dao_address).cap;
let borrow_nft = IdentifierNFT::borrow_out<DAOMember<DAOT>, DAOMemberBody<DAOT>>(nft_update_cap, member_addr);
let nft = IdentifierNFT::borrow_nft_mut(&mut borrow_nft);
let member_id = NFT::get_type_meta<DAOMember<DAOT>, DAOMemberBody<DAOT>>(nft).id;
let body = NFT::borrow_body_mut_with_cap(nft_update_cap, nft);
let token_mint_cap = &mut borrow_global_mut<DAOSBTMintCapHolder<DAOT>>(dao_address).cap;
let increase_sbt = Token::mint_with_capability<DAOT>(token_mint_cap, amount);
Token::deposit(&mut body.sbt, increase_sbt);
let sbt_amount = Token::value<DAOT>(&body.sbt);
IdentifierNFT::return_back(borrow_nft);
let memeber_event = borrow_global_mut<MemberEvent>(dao_address);
Event::emit_event(&mut memeber_event.member_increase_sbt_event_handler, MemberIncreaseSBTEvent {
member_id,
addr:member_addr,
increase_sbt:amount,
sbt: sbt_amount,
});
}
/// Decrement the member SBT
public fun decrease_member_sbt<DAOT: store, PluginT>(_cap: &DAOMemberCap<DAOT, PluginT>, member_addr: address, amount: u128) acquires DAONFTUpdateCapHolder, DAOSBTBurnCapHolder, MemberEvent {
ensure_member<DAOT>(member_addr);
let dao_address = dao_address<DAOT>();
let nft_update_cap = &mut borrow_global_mut<DAONFTUpdateCapHolder<DAOT>>(dao_address).cap;
let borrow_nft = IdentifierNFT::borrow_out<DAOMember<DAOT>, DAOMemberBody<DAOT>>(nft_update_cap, member_addr);
let nft = IdentifierNFT::borrow_nft_mut(&mut borrow_nft);
let member_id = NFT::get_type_meta<DAOMember<DAOT>, DAOMemberBody<DAOT>>(nft).id;
let body = NFT::borrow_body_mut_with_cap(nft_update_cap, nft);
let token_burn_cap = &mut borrow_global_mut<DAOSBTBurnCapHolder<DAOT>>(dao_address).cap;
let decrease_sbt = Token::withdraw(&mut body.sbt, amount);
let sbt_amount = Token::value<DAOT>(&body.sbt);
Token::burn_with_capability(token_burn_cap, decrease_sbt);
IdentifierNFT::return_back(borrow_nft);
let memeber_event = borrow_global_mut<MemberEvent>(dao_address);
Event::emit_event(&mut memeber_event.member_decrease_sbt_event_handler, MemberDecreaseSBTEvent {
member_id,
addr:member_addr,
decrease_sbt:amount,
sbt: sbt_amount,
});
}
public fun set_member_image<DAOT: store, PluginT>(
_cap: &DAOMemberCap<DAOT, PluginT>,
member_addr: address,
image_data: Option::Option<vector<u8>>,
image_url: Option::Option<vector<u8>>
) acquires DAONFTUpdateCapHolder {
ensure_member<DAOT>(member_addr);
let dao_address = dao_address<DAOT>();
let nft_update_cap = &mut borrow_global_mut<DAONFTUpdateCapHolder<DAOT>>(dao_address).cap;
let borrow_nft = IdentifierNFT::borrow_out<DAOMember<DAOT>, DAOMemberBody<DAOT>>(nft_update_cap, member_addr);
let nft = IdentifierNFT::borrow_nft_mut(&mut borrow_nft);
let metadata = *NFT::get_type_meta<DAOMember<DAOT>, DAOMemberBody<DAOT>>(nft);
let old_basemeta = NFT::get_base_meta<DAOMember<DAOT>, DAOMemberBody<DAOT>>(nft);
let new_basemeta = if(Option::is_some(&image_data)){
NFT::new_meta_with_image_data(NFT::meta_name(old_basemeta), Option::destroy_some(image_data), NFT::meta_description(old_basemeta))
}else if(Option::is_some(&image_url)){
NFT::new_meta_with_image(NFT::meta_name(old_basemeta), Option::destroy_some(image_url), NFT::meta_description(old_basemeta))
}else{
NFT::new_meta(NFT::meta_name(old_basemeta), NFT::meta_description(old_basemeta))
};
NFT::update_meta_with_cap(nft_update_cap, nft, new_basemeta, metadata);
IdentifierNFT::return_back(borrow_nft);
}
/// Query amount of the member SBT
public fun query_sbt<DAOT: store>(member_addr: address)
: u128 acquires DAONFTUpdateCapHolder {
if (!is_member<DAOT>(member_addr)) {
return 0
};
let dao_address = dao_address<DAOT>();
let nft_update_cap =
&mut borrow_global_mut<DAONFTUpdateCapHolder<DAOT>>(dao_address).cap;
let borrow_nft =
IdentifierNFT::borrow_out<DAOMember<DAOT>, DAOMemberBody<DAOT>>(nft_update_cap, member_addr);
let nft = IdentifierNFT::borrow_nft(&mut borrow_nft);
let body = NFT::borrow_body(nft);
let result = Token::value(&body.sbt);
IdentifierNFT::return_back(borrow_nft);
result
}
/// Query member id of the member
public fun query_member_id<DAOT: store>(member_addr: address): Option::Option<u64>{
if(!is_member<DAOT>(member_addr)){
return Option::none<u64>()
};
let nft_info = Option::destroy_some(IdentifierNFT::get_nft_info<DAOMember<DAOT>, DAOMemberBody<DAOT>>(member_addr));
let (_, _, _, type_meta) = NFT::unpack_info(nft_info);
Option::some(type_meta.id)
}
/// Check the `member_addr` account is a member of DAOT
public fun is_member<DAOT: store>(member_addr: address): bool {
IdentifierNFT::owns<DAOMember<DAOT>, DAOMemberBody<DAOT>>(member_addr)
}
public fun is_exist_member_offer<DAOT>(member_addr: address):bool{
let dao_address = dao_address<DAOT>();
Option::is_some(&Offer::find_offer<OfferMemeber<DAOT>>(dao_address, member_addr))
}
struct PluginEvent<phantom DAOT : store, phantom PluginT: store, phantom EventT: store + drop> has key, store {
event_handle: Event::EventHandle<EventT>,
}
/// Plugin event
public fun init_plugin_event<
DAOT: store,
PluginT: store,
EventT: store + drop
>(_cap: &DAOPluginEventCap<DAOT, PluginT>) acquires DAOAccountCapHolder {
let dao_signer = dao_signer<DAOT>();
EventUtil::init_event<EventT>(&dao_signer);