forked from StellarSplit/StellarSplit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlib.rs
More file actions
197 lines (172 loc) · 6.59 KB
/
Copy pathlib.rs
File metadata and controls
197 lines (172 loc) · 6.59 KB
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
//! # Split Template Contract
//!
//! Manages reusable split templates that can be deployed across multiple splits.
//! Provides deterministic template ID generation, creator-based indexing, and
//! template application tracking.
#![no_std]
use soroban_sdk::{contract, contractimpl, Address, Env, String, Vec};
mod events;
mod storage;
mod types;
mod utils;
#[cfg(test)]
mod test;
pub use events::*;
pub use storage::*;
pub use types::*;
pub use utils::*;
/// The Split Template contract for managing reusable split configurations.
#[contract]
pub struct SplitTemplateContract;
#[contractimpl]
impl SplitTemplateContract {
/// Create a new split template with the given configuration.
///
/// Generates a deterministic template ID based on creator, name, and current ledger time.
/// Validates participants and shares according to the split type.
/// Stores the template and indexes it by creator.
///
/// # Arguments
/// * `env` - The Soroban environment
/// * `creator` - The address creating this template (must authorize)
/// * `name` - Human-readable name for the template
/// * `split_type` - How to divide funds (Equal, Percentage, or Fixed)
/// * `participants` - List of participants and their share values
///
/// # Returns
/// The deterministic template ID (hex string) or an error
pub fn create_template(
env: Env,
creator: Address,
name: String,
split_type: SplitType,
participants: Vec<Participant>,
) -> Result<String, Error> {
// Require authorization from the creator
creator.require_auth();
// Validate that participants list is not empty
if participants.len() == 0 {
return Err(Error::InvalidParticipants);
}
// Validate shares based on split type
Self::validate_shares(&env, split_type, &participants)?;
// Generate deterministic template ID from creator + name + ledger time
let template_id = Self::generate_template_id(&env, &creator, &name);
// Create the template struct
let template = Template {
id: template_id.clone(),
creator: creator.clone(),
name,
split_type,
participants,
};
// Store the template
storage::store_template(&env, &template);
// Add to creator's index for efficient lookup
storage::add_to_creator_index(&env, &creator, template_id.clone());
// Emit event
events::emit_template_created(&env, template_id.clone(), creator, template.name.clone());
Ok(template_id)
}
/// Use an existing template to create a split (scaffolding).
///
/// Loads the template and emits an event linking the template to a new split.
/// No cross-contract call yet; this is the scaffold for future integration.
///
/// # Arguments
/// * `env` - The Soroban environment
/// * `template_id` - The ID of the template to use
/// * `split_id` - The ID of the new split being created
///
/// # Returns
/// Success or error if template not found
pub fn use_template(env: Env, template_id: String, split_id: String) -> Result<(), Error> {
// Load the template; fail if not found
storage::get_template(&env, &template_id).ok_or(Error::TemplateNotFound)?;
// Emit event linking template to split
events::emit_template_used(&env, template_id, split_id);
Ok(())
}
/// Get all templates created by a specific creator.
///
/// Reads the creator index and returns full template objects.
/// Returns empty vec if creator has no templates.
///
/// # Arguments
/// * `env` - The Soroban environment
/// * `creator` - The address to list templates for
///
/// # Returns
/// Vector of full Template objects for this creator
pub fn get_templates(env: Env, creator: Address) -> Vec<Template> {
// Get all template IDs for this creator
let template_ids = storage::get_creator_template_ids(&env, &creator);
// Load full template objects for each ID
let mut templates = Vec::new(&env);
for template_id in template_ids.iter() {
if let Some(template) = storage::get_template(&env, &template_id) {
templates.push_back(template);
}
}
templates
}
/// Get a single template by ID.
///
/// # Arguments
/// * `env` - The Soroban environment
/// * `template_id` - The template ID to retrieve
///
/// # Returns
/// The template if found, or an error
pub fn get_template(env: Env, template_id: String) -> Result<Template, Error> {
storage::get_template(&env, &template_id).ok_or(Error::TemplateNotFound)
}
// ============================================
// Private Helper Functions
// ============================================
/// Generate a deterministic template ID.
///
/// Creates a template ID from creator and name.
/// For simplicity, uses the name itself as the ID (must be unique per creator).
fn generate_template_id(_env: &Env, _creator: &Address, name: &String) -> String {
// Use the name itself as a simple, deterministic ID
// In production, could add timestamp/sequence for uniqueness
name.clone()
}
/// Validate participant shares based on split type.
fn validate_shares(
_env: &Env,
split_type: SplitType,
participants: &Vec<Participant>,
) -> Result<(), Error> {
match split_type {
SplitType::Equal => {
// For equal splits, shares must all be 1 (or not checked; we trust the caller)
Ok(())
}
SplitType::Percentage => {
// For percentage splits, all shares must be 0-100 and sum to 100
let mut total: i128 = 0;
for participant in participants.iter() {
if participant.share < 0 || participant.share > 100 {
return Err(Error::InvalidShares);
}
total += participant.share;
}
if total != 100 {
return Err(Error::InvalidShares);
}
Ok(())
}
SplitType::Fixed => {
// For fixed splits, all shares must be positive
for participant in participants.iter() {
if participant.share <= 0 {
return Err(Error::InvalidShares);
}
}
Ok(())
}
}
}
}