Skip to content

Commit 94606ab

Browse files
Merge pull request #411 from Divineifed1/val
implementation of asset depreciatiuon and valuation
2 parents dad1d09 + d3b3b6f commit 94606ab

File tree

1 file changed

+289
-0
lines changed

1 file changed

+289
-0
lines changed
Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
#![no_std]
2+
use soroban_sdk::{contract, contractimpl, contracttype, Address, BytesN, Env, Map, Vec, Symbol};
3+
4+
// -----------------------------
5+
// DATA STRUCTURES
6+
// -----------------------------
7+
8+
#[contracttype]
9+
#[derive(Clone)]
10+
pub enum DepreciationMethod {
11+
StraightLine,
12+
DecliningBalance,
13+
DoubleDecliningBalance,
14+
UnitsOfProduction,
15+
SumOfYearsDigits,
16+
}
17+
18+
#[contracttype]
19+
#[derive(Clone)]
20+
pub enum ValuationMethod {
21+
Market,
22+
Appraisal,
23+
Calculated,
24+
Oracle,
25+
}
26+
27+
#[contracttype]
28+
#[derive(Clone)]
29+
pub struct ValuationRecord {
30+
pub valuation_id: u64,
31+
pub asset_id: u64,
32+
pub valuation_date: u64,
33+
pub purchase_price: i128,
34+
pub market_value: i128,
35+
pub book_value: i128,
36+
pub delta: i128,
37+
pub method: ValuationMethod,
38+
pub valuator: Address,
39+
pub confidence: u32,
40+
pub doc_hash: BytesN<32>,
41+
pub notes: Symbol,
42+
pub timestamp: u64,
43+
}
44+
45+
#[contracttype]
46+
#[derive(Clone)]
47+
pub struct DepreciationSchedule {
48+
pub asset_id: u64,
49+
pub purchase_date: u64,
50+
pub purchase_price: i128,
51+
pub salvage_value: i128,
52+
pub useful_life_months: u32,
53+
pub method: DepreciationMethod,
54+
pub rate: u32,
55+
pub accumulated: i128,
56+
pub book_value: i128,
57+
pub last_calc: u64,
58+
pub next_calc: u64,
59+
pub auto_calc: bool,
60+
}
61+
62+
#[contracttype]
63+
#[derive(Clone)]
64+
pub struct DepreciationEntry {
65+
pub entry_id: u64,
66+
pub asset_id: u64,
67+
pub period: u64,
68+
pub opening_value: i128,
69+
pub depreciation: i128,
70+
pub accumulated: i128,
71+
pub closing_value: i128,
72+
pub method: DepreciationMethod,
73+
pub timestamp: u64,
74+
}
75+
76+
#[contracttype]
77+
#[derive(Clone)]
78+
pub struct ValuationOracle {
79+
pub oracle: Address,
80+
pub name: Symbol,
81+
pub accuracy: u32,
82+
pub total_vals: u64,
83+
pub active: bool,
84+
pub registered_at: u64,
85+
}
86+
87+
#[contracttype]
88+
pub enum DataKey {
89+
Admin,
90+
AssetRegistry,
91+
Schedules(u64),
92+
DepreciationHistory(u64),
93+
Valuations(u64),
94+
Oracle(Address),
95+
ValuationCounter,
96+
DepreciationCounter,
97+
}
98+
99+
// -----------------------------
100+
// CONTRACT
101+
// -----------------------------
102+
103+
#[contract]
104+
pub struct AssetDepreciationContract;
105+
106+
#[contractimpl]
107+
impl AssetDepreciationContract {
108+
// -------------------------
109+
// INIT
110+
// -------------------------
111+
pub fn init(env: Env, admin: Address, asset_registry: Address) {
112+
admin.require_auth();
113+
env.storage().instance().set(&DataKey::Admin, &admin);
114+
env.storage().instance().set(&DataKey::AssetRegistry, &asset_registry);
115+
env.storage().instance().set(&DataKey::ValuationCounter, &0u64);
116+
env.storage().instance().set(&DataKey::DepreciationCounter, &0u64);
117+
}
118+
119+
// -------------------------
120+
// ACCESS HELPERS
121+
// -------------------------
122+
fn assert_admin(env: &Env) {
123+
let admin: Address = env.storage().instance().get(&DataKey::Admin).unwrap();
124+
admin.require_auth();
125+
}
126+
127+
// -------------------------
128+
// ORACLES
129+
// -------------------------
130+
pub fn register_oracle(env: Env, oracle: Address, name: Symbol, accuracy: u32) {
131+
Self::assert_admin(&env);
132+
let data = ValuationOracle {
133+
oracle: oracle.clone(),
134+
name,
135+
accuracy,
136+
total_vals: 0,
137+
active: true,
138+
registered_at: env.ledger().timestamp(),
139+
};
140+
env.storage().persistent().set(&DataKey::Oracle(oracle), &data);
141+
}
142+
143+
// -------------------------
144+
// DEPRECIATION SCHEDULE
145+
// -------------------------
146+
pub fn set_schedule(env: Env, schedule: DepreciationSchedule) {
147+
if schedule.purchase_price <= 0 { panic!("Invalid price") }
148+
if schedule.salvage_value >= schedule.purchase_price { panic!("Invalid salvage") }
149+
env.storage().persistent().set(&DataKey::Schedules(schedule.asset_id), &schedule);
150+
}
151+
152+
// -------------------------
153+
// CALCULATION CORE
154+
// -------------------------
155+
pub fn calculate_depreciation(env: Env, asset_id: u64, period_ts: u64) -> i128 {
156+
let mut schedule: DepreciationSchedule = env
157+
.storage()
158+
.persistent()
159+
.get(&DataKey::Schedules(asset_id))
160+
.unwrap();
161+
162+
if schedule.book_value <= schedule.salvage_value {
163+
return 0;
164+
}
165+
166+
let dep = match schedule.method {
167+
DepreciationMethod::StraightLine => {
168+
(schedule.purchase_price - schedule.salvage_value)
169+
/ schedule.useful_life_months as i128
170+
}
171+
DepreciationMethod::DecliningBalance => {
172+
schedule.book_value * schedule.rate as i128 / 100
173+
}
174+
DepreciationMethod::DoubleDecliningBalance => {
175+
let rate = 2 * 100 / schedule.useful_life_months as i128;
176+
schedule.book_value * rate / 100
177+
}
178+
_ => 0,
179+
};
180+
181+
let depreciation = core::cmp::min(dep, schedule.book_value - schedule.salvage_value);
182+
183+
schedule.accumulated += depreciation;
184+
schedule.book_value -= depreciation;
185+
schedule.last_calc = period_ts;
186+
env.storage().persistent().set(&DataKey::Schedules(asset_id), &schedule);
187+
188+
let mut counter: u64 = env.storage().instance().get(&DataKey::DepreciationCounter).unwrap();
189+
counter += 1;
190+
env.storage().instance().set(&DataKey::DepreciationCounter, &counter);
191+
192+
let entry = DepreciationEntry {
193+
entry_id: counter,
194+
asset_id,
195+
period: period_ts,
196+
opening_value: schedule.book_value + depreciation,
197+
depreciation,
198+
accumulated: schedule.accumulated,
199+
closing_value: schedule.book_value,
200+
method: schedule.method.clone(),
201+
timestamp: env.ledger().timestamp(),
202+
};
203+
204+
let mut history: Vec<DepreciationEntry> = env
205+
.storage()
206+
.persistent()
207+
.get(&DataKey::DepreciationHistory(asset_id))
208+
.unwrap_or(Vec::new(&env));
209+
history.push_back(entry);
210+
env.storage().persistent().set(&DataKey::DepreciationHistory(asset_id), &history);
211+
212+
depreciation
213+
}
214+
215+
// -------------------------
216+
// VALUATION
217+
// -------------------------
218+
pub fn record_valuation(
219+
env: Env,
220+
asset_id: u64,
221+
market_value: i128,
222+
method: ValuationMethod,
223+
confidence: u32,
224+
doc_hash: BytesN<32>,
225+
notes: Symbol,
226+
) {
227+
if confidence > 100 { panic!("Invalid confidence") }
228+
229+
let mut counter: u64 = env.storage().instance().get(&DataKey::ValuationCounter).unwrap();
230+
counter += 1;
231+
env.storage().instance().set(&DataKey::ValuationCounter, &counter);
232+
233+
let schedule: DepreciationSchedule = env
234+
.storage()
235+
.persistent()
236+
.get(&DataKey::Schedules(asset_id))
237+
.unwrap();
238+
239+
let record = ValuationRecord {
240+
valuation_id: counter,
241+
asset_id,
242+
valuation_date: env.ledger().timestamp(),
243+
purchase_price: schedule.purchase_price,
244+
market_value,
245+
book_value: schedule.book_value,
246+
delta: market_value - schedule.book_value,
247+
method,
248+
valuator: env.invoker(),
249+
confidence,
250+
doc_hash,
251+
notes,
252+
timestamp: env.ledger().timestamp(),
253+
};
254+
255+
let mut vals: Vec<ValuationRecord> = env
256+
.storage()
257+
.persistent()
258+
.get(&DataKey::Valuations(asset_id))
259+
.unwrap_or(Vec::new(&env));
260+
vals.push_back(record);
261+
env.storage().persistent().set(&DataKey::Valuations(asset_id), &vals);
262+
}
263+
264+
// -------------------------
265+
// QUERIES
266+
// -------------------------
267+
pub fn get_book_value(env: Env, asset_id: u64) -> i128 {
268+
let schedule: DepreciationSchedule = env
269+
.storage()
270+
.persistent()
271+
.get(&DataKey::Schedules(asset_id))
272+
.unwrap();
273+
schedule.book_value
274+
}
275+
276+
pub fn get_depreciation_history(env: Env, asset_id: u64) -> Vec<DepreciationEntry> {
277+
env.storage()
278+
.persistent()
279+
.get(&DataKey::DepreciationHistory(asset_id))
280+
.unwrap_or(Vec::new(&env))
281+
}
282+
283+
pub fn get_valuations(env: Env, asset_id: u64) -> Vec<ValuationRecord> {
284+
env.storage()
285+
.persistent()
286+
.get(&DataKey::Valuations(asset_id))
287+
.unwrap_or(Vec::new(&env))
288+
}
289+
}

0 commit comments

Comments
 (0)