amadeus_runtime/consensus/bic/
lockup_prime.rs1use crate::consensus::bic::coin::{balance, to_flat};
2use crate::consensus::bic::epoch::TREASURY_DONATION_ADDRESS;
3use crate::consensus::bic::lockup::create_lock;
4use crate::consensus::consensus_apply::ApplyEnv;
5use crate::consensus::consensus_kv::{kv_delete, kv_get, kv_increment, kv_put};
6use crate::{Result, bcat};
7use amadeus_utils::misc::list_of_binaries_to_vecpak;
8
9fn init_prime_if_needed(env: &mut ApplyEnv) -> Result<()> {
11 use crate::consensus::bic::coin::exists;
12 if !exists(env, b"PRIME")? {
13 kv_increment(env, b"coin:PRIME:totalSupply", 0)?;
14
15 let v0: &[u8; 48] = &[
17 149, 216, 55, 255, 29, 8, 239, 251, 139, 112, 30, 29, 199, 57, 90, 67, 198, 220, 101, 18, 228, 100, 100,
18 241, 43, 213, 221, 230, 253, 58, 231, 1, 102, 166, 54, 66, 245, 148, 140, 44, 78, 56, 84, 12, 222, 205, 57,
19 210,
20 ];
21 let admin = vec![v0.to_vec()];
22 let term_admins = list_of_binaries_to_vecpak(admin);
23 kv_put(env, b"coin:PRIME:permission", &term_admins)?;
24
25 kv_put(env, b"coin:PRIME:mintable", b"true")?;
26 kv_put(env, b"coin:PRIME:pausable", b"true")?;
27 kv_put(env, b"coin:PRIME:soulbound", b"true")?;
28 }
29 Ok(())
30}
31
32fn mint_prime(env: &mut ApplyEnv, amount: i128, receiver: &[u8]) -> Result<()> {
34 if amount <= 0 {
35 return Err("invalid_amount");
36 }
37 kv_increment(env, &bcat(&[b"account:", receiver, b":balance:PRIME"]), amount)?;
38 kv_increment(env, b"coin:PRIME:totalSupply", amount)?;
39 Ok(())
40}
41
42pub fn call_lock(env: &mut ApplyEnv, args: Vec<Vec<u8>>) -> Result<()> {
44 init_prime_if_needed(env)?;
45
46 if args.len() != 2 {
47 return Err("invalid_args");
48 }
49 let amount = args[0].as_slice();
50 let amount = std::str::from_utf8(&amount).ok().and_then(|s| s.parse::<i128>().ok()).ok_or("invalid_amount")?;
51 let tier = args[1].as_slice();
52
53 let (tier_epochs, multiplier) = match tier {
54 b"magic" => (0, 1),
55 b"magic2" => (1, 1),
56 b"7d" => (10, 13),
57 b"30d" => (45, 17),
58 b"90d" => (135, 27),
59 b"180d" => (270, 35),
60 b"365d" => (547, 54),
61 _ => return Err("invalid_tier"),
62 };
63
64 if amount <= to_flat(1) {
65 return Err("invalid_amount");
66 }
67 if amount > balance(env, env.caller_env.account_caller.as_slice(), b"AMA")? {
68 return Err("insufficient_funds");
69 }
70 kv_increment(env, &bcat(&[b"account:", &env.caller_env.account_caller, b":balance:AMA"]), -amount)?;
71
72 let vault_index = kv_increment(env, b"bic:lockup_prime:unique_index", 1)?;
73 let vault_value = bcat(&[
74 tier,
75 b"-",
76 multiplier.to_string().as_bytes(),
77 b"-",
78 (env.caller_env.entry_epoch.saturating_add(tier_epochs)).to_string().as_bytes(),
79 b"-",
80 amount.to_string().as_bytes(),
81 ]);
82 kv_put(
83 env,
84 &bcat(&[b"bic:lockup_prime:vault:", &env.caller_env.account_caller, b":", vault_index.to_string().as_bytes()]),
85 &vault_value,
86 )?;
87 Ok(())
88}
89
90pub fn call_unlock(env: &mut ApplyEnv, args: Vec<Vec<u8>>) -> Result<()> {
92 if args.len() != 1 {
93 return Err("invalid_args");
94 }
95 let vault_index = args[0].as_slice();
96
97 let vault_key = bcat(&[b"bic:lockup_prime:vault:", &env.caller_env.account_caller, b":", vault_index]);
98
99 let vault = kv_get(env, &vault_key)?.ok_or("invalid_vault")?;
100
101 let vault_parts: Vec<Vec<u8>> = vault.split(|&b| b == b'-').map(|seg| seg.to_vec()).collect();
102 if vault_parts.len() < 4 {
103 return Err("invalid_vault_format");
104 }
105
106 let multiplier =
108 std::str::from_utf8(&vault_parts[1]).ok().and_then(|s| s.parse::<u64>().ok()).ok_or("invalid_multiplier")?;
109 let unlock_epoch =
110 std::str::from_utf8(&vault_parts[2]).ok().and_then(|s| s.parse::<u64>().ok()).ok_or("invalid_unlock_epoch")?;
111 let unlock_amount =
112 std::str::from_utf8(&vault_parts[3]).ok().and_then(|s| s.parse::<u64>().ok()).ok_or("invalid_unlock_amount")?;
113
114 if env.caller_env.entry_epoch < unlock_epoch {
115 let penalty = unlock_amount / 4;
117 let disbursement = unlock_amount - penalty;
118
119 kv_increment(
120 env,
121 &bcat(&[b"account:", TREASURY_DONATION_ADDRESS.as_slice(), b":balance:AMA"]),
122 penalty as i128,
123 )?;
124 create_lock(
126 env,
127 env.caller_env.account_caller.to_vec().as_slice(),
128 b"AMA",
129 disbursement as i128,
130 env.caller_env.entry_epoch.saturating_add(5),
131 )?;
132 } else {
133 let prime_points = unlock_amount * multiplier;
135 mint_prime(env, prime_points as i128, env.caller_env.account_caller.to_vec().as_slice())?;
136 kv_increment(
137 env,
138 &bcat(&[b"account:", &env.caller_env.account_caller, b":balance:AMA"]),
139 unlock_amount as i128,
140 )?;
141 }
142
143 kv_delete(env, &vault_key)?;
144 Ok(())
145}
146
147pub fn call_daily_checkin(env: &mut ApplyEnv, args: Vec<Vec<u8>>) -> Result<()> {
149 if args.len() != 1 {
150 return Err("invalid_args");
151 }
152 let vault_index = args[0].as_slice();
153
154 let vault_key = bcat(&[b"bic:lockup_prime:vault:", &env.caller_env.account_caller, b":", vault_index]);
155 let vault = kv_get(env, &vault_key)?.ok_or("invalid_vault")?;
156 let vault_parts: Vec<Vec<u8>> = vault.split(|&b| b == b'-').map(|seg| seg.to_vec()).collect();
157 if vault_parts.len() < 4 {
158 return Err("invalid_vault_format");
159 }
160
161 let unlock_amount =
162 std::str::from_utf8(&vault_parts[3]).ok().and_then(|s| s.parse::<u64>().ok()).ok_or("invalid_unlock_amount")?;
163
164 let next_checkin_key = bcat(&[b"bic:lockup_prime:next_checkin_epoch:", &env.caller_env.account_caller]);
165 let next_checkin_epoch: u64 = kv_get(env, &next_checkin_key)?
166 .map(|bytes| {
167 std::str::from_utf8(&bytes).ok().and_then(|s| s.parse::<u64>().ok()).unwrap_or(env.caller_env.entry_epoch)
168 })
169 .unwrap_or(env.caller_env.entry_epoch);
170
171 let delta = (env.caller_env.entry_epoch as i64) - (next_checkin_epoch as i64);
172 if delta == 0 || delta == 1 {
173 kv_put(env, &next_checkin_key, env.caller_env.entry_epoch.saturating_add(2).to_string().as_bytes())?;
174
175 let daily_bonus = unlock_amount / 100;
176 mint_prime(env, daily_bonus as i128, env.caller_env.account_caller.to_vec().as_slice())?;
177
178 let streak_key = bcat(&[b"bic:lockup_prime:daily_streak:", &env.caller_env.account_caller]);
179 let streak = kv_increment(env, &streak_key, 1)?;
180 if streak >= 30 {
181 kv_put(env, &streak_key, b"0")?;
182 let streak_bonus = daily_bonus * 30;
183 mint_prime(env, streak_bonus as i128, env.caller_env.account_caller.to_vec().as_slice())?;
184 }
185 } else if delta > 2 {
186 kv_put(env, &next_checkin_key, env.caller_env.entry_epoch.saturating_add(2).to_string().as_bytes())?;
188 let streak_key = bcat(&[b"bic:lockup_prime:daily_streak:", &env.caller_env.account_caller]);
189 kv_put(env, &streak_key, b"0")?;
190 }
191 Ok(())
194}