Skip to content

Commit 56ce7f2

Browse files
Merge pull request #421 from AbdulSnk/feature/asset-tokenization-fractional-ownership
feat(assets): implement asset tokenization and fractional ownership c…
2 parents e227b3b + 3359c95 commit 56ce7f2

15 files changed

Lines changed: 2801 additions & 92 deletions
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
use crate::error::Error;
2+
use crate::types::{ContractEvent, DetokenizationProposal, TokenDataKey, TokenizedAsset};
3+
use crate::voting;
4+
use soroban_sdk::{Address, BigInt, Env};
5+
6+
/// Propose detokenization (requires voting)
7+
pub fn propose_detokenization(
8+
env: &Env,
9+
asset_id: u64,
10+
proposer: Address,
11+
) -> Result<u64, Error> {
12+
let store = env.storage().persistent();
13+
14+
// Verify asset is tokenized
15+
let key = TokenDataKey::TokenizedAsset(asset_id);
16+
let _: TokenizedAsset = store
17+
.get(&key)
18+
.ok_or(Error::AssetNotTokenized)?
19+
.ok_or(Error::AssetNotTokenized)?;
20+
21+
// Check if proposal already exists
22+
let proposal_key = TokenDataKey::DetokenizationProposal(asset_id);
23+
if store.has(&proposal_key) {
24+
if let Some(Some(DetokenizationProposal::Active { .. })) = store.get(&proposal_key) {
25+
return Err(Error::DetokenizationAlreadyProposed);
26+
}
27+
}
28+
29+
// Create proposal
30+
let proposal_id = asset_id; // Use asset_id as proposal_id for simplicity
31+
let timestamp = env.ledger().timestamp();
32+
33+
let proposal = DetokenizationProposal::Active {
34+
proposal_id,
35+
proposer,
36+
created_at: timestamp,
37+
};
38+
39+
store.set(&proposal_key, &proposal);
40+
41+
Ok(proposal_id)
42+
}
43+
44+
/// Execute detokenization if vote passed
45+
pub fn execute_detokenization(
46+
env: &Env,
47+
asset_id: u64,
48+
proposal_id: u64,
49+
) -> Result<(), Error> {
50+
let store = env.storage().persistent();
51+
52+
// Verify asset is tokenized
53+
let key = TokenDataKey::TokenizedAsset(asset_id);
54+
let mut tokenized_asset: TokenizedAsset = store
55+
.get(&key)
56+
.ok_or(Error::AssetNotTokenized)?
57+
.ok_or(Error::AssetNotTokenized)?;
58+
59+
// Check if proposal is active
60+
let proposal_key = TokenDataKey::DetokenizationProposal(asset_id);
61+
match store.get(&proposal_key) {
62+
Some(Some(DetokenizationProposal::Active { .. })) => {
63+
// Continue
64+
}
65+
_ => {
66+
return Err(Error::InvalidProposal);
67+
}
68+
}
69+
70+
// Check if proposal passed (>50% votes)
71+
let passed = voting::proposal_passed(env, asset_id, proposal_id)?;
72+
if !passed {
73+
return Err(Error::DetokenizationNotApproved);
74+
}
75+
76+
// Update proposal to executed
77+
let timestamp = env.ledger().timestamp();
78+
let executed_proposal = DetokenizationProposal::Executed {
79+
proposal_id,
80+
executed_at: timestamp,
81+
};
82+
store.set(&proposal_key, &executed_proposal);
83+
84+
// Clear all votes
85+
voting::clear_proposal_votes(env, asset_id, proposal_id)?;
86+
87+
// Emit event
88+
env.events().publish(
89+
("detokenization", "asset_detokenized"),
90+
ContractEvent::AssetDetokenized {
91+
asset_id,
92+
proposal_id,
93+
},
94+
);
95+
96+
Ok(())
97+
}
98+
99+
/// Reject detokenization proposal
100+
pub fn reject_detokenization(env: &Env, asset_id: u64) -> Result<(), Error> {
101+
let store = env.storage().persistent();
102+
103+
// Verify asset is tokenized
104+
let key = TokenDataKey::TokenizedAsset(asset_id);
105+
let _: TokenizedAsset = store
106+
.get(&key)
107+
.ok_or(Error::AssetNotTokenized)?
108+
.ok_or(Error::AssetNotTokenized)?;
109+
110+
// Get proposal
111+
let proposal_key = TokenDataKey::DetokenizationProposal(asset_id);
112+
let proposal: DetokenizationProposal = store
113+
.get(&proposal_key)
114+
.ok_or(Error::InvalidProposal)?
115+
.ok_or(Error::InvalidProposal)?;
116+
117+
match proposal {
118+
DetokenizationProposal::Active {
119+
proposal_id,
120+
..
121+
} => {
122+
// Mark as rejected
123+
let timestamp = env.ledger().timestamp();
124+
let rejected_proposal = DetokenizationProposal::Rejected {
125+
proposal_id,
126+
rejected_at: timestamp,
127+
};
128+
store.set(&proposal_key, &rejected_proposal);
129+
130+
// Clear votes
131+
voting::clear_proposal_votes(env, asset_id, proposal_id)?;
132+
133+
Ok(())
134+
}
135+
_ => Err(Error::InvalidProposal),
136+
}
137+
}
138+
139+
/// Get detokenization proposal status
140+
pub fn get_detokenization_proposal(
141+
env: &Env,
142+
asset_id: u64,
143+
) -> Result<DetokenizationProposal, Error> {
144+
let store = env.storage().persistent();
145+
146+
let key = TokenDataKey::DetokenizationProposal(asset_id);
147+
store
148+
.get(&key)
149+
.ok_or(Error::InvalidProposal)?
150+
.ok_or(Error::InvalidProposal)
151+
}
152+
153+
/// Check if detokenization is in progress
154+
pub fn is_detokenization_active(env: &Env, asset_id: u64) -> Result<bool, Error> {
155+
let store = env.storage().persistent();
156+
157+
let key = TokenDataKey::DetokenizationProposal(asset_id);
158+
match store.get(&key) {
159+
Some(Some(DetokenizationProposal::Active { .. })) => Ok(true),
160+
_ => Ok(false),
161+
}
162+
}

0 commit comments

Comments
 (0)