Skip to content

Commit 91d6bfb

Browse files
authored
Merge pull request #106 from MisoShiru69/main
first commit
2 parents d110ef7 + 4515f4f commit 91d6bfb

File tree

4 files changed

+432
-0
lines changed

4 files changed

+432
-0
lines changed

5-DAO/grant_funder/Cargo.toml

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[package]
2+
name = "grant_funder"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
sbor = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.6.0" }
8+
scrypto = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.6.0" }
9+
10+
[dev-dependencies]
11+
transaction = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.6.0" }
12+
radix-engine = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.6.0" }
13+
scrypto-unit = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.6.0" }
14+
15+
[profile.release]
16+
opt-level = 's' # Optimize for size.
17+
lto = true # Enable Link Time Optimization.
18+
codegen-units = 1 # Reduce number of codegen units to increase optimizations.
19+
panic = 'abort' # Abort on panic.
20+
strip = "debuginfo" # Strip debug info.
21+
overflow-checks = true # Panic in the case of an overflow.
22+
23+
[lib]
24+
crate-type = ["cdylib", "lib"]

5-DAO/grant_funder/README.md

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Grant Funder
2+
3+
Grant Funder allows users to create a grant proposal and receive funding. The proposal owner must put down 50% of the requested grant amount. Funds are released in 4 stages. Each proposal stage is voted on by the community. The proposal owner must show the community their progress, answer questions, and address suggestions that arise, to earn enough votes to proceed to the next stage. If a proposal does not earn enough points in the alloted time, the proposal is burned. Stage 1 releases 20%, stage 2 releases 40%, stage 3 released 70%, and stage 4 releases 100% of the approved funds.
4+
5+
## Design Details
6+
7+
1. User creates a grant proposal which mints a NFT containing information about the grant and requested XRD amount. The user will receive a receipt NFT referencing the proposal NFT id.
8+
9+
2. There are two vaults located in the component. Users can vote on a proposal by sending 1 XRD to the vaults. One vault represents a YES vote the other a NO vote.
10+
11+
3. Votes are counted and if the proposal is > 50% then the proposal NFT data stage is incremented and returned to the proposal vault.
12+
13+
4. The user can use the update_receipt method to see if their proposal was approved. If it was approved their proposal receipt NFT data will be updated.
14+
15+
5. The user can take their updated proposal receipt NFT and use it to retrieve their funds.
16+
17+
## Getting Started
18+
19+
```
20+
resim reset
21+
export XRD=resource_sim1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzqu57yag
22+
```
23+
24+
1. Lets create a new account
25+
26+
```
27+
OP1=$(resim new-account)
28+
export PRIV_KEY1=$(echo "$OP1" | sed -nr "s/Private key: ([[:alnum:]_]+)/\1/p")
29+
export PUB_KEY1=$(echo "$OP1" | sed -nr "s/Public key: ([[:alnum:]_]+)/\1/p")
30+
export ACCOUNT_ADDRESS1=$(echo "$OP1" | sed -nr "s/Account component address: ([[:alnum:]_]+)/\1/p")
31+
```
32+
33+
2. Lets publish the blueprint
34+
```
35+
PK_OP=$(resim publish ".")
36+
echo $PK_OP
37+
export PACKAGE=$(echo "$PK_OP" | sed -nr "s/Success! New Package: ([[:alnum:]_]+)/\1/p")
38+
```
39+
40+
3. Lets fund the Grant Funder Protocal with 700 XRD and instantiate the component
41+
```
42+
COMPONENT_OP=$(resim call-function $PACKAGE GrantFunder new 700,$XRD)
43+
echo $COMPONENT_OP
44+
45+
export COMPONENT=$(echo "$COMPONENT_OP" | sed -nr "s/.* Component: ([[:alnum:]_]+)/\1/p" | sed '1q;d')
46+
47+
export PROPOSAL_RECEIPT=$(echo "$COMPONENT_OP" | sed -nr "s/.* Resource: ([[:alnum:]_]+)/\1/p" | sed '3q;d')
48+
```
49+
50+
4. Lets create a grant proposal for 200 XRD by putting 100 XRD down.
51+
```
52+
resim call-method $COMPONENT create_proposal 100,$XRD first_grant 200
53+
```
54+
55+
5. Lets vote of the proposal
56+
```
57+
resim call-method $COMPONENT vote_yes 1,$XRD
58+
resim call-method $COMPONENT vote_yes 1,$XRD
59+
resim call-method $COMPONENT vote_yes 1,$XRD
60+
resim call-method $COMPONENT vote_yes 1,$XRD
61+
resim call-method $COMPONENT vote_yes 1,$XRD
62+
resim call-method $COMPONENT vote_no 1,$XRD
63+
resim call-method $COMPONENT vote_no 1,$XRD
64+
resim call-method $COMPONENT vote_no 1,$XRD
65+
```
66+
67+
6. Lets grab the proposal NFT id from inside the component
68+
69+
```
70+
resim show $COMPONENT
71+
```
72+
73+
7. We will use the proposal NFT id for the count_votes method. If the proposal is voted for then the proposal NFT is incremented and placed back in the component.
74+
```
75+
resim call-method $COMPONENT count_votes ---PASTE NFT ID HERE---
76+
```
77+
78+
8. Lets see if our grant proposal was approved by running the update_receipt method. If the proposal moved forward the proposal receipt NFT stage data is incremented.
79+
```
80+
resim call-method $COMPONENT update_receipt 1,$PROPOSAL_RECEIPT
81+
```
82+
83+
9. Lets retrieve our grant funds using the updated proposal receipt NFT.
84+
```
85+
resim call-method $COMPONENT collect_xrd 1,$PROPOSAL_RECEIPT
86+
```
87+
88+
10. Typically the YES NO vaults will get emptied after each vote. But for this example we will run the count_votes, update_receipt, and collect_xrd method a few more times just to illustrate the life cycle of the grant proposal.
89+
```
90+
resim call-method $COMPONENT count_votes ---PASTE NFT ID HERE---
91+
resim call-method $COMPONENT update_receipt 1,$PROPOSAL_RECEIPT
92+
resim call-method $COMPONENT collect_xrd 1,$PROPOSAL_RECEIPT
93+
```
94+

5-DAO/grant_funder/src/lib.rs

+271
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
use scrypto::prelude::*;
2+
3+
#[derive(NonFungibleData)]
4+
pub struct Proposal {
5+
name:String,
6+
amount:Decimal,
7+
#[scrypto(mutable)]
8+
stage:u8,
9+
}
10+
11+
#[derive(NonFungibleData)]
12+
pub struct ProposalReceipt {
13+
14+
amount:Decimal,
15+
#[scrypto(mutable)]
16+
stage:u8,
17+
proposal_id:NonFungibleId,
18+
#[scrypto(mutable)]
19+
stage1_funded:bool,
20+
#[scrypto(mutable)]
21+
stage2_funded:bool,
22+
#[scrypto(mutable)]
23+
stage3_funded:bool,
24+
#[scrypto(mutable)]
25+
stage4_funded:bool,
26+
}
27+
28+
blueprint! {
29+
struct GrantFunder {
30+
//Vault that holds the admin badge
31+
admin_badge_vault:Vault,
32+
33+
//Vault that holds the proposal NFT
34+
proposal_vault:Vault,
35+
36+
//Vault that signifies YES votes
37+
yes_vote_vault:Vault,
38+
39+
//Vault that signifies NO votes
40+
no_vote_vault:Vault,
41+
42+
//Proposal NFT resource address
43+
proposal_resource_address:ResourceAddress,
44+
45+
//Proposal receipt for accessing funds
46+
proposal_receipt_resource_address:ResourceAddress,
47+
48+
//Vault that holds XRD for grants
49+
grant_vault:Vault,
50+
}
51+
52+
impl GrantFunder {
53+
54+
pub fn new(grant_xrd:Bucket) -> ComponentAddress {
55+
56+
let admin_badge:Bucket = ResourceBuilder::new_fungible()
57+
.metadata("name", "Admin Badge")
58+
.initial_supply(1);
59+
60+
let proposal_resource_address:ResourceAddress = ResourceBuilder::new_non_fungible()
61+
.metadata("name", "Proposal")
62+
.mintable(rule!(require(admin_badge.resource_address(),)), LOCKED)
63+
.burnable(rule!(require(admin_badge.resource_address())), LOCKED)
64+
.updateable_non_fungible_data(rule!(require(admin_badge.resource_address())), LOCKED)
65+
.no_initial_supply();
66+
67+
let proposal_receipt_resource_address:ResourceAddress = ResourceBuilder::new_non_fungible()
68+
.metadata("name", "Proposal Receipt")
69+
.mintable(rule!(require(admin_badge.resource_address(),)), LOCKED)
70+
.burnable(rule!(require(admin_badge.resource_address())), LOCKED)
71+
.updateable_non_fungible_data(rule!(require(admin_badge.resource_address())), LOCKED)
72+
.no_initial_supply();
73+
74+
let component = Self {
75+
yes_vote_vault:Vault::new(RADIX_TOKEN),
76+
no_vote_vault:Vault::new(RADIX_TOKEN),
77+
admin_badge_vault:Vault::with_bucket(admin_badge),
78+
proposal_vault:Vault::new(proposal_resource_address),
79+
proposal_resource_address,
80+
grant_vault:Vault::with_bucket(grant_xrd),
81+
proposal_receipt_resource_address,
82+
}
83+
84+
.instantiate()
85+
.globalize();
86+
87+
return component;
88+
}
89+
90+
//Create grant proposal, enter name and amount
91+
pub fn create_proposal(&mut self,
92+
down_payment:Bucket,
93+
name:String,
94+
grant_amount:Decimal,
95+
) -> Bucket {
96+
97+
assert!(down_payment.amount() >= grant_amount / 2, "Down payment must be 50% of grant");
98+
99+
//Put down payment in grant vault
100+
self.grant_vault.put(down_payment);
101+
102+
//Proposal NFT data
103+
let proposal_nft_data = Proposal {
104+
name:name,
105+
amount:grant_amount,
106+
stage:0,
107+
};
108+
109+
//Mint proposal NFT
110+
let proposal_nft_bucket = self.admin_badge_vault.authorize(||
111+
borrow_resource_manager!(self.proposal_resource_address).mint_non_fungible(
112+
&NonFungibleId::random(),
113+
proposal_nft_data,
114+
)
115+
);
116+
117+
//Proposal receipt NFT data
118+
let receipt_nft_data = ProposalReceipt {
119+
120+
amount:grant_amount,
121+
stage:0,
122+
proposal_id:proposal_nft_bucket.non_fungible::<Proposal>().id(),
123+
stage1_funded:false,
124+
stage2_funded:false,
125+
stage3_funded:false,
126+
stage4_funded:false,
127+
};
128+
129+
//Mint proposal receipt NFT
130+
let receipt_nft_bucket = self.admin_badge_vault.authorize(||
131+
borrow_resource_manager!(self.proposal_receipt_resource_address).mint_non_fungible(
132+
&NonFungibleId::random(),
133+
receipt_nft_data,
134+
)
135+
);
136+
137+
//Put the proposal NFT back in the proposal NFT vault
138+
self.proposal_vault.put(proposal_nft_bucket);
139+
140+
//Return proposal receipt to user
141+
return receipt_nft_bucket;
142+
143+
}
144+
145+
//Vote yes
146+
pub fn vote_yes(&mut self, mut vote_xrd:Bucket) -> Bucket{
147+
self.yes_vote_vault.put(vote_xrd.take(dec!("1")));
148+
return vote_xrd;
149+
150+
}
151+
152+
//Vote no
153+
pub fn vote_no(&mut self, mut vote_xrd:Bucket) -> Bucket{
154+
self.no_vote_vault.put(vote_xrd.take(dec!("1")));
155+
return vote_xrd;
156+
157+
}
158+
159+
//This method counts the total of yes and no votes
160+
//If there are more yes than no votes increment the proposal NFT data
161+
pub fn count_votes(&mut self, nft_id:NonFungibleId) {
162+
163+
//Take the proposal from the proposal NFT vault
164+
let nft_bucket = self.proposal_vault.take_non_fungible(&nft_id);
165+
166+
//Get the proposal NFT data
167+
let mut nft_data:Proposal = nft_bucket.non_fungible().data();
168+
169+
//Determine the amount of yes and no votes
170+
let yes_votes:Decimal = self.yes_vote_vault.amount();
171+
172+
info!("Yes votes{}", yes_votes);
173+
174+
let no_votes:Decimal = self.no_vote_vault.amount();
175+
176+
info!("No votes{}", no_votes);
177+
178+
//If yes votes > no votes increment the proposal NFT data
179+
if yes_votes > no_votes {
180+
181+
//Increment NFT data stage
182+
nft_data.stage +=1;
183+
184+
//Update the proposal NFT data
185+
self.admin_badge_vault.authorize(|| nft_bucket.non_fungible().update_data(nft_data));
186+
187+
//Put the proposal NFT back into the proposal vote
188+
self.proposal_vault.put(nft_bucket);
189+
190+
//Remove all funds from the yes no vaults and place them in the grant fund
191+
192+
} else {
193+
//Burn proposal
194+
self.admin_badge_vault.authorize(|| nft_bucket.burn());
195+
}
196+
}
197+
198+
pub fn update_receipt(&mut self, receipt:Bucket) -> Bucket {
199+
//Get NFT data from receipt NFT
200+
let mut receipt_nft_data:ProposalReceipt = receipt.non_fungible().data();
201+
202+
//Get the proposal id from the NFT data
203+
let proposal_id:NonFungibleId = receipt_nft_data.proposal_id.clone();
204+
205+
//Use the proposal id to find proposal NFT in proposal vault
206+
let proposal_nft:Bucket = self.proposal_vault.take_non_fungible(&proposal_id);
207+
208+
//Get the NFT data from the proposal NFT
209+
let proposal_nft_data:Proposal = proposal_nft.non_fungible().data();
210+
211+
//Get the stage from the proposal id
212+
let proposal_stage:u8 = proposal_nft_data.stage;
213+
214+
//Update the receipt NFT stage
215+
receipt_nft_data.stage = proposal_stage;
216+
217+
//Update the receipt NFT data
218+
self.admin_badge_vault.authorize( || receipt.non_fungible()
219+
.update_data(receipt_nft_data));
220+
221+
//Return proposal receipt to vault
222+
self.proposal_vault.put(proposal_nft);
223+
224+
//Return receipt to user
225+
return receipt;
226+
227+
}
228+
229+
pub fn collect_xrd(&mut self, proposal_receipt:Bucket) -> Option<(Bucket, Bucket)> {
230+
231+
//Get data from proposal receipt
232+
let mut receipt_nft_data:ProposalReceipt = proposal_receipt.non_fungible().data();
233+
let amount = receipt_nft_data.amount;
234+
let stage = receipt_nft_data.stage;
235+
236+
//Verify proposal receipt stage and pay grant funds and update proposal nft data
237+
if stage == 1 && receipt_nft_data.stage1_funded == false {
238+
info!("Here are your stage 1 funds");
239+
let funds = self.grant_vault.take((amount/10)*2);
240+
receipt_nft_data.stage1_funded = true;
241+
self.admin_badge_vault.authorize(|| proposal_receipt.non_fungible().update_data(receipt_nft_data));
242+
return Some((funds, proposal_receipt));
243+
244+
} else if stage == 2 && receipt_nft_data.stage2_funded == false{
245+
info!("Here are your stage 2 funds");
246+
let funds = self.grant_vault.take((amount/10)*2);
247+
receipt_nft_data.stage2_funded = true;
248+
self.admin_badge_vault.authorize(|| proposal_receipt.non_fungible().update_data(receipt_nft_data));
249+
return Some((funds, proposal_receipt));
250+
251+
} else if stage == 3 && receipt_nft_data.stage3_funded == false{
252+
info!("Here are your stage 3 funds");
253+
let funds = self.grant_vault.take((amount/10)*3);
254+
receipt_nft_data.stage3_funded = true;
255+
self.admin_badge_vault.authorize(|| proposal_receipt.non_fungible().update_data(receipt_nft_data));
256+
return Some((funds, proposal_receipt));
257+
258+
} else if stage == 4 && receipt_nft_data.stage4_funded == false{
259+
info!("Here are your stage 4 funds");
260+
let funds = self.grant_vault.take((amount/10)*3);
261+
receipt_nft_data.stage4_funded = true;
262+
self.admin_badge_vault.authorize(|| proposal_receipt.non_fungible().update_data(receipt_nft_data));
263+
return Some((funds, proposal_receipt));
264+
265+
} else {
266+
info!("Invalid Receipt");
267+
return None;
268+
};
269+
}
270+
}
271+
}

0 commit comments

Comments
 (0)