1use sha2::{Digest, Sha256};
2
3pub fn compute_discriminator(name: &str) -> [u8; 8] {
6 let mut hasher = Sha256::new();
7 hasher.update(format!("global:{name}"));
8 let hash = hasher.finalize();
9 let mut disc = [0u8; 8];
10 disc.copy_from_slice(&hash[..8]);
11 disc
12}
13
14macro_rules! disc {
15 ($name:ident, $ix:expr) => {
16 pub static $name: [u8; 8] = {
17 const PREIMAGE: &[u8] = concat!("global:", $ix).as_bytes();
18 sha256_first8(PREIMAGE)
19 };
20 };
21}
22
23const SHA256_K: [u32; 64] = [
24 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
25 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
26 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
27 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
28 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
29 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
30 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
31 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
32];
33
34const fn compress_block(h: [u32; 8], block: [u8; 64]) -> [u32; 8] {
37 let mut w = [0u32; 64];
38 let mut i = 0;
39 while i < 16 {
40 w[i] = ((block[i * 4] as u32) << 24)
41 | ((block[i * 4 + 1] as u32) << 16)
42 | ((block[i * 4 + 2] as u32) << 8)
43 | (block[i * 4 + 3] as u32);
44 i += 1;
45 }
46 i = 16;
47 while i < 64 {
48 let s0 = w[i - 15].rotate_right(7) ^ w[i - 15].rotate_right(18) ^ (w[i - 15] >> 3);
49 let s1 = w[i - 2].rotate_right(17) ^ w[i - 2].rotate_right(19) ^ (w[i - 2] >> 10);
50 w[i] = w[i - 16]
51 .wrapping_add(s0)
52 .wrapping_add(w[i - 7])
53 .wrapping_add(s1);
54 i += 1;
55 }
56
57 let (mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut hh) =
58 (h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7]);
59
60 i = 0;
61 while i < 64 {
62 let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
63 let ch = (e & f) ^ ((!e) & g);
64 let temp1 = hh
65 .wrapping_add(s1)
66 .wrapping_add(ch)
67 .wrapping_add(SHA256_K[i])
68 .wrapping_add(w[i]);
69 let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
70 let maj = (a & b) ^ (a & c) ^ (b & c);
71 let temp2 = s0.wrapping_add(maj);
72
73 hh = g;
74 g = f;
75 f = e;
76 e = d.wrapping_add(temp1);
77 d = c;
78 c = b;
79 b = a;
80 a = temp1.wrapping_add(temp2);
81 i += 1;
82 }
83
84 [
85 h[0].wrapping_add(a),
86 h[1].wrapping_add(b),
87 h[2].wrapping_add(c),
88 h[3].wrapping_add(d),
89 h[4].wrapping_add(e),
90 h[5].wrapping_add(f),
91 h[6].wrapping_add(g),
92 h[7].wrapping_add(hh),
93 ]
94}
95
96pub const fn sha256_first8(msg: &[u8]) -> [u8; 8] {
99 let h: [u32; 8] = [
100 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab,
101 0x5be0cd19,
102 ];
103
104 let bit_len = (msg.len() as u64) * 8;
105 let padded_len = if msg.len() + 9 <= 64 { 64 } else { 128 };
106
107 let mut padded = [0u8; 128];
108 let mut i = 0;
109 while i < msg.len() {
110 padded[i] = msg[i];
111 i += 1;
112 }
113 padded[msg.len()] = 0x80;
114 padded[padded_len - 8] = (bit_len >> 56) as u8;
115 padded[padded_len - 7] = (bit_len >> 48) as u8;
116 padded[padded_len - 6] = (bit_len >> 40) as u8;
117 padded[padded_len - 5] = (bit_len >> 32) as u8;
118 padded[padded_len - 4] = (bit_len >> 24) as u8;
119 padded[padded_len - 3] = (bit_len >> 16) as u8;
120 padded[padded_len - 2] = (bit_len >> 8) as u8;
121 padded[padded_len - 1] = bit_len as u8;
122
123 let mut block0 = [0u8; 64];
125 i = 0;
126 while i < 64 {
127 block0[i] = padded[i];
128 i += 1;
129 }
130 let h = compress_block(h, block0);
131
132 let h = if padded_len > 64 {
134 let mut block1 = [0u8; 64];
135 i = 0;
136 while i < 64 {
137 block1[i] = padded[64 + i];
138 i += 1;
139 }
140 compress_block(h, block1)
141 } else {
142 h
143 };
144
145 [
146 (h[0] >> 24) as u8,
147 (h[0] >> 16) as u8,
148 (h[0] >> 8) as u8,
149 h[0] as u8,
150 (h[1] >> 24) as u8,
151 (h[1] >> 16) as u8,
152 (h[1] >> 8) as u8,
153 h[1] as u8,
154 ]
155}
156
157disc!(REFRESH_RESERVE, "refresh_reserve");
159disc!(REFRESH_RESERVES_BATCH, "refresh_reserves_batch");
160disc!(REFRESH_OBLIGATION, "refresh_obligation");
161
162disc!(DEPOSIT_RESERVE_LIQUIDITY, "deposit_reserve_liquidity");
164disc!(
165 DEPOSIT_OBLIGATION_COLLATERAL_V2,
166 "deposit_obligation_collateral_v2"
167);
168disc!(
169 DEPOSIT_RESERVE_LIQUIDITY_AND_OBLIGATION_COLLATERAL_V2,
170 "deposit_reserve_liquidity_and_obligation_collateral_v2"
171);
172
173disc!(REDEEM_RESERVE_COLLATERAL, "redeem_reserve_collateral");
175disc!(
176 WITHDRAW_OBLIGATION_COLLATERAL_V2,
177 "withdraw_obligation_collateral_v2"
178);
179disc!(
180 WITHDRAW_OBLIGATION_COLLATERAL_AND_REDEEM_RESERVE_COLLATERAL_V2,
181 "withdraw_obligation_collateral_and_redeem_reserve_collateral_v2"
182);
183
184disc!(
186 BORROW_OBLIGATION_LIQUIDITY_V2,
187 "borrow_obligation_liquidity_v2"
188);
189
190disc!(
192 REPAY_OBLIGATION_LIQUIDITY_V2,
193 "repay_obligation_liquidity_v2"
194);
195disc!(
196 REPAY_AND_WITHDRAW_AND_REDEEM,
197 "repay_and_withdraw_and_redeem"
198);
199
200disc!(DEPOSIT_AND_WITHDRAW, "deposit_and_withdraw");
202
203disc!(
205 LIQUIDATE_OBLIGATION_AND_REDEEM_RESERVE_COLLATERAL_V2,
206 "liquidate_obligation_and_redeem_reserve_collateral_v2"
207);
208
209disc!(
211 FLASH_BORROW_RESERVE_LIQUIDITY,
212 "flash_borrow_reserve_liquidity"
213);
214disc!(
215 FLASH_REPAY_RESERVE_LIQUIDITY,
216 "flash_repay_reserve_liquidity"
217);
218
219disc!(INIT_OBLIGATION, "init_obligation");
221disc!(
222 INIT_OBLIGATION_FARMS_FOR_RESERVE,
223 "init_obligation_farms_for_reserve"
224);
225disc!(
226 REFRESH_OBLIGATION_FARMS_FOR_RESERVE,
227 "refresh_obligation_farms_for_reserve"
228);
229disc!(REQUEST_ELEVATION_GROUP, "request_elevation_group");
230
231disc!(SET_OBLIGATION_ORDER, "set_obligation_order");
233disc!(SET_BORROW_ORDER, "set_borrow_order");
234disc!(FILL_BORROW_ORDER, "fill_borrow_order");
235
236disc!(INIT_REFERRER_TOKEN_STATE, "init_referrer_token_state");
238disc!(INIT_USER_METADATA, "init_user_metadata");
239disc!(WITHDRAW_REFERRER_FEES, "withdraw_referrer_fees");
240disc!(
241 INIT_REFERRER_STATE_AND_SHORT_URL,
242 "init_referrer_state_and_short_url"
243);
244disc!(
245 DELETE_REFERRER_STATE_AND_SHORT_URL,
246 "delete_referrer_state_and_short_url"
247);
248
249disc!(ENQUEUE_TO_WITHDRAW, "enqueue_to_withdraw");
251disc!(WITHDRAW_QUEUED_LIQUIDITY, "withdraw_queued_liquidity");
252disc!(
253 RECOVER_INVALID_TICKET_COLLATERAL,
254 "recover_invalid_ticket_collateral"
255);
256disc!(CANCEL_WITHDRAW_TICKET, "cancel_withdraw_ticket");
257
258disc!(ROLLOVER_FIXED_TERM_BORROW, "rollover_fixed_term_borrow");
260disc!(UPDATE_OBLIGATION_CONFIG, "update_obligation_config");
261
262disc!(CLONE_RESERVE_CONFIG, "clone_reserve_config");
264
265#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
267#[non_exhaustive]
268pub enum KlendInstruction {
269 RefreshReserve,
270 RefreshReservesBatch,
271 RefreshObligation,
272 DepositReserveLiquidity,
273 DepositObligationCollateralV2,
274 DepositReserveLiquidityAndObligationCollateralV2,
275 RedeemReserveCollateral,
276 WithdrawObligationCollateralV2,
277 WithdrawObligationCollateralAndRedeemReserveCollateralV2,
278 BorrowObligationLiquidityV2,
279 RepayObligationLiquidityV2,
280 RepayAndWithdrawAndRedeem,
281 DepositAndWithdraw,
282 LiquidateObligationAndRedeemReserveCollateralV2,
283 FlashBorrowReserveLiquidity,
284 FlashRepayReserveLiquidity,
285 InitObligation,
286 InitObligationFarmsForReserve,
287 RefreshObligationFarmsForReserve,
288 RequestElevationGroup,
289 SetObligationOrder,
290 SetBorrowOrder,
291 FillBorrowOrder,
292 InitReferrerTokenState,
293 InitUserMetadata,
294 WithdrawReferrerFees,
295 InitReferrerStateAndShortUrl,
296 DeleteReferrerStateAndShortUrl,
297 EnqueueToWithdraw,
298 WithdrawQueuedLiquidity,
299 RecoverInvalidTicketCollateral,
300 CancelWithdrawTicket,
301 RolloverFixedTermBorrow,
302 UpdateObligationConfig,
303 CloneReserveConfig,
304}
305
306impl core::fmt::Display for KlendInstruction {
307 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
308 write!(f, "{self:?}")
309 }
310}
311
312pub fn identify_instruction(data: &[u8]) -> Option<KlendInstruction> {
317 if data.len() < 8 {
318 return None;
319 }
320 let mut disc = [0u8; 8];
321 disc.copy_from_slice(&data[..8]);
322
323 match disc {
324 d if d == REFRESH_RESERVE => Some(KlendInstruction::RefreshReserve),
325 d if d == REFRESH_RESERVES_BATCH => Some(KlendInstruction::RefreshReservesBatch),
326 d if d == REFRESH_OBLIGATION => Some(KlendInstruction::RefreshObligation),
327 d if d == DEPOSIT_RESERVE_LIQUIDITY => Some(KlendInstruction::DepositReserveLiquidity),
328 d if d == DEPOSIT_OBLIGATION_COLLATERAL_V2 => {
329 Some(KlendInstruction::DepositObligationCollateralV2)
330 }
331 d if d == DEPOSIT_RESERVE_LIQUIDITY_AND_OBLIGATION_COLLATERAL_V2 => {
332 Some(KlendInstruction::DepositReserveLiquidityAndObligationCollateralV2)
333 }
334 d if d == REDEEM_RESERVE_COLLATERAL => Some(KlendInstruction::RedeemReserveCollateral),
335 d if d == WITHDRAW_OBLIGATION_COLLATERAL_V2 => {
336 Some(KlendInstruction::WithdrawObligationCollateralV2)
337 }
338 d if d == WITHDRAW_OBLIGATION_COLLATERAL_AND_REDEEM_RESERVE_COLLATERAL_V2 => {
339 Some(KlendInstruction::WithdrawObligationCollateralAndRedeemReserveCollateralV2)
340 }
341 d if d == BORROW_OBLIGATION_LIQUIDITY_V2 => {
342 Some(KlendInstruction::BorrowObligationLiquidityV2)
343 }
344 d if d == REPAY_OBLIGATION_LIQUIDITY_V2 => {
345 Some(KlendInstruction::RepayObligationLiquidityV2)
346 }
347 d if d == REPAY_AND_WITHDRAW_AND_REDEEM => {
348 Some(KlendInstruction::RepayAndWithdrawAndRedeem)
349 }
350 d if d == DEPOSIT_AND_WITHDRAW => Some(KlendInstruction::DepositAndWithdraw),
351 d if d == LIQUIDATE_OBLIGATION_AND_REDEEM_RESERVE_COLLATERAL_V2 => {
352 Some(KlendInstruction::LiquidateObligationAndRedeemReserveCollateralV2)
353 }
354 d if d == FLASH_BORROW_RESERVE_LIQUIDITY => {
355 Some(KlendInstruction::FlashBorrowReserveLiquidity)
356 }
357 d if d == FLASH_REPAY_RESERVE_LIQUIDITY => {
358 Some(KlendInstruction::FlashRepayReserveLiquidity)
359 }
360 d if d == INIT_OBLIGATION => Some(KlendInstruction::InitObligation),
361 d if d == INIT_OBLIGATION_FARMS_FOR_RESERVE => {
362 Some(KlendInstruction::InitObligationFarmsForReserve)
363 }
364 d if d == REFRESH_OBLIGATION_FARMS_FOR_RESERVE => {
365 Some(KlendInstruction::RefreshObligationFarmsForReserve)
366 }
367 d if d == REQUEST_ELEVATION_GROUP => Some(KlendInstruction::RequestElevationGroup),
368 d if d == SET_OBLIGATION_ORDER => Some(KlendInstruction::SetObligationOrder),
369 d if d == SET_BORROW_ORDER => Some(KlendInstruction::SetBorrowOrder),
370 d if d == FILL_BORROW_ORDER => Some(KlendInstruction::FillBorrowOrder),
371 d if d == INIT_REFERRER_TOKEN_STATE => Some(KlendInstruction::InitReferrerTokenState),
372 d if d == INIT_USER_METADATA => Some(KlendInstruction::InitUserMetadata),
373 d if d == WITHDRAW_REFERRER_FEES => Some(KlendInstruction::WithdrawReferrerFees),
374 d if d == INIT_REFERRER_STATE_AND_SHORT_URL => {
375 Some(KlendInstruction::InitReferrerStateAndShortUrl)
376 }
377 d if d == DELETE_REFERRER_STATE_AND_SHORT_URL => {
378 Some(KlendInstruction::DeleteReferrerStateAndShortUrl)
379 }
380 d if d == ENQUEUE_TO_WITHDRAW => Some(KlendInstruction::EnqueueToWithdraw),
381 d if d == WITHDRAW_QUEUED_LIQUIDITY => Some(KlendInstruction::WithdrawQueuedLiquidity),
382 d if d == RECOVER_INVALID_TICKET_COLLATERAL => {
383 Some(KlendInstruction::RecoverInvalidTicketCollateral)
384 }
385 d if d == CANCEL_WITHDRAW_TICKET => Some(KlendInstruction::CancelWithdrawTicket),
386 d if d == ROLLOVER_FIXED_TERM_BORROW => Some(KlendInstruction::RolloverFixedTermBorrow),
387 d if d == UPDATE_OBLIGATION_CONFIG => Some(KlendInstruction::UpdateObligationConfig),
388 d if d == CLONE_RESERVE_CONFIG => Some(KlendInstruction::CloneReserveConfig),
389 _ => None,
390 }
391}
392
393#[cfg(test)]
394mod tests {
395 use super::*;
396
397 macro_rules! check_disc {
398 ($name:expr, $constant:ident) => {
399 assert_eq!(
400 compute_discriminator($name),
401 $constant,
402 "Discriminator mismatch for {}",
403 $name
404 );
405 };
406 }
407
408 #[test]
409 fn verify_all_discriminators() {
410 check_disc!("refresh_reserve", REFRESH_RESERVE);
411 check_disc!("refresh_reserves_batch", REFRESH_RESERVES_BATCH);
412 check_disc!("refresh_obligation", REFRESH_OBLIGATION);
413 check_disc!("deposit_reserve_liquidity", DEPOSIT_RESERVE_LIQUIDITY);
414 check_disc!(
415 "deposit_obligation_collateral_v2",
416 DEPOSIT_OBLIGATION_COLLATERAL_V2
417 );
418 check_disc!(
419 "deposit_reserve_liquidity_and_obligation_collateral_v2",
420 DEPOSIT_RESERVE_LIQUIDITY_AND_OBLIGATION_COLLATERAL_V2
421 );
422 check_disc!("redeem_reserve_collateral", REDEEM_RESERVE_COLLATERAL);
423 check_disc!(
424 "withdraw_obligation_collateral_v2",
425 WITHDRAW_OBLIGATION_COLLATERAL_V2
426 );
427 check_disc!(
428 "withdraw_obligation_collateral_and_redeem_reserve_collateral_v2",
429 WITHDRAW_OBLIGATION_COLLATERAL_AND_REDEEM_RESERVE_COLLATERAL_V2
430 );
431 check_disc!(
432 "borrow_obligation_liquidity_v2",
433 BORROW_OBLIGATION_LIQUIDITY_V2
434 );
435 check_disc!(
436 "repay_obligation_liquidity_v2",
437 REPAY_OBLIGATION_LIQUIDITY_V2
438 );
439 check_disc!(
440 "repay_and_withdraw_and_redeem",
441 REPAY_AND_WITHDRAW_AND_REDEEM
442 );
443 check_disc!("deposit_and_withdraw", DEPOSIT_AND_WITHDRAW);
444 check_disc!(
445 "liquidate_obligation_and_redeem_reserve_collateral_v2",
446 LIQUIDATE_OBLIGATION_AND_REDEEM_RESERVE_COLLATERAL_V2
447 );
448 check_disc!(
449 "flash_borrow_reserve_liquidity",
450 FLASH_BORROW_RESERVE_LIQUIDITY
451 );
452 check_disc!(
453 "flash_repay_reserve_liquidity",
454 FLASH_REPAY_RESERVE_LIQUIDITY
455 );
456 check_disc!("init_obligation", INIT_OBLIGATION);
457 check_disc!(
458 "init_obligation_farms_for_reserve",
459 INIT_OBLIGATION_FARMS_FOR_RESERVE
460 );
461 check_disc!(
462 "refresh_obligation_farms_for_reserve",
463 REFRESH_OBLIGATION_FARMS_FOR_RESERVE
464 );
465 check_disc!("request_elevation_group", REQUEST_ELEVATION_GROUP);
466 check_disc!("set_obligation_order", SET_OBLIGATION_ORDER);
467 check_disc!("set_borrow_order", SET_BORROW_ORDER);
468 check_disc!("fill_borrow_order", FILL_BORROW_ORDER);
469 check_disc!("init_referrer_token_state", INIT_REFERRER_TOKEN_STATE);
470 check_disc!("init_user_metadata", INIT_USER_METADATA);
471 check_disc!("withdraw_referrer_fees", WITHDRAW_REFERRER_FEES);
472 check_disc!(
473 "init_referrer_state_and_short_url",
474 INIT_REFERRER_STATE_AND_SHORT_URL
475 );
476 check_disc!(
477 "delete_referrer_state_and_short_url",
478 DELETE_REFERRER_STATE_AND_SHORT_URL
479 );
480 check_disc!("enqueue_to_withdraw", ENQUEUE_TO_WITHDRAW);
481 check_disc!("withdraw_queued_liquidity", WITHDRAW_QUEUED_LIQUIDITY);
482 check_disc!(
483 "recover_invalid_ticket_collateral",
484 RECOVER_INVALID_TICKET_COLLATERAL
485 );
486 check_disc!("cancel_withdraw_ticket", CANCEL_WITHDRAW_TICKET);
487 check_disc!("rollover_fixed_term_borrow", ROLLOVER_FIXED_TERM_BORROW);
488 check_disc!("update_obligation_config", UPDATE_OBLIGATION_CONFIG);
489 check_disc!("clone_reserve_config", CLONE_RESERVE_CONFIG);
490 }
491}