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
265disc!(
267 INITIATE_OBLIGATION_OWNERSHIP_TRANSFER,
268 "initiate_obligation_ownership_transfer"
269);
270disc!(
271 APPROVE_OBLIGATION_OWNERSHIP_TRANSFER,
272 "approve_obligation_ownership_transfer"
273);
274disc!(ACCEPT_OBLIGATION_OWNERSHIP, "accept_obligation_ownership");
275disc!(
276 ABORT_OBLIGATION_OWNERSHIP_TRANSFER,
277 "abort_obligation_ownership_transfer"
278);
279
280#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
282#[non_exhaustive]
283pub enum KlendInstruction {
284 RefreshReserve,
285 RefreshReservesBatch,
286 RefreshObligation,
287 DepositReserveLiquidity,
288 DepositObligationCollateralV2,
289 DepositReserveLiquidityAndObligationCollateralV2,
290 RedeemReserveCollateral,
291 WithdrawObligationCollateralV2,
292 WithdrawObligationCollateralAndRedeemReserveCollateralV2,
293 BorrowObligationLiquidityV2,
294 RepayObligationLiquidityV2,
295 RepayAndWithdrawAndRedeem,
296 DepositAndWithdraw,
297 LiquidateObligationAndRedeemReserveCollateralV2,
298 FlashBorrowReserveLiquidity,
299 FlashRepayReserveLiquidity,
300 InitObligation,
301 InitObligationFarmsForReserve,
302 RefreshObligationFarmsForReserve,
303 RequestElevationGroup,
304 SetObligationOrder,
305 SetBorrowOrder,
306 FillBorrowOrder,
307 InitReferrerTokenState,
308 InitUserMetadata,
309 WithdrawReferrerFees,
310 InitReferrerStateAndShortUrl,
311 DeleteReferrerStateAndShortUrl,
312 EnqueueToWithdraw,
313 WithdrawQueuedLiquidity,
314 RecoverInvalidTicketCollateral,
315 CancelWithdrawTicket,
316 RolloverFixedTermBorrow,
317 UpdateObligationConfig,
318 CloneReserveConfig,
319 InitiateObligationOwnershipTransfer,
320 ApproveObligationOwnershipTransfer,
321 AcceptObligationOwnership,
322 AbortObligationOwnershipTransfer,
323}
324
325impl core::fmt::Display for KlendInstruction {
326 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
327 write!(f, "{self:?}")
328 }
329}
330
331pub fn identify_instruction(data: &[u8]) -> Option<KlendInstruction> {
336 if data.len() < 8 {
337 return None;
338 }
339 let mut disc = [0u8; 8];
340 disc.copy_from_slice(&data[..8]);
341
342 match disc {
343 d if d == REFRESH_RESERVE => Some(KlendInstruction::RefreshReserve),
344 d if d == REFRESH_RESERVES_BATCH => Some(KlendInstruction::RefreshReservesBatch),
345 d if d == REFRESH_OBLIGATION => Some(KlendInstruction::RefreshObligation),
346 d if d == DEPOSIT_RESERVE_LIQUIDITY => Some(KlendInstruction::DepositReserveLiquidity),
347 d if d == DEPOSIT_OBLIGATION_COLLATERAL_V2 => {
348 Some(KlendInstruction::DepositObligationCollateralV2)
349 }
350 d if d == DEPOSIT_RESERVE_LIQUIDITY_AND_OBLIGATION_COLLATERAL_V2 => {
351 Some(KlendInstruction::DepositReserveLiquidityAndObligationCollateralV2)
352 }
353 d if d == REDEEM_RESERVE_COLLATERAL => Some(KlendInstruction::RedeemReserveCollateral),
354 d if d == WITHDRAW_OBLIGATION_COLLATERAL_V2 => {
355 Some(KlendInstruction::WithdrawObligationCollateralV2)
356 }
357 d if d == WITHDRAW_OBLIGATION_COLLATERAL_AND_REDEEM_RESERVE_COLLATERAL_V2 => {
358 Some(KlendInstruction::WithdrawObligationCollateralAndRedeemReserveCollateralV2)
359 }
360 d if d == BORROW_OBLIGATION_LIQUIDITY_V2 => {
361 Some(KlendInstruction::BorrowObligationLiquidityV2)
362 }
363 d if d == REPAY_OBLIGATION_LIQUIDITY_V2 => {
364 Some(KlendInstruction::RepayObligationLiquidityV2)
365 }
366 d if d == REPAY_AND_WITHDRAW_AND_REDEEM => {
367 Some(KlendInstruction::RepayAndWithdrawAndRedeem)
368 }
369 d if d == DEPOSIT_AND_WITHDRAW => Some(KlendInstruction::DepositAndWithdraw),
370 d if d == LIQUIDATE_OBLIGATION_AND_REDEEM_RESERVE_COLLATERAL_V2 => {
371 Some(KlendInstruction::LiquidateObligationAndRedeemReserveCollateralV2)
372 }
373 d if d == FLASH_BORROW_RESERVE_LIQUIDITY => {
374 Some(KlendInstruction::FlashBorrowReserveLiquidity)
375 }
376 d if d == FLASH_REPAY_RESERVE_LIQUIDITY => {
377 Some(KlendInstruction::FlashRepayReserveLiquidity)
378 }
379 d if d == INIT_OBLIGATION => Some(KlendInstruction::InitObligation),
380 d if d == INIT_OBLIGATION_FARMS_FOR_RESERVE => {
381 Some(KlendInstruction::InitObligationFarmsForReserve)
382 }
383 d if d == REFRESH_OBLIGATION_FARMS_FOR_RESERVE => {
384 Some(KlendInstruction::RefreshObligationFarmsForReserve)
385 }
386 d if d == REQUEST_ELEVATION_GROUP => Some(KlendInstruction::RequestElevationGroup),
387 d if d == SET_OBLIGATION_ORDER => Some(KlendInstruction::SetObligationOrder),
388 d if d == SET_BORROW_ORDER => Some(KlendInstruction::SetBorrowOrder),
389 d if d == FILL_BORROW_ORDER => Some(KlendInstruction::FillBorrowOrder),
390 d if d == INIT_REFERRER_TOKEN_STATE => Some(KlendInstruction::InitReferrerTokenState),
391 d if d == INIT_USER_METADATA => Some(KlendInstruction::InitUserMetadata),
392 d if d == WITHDRAW_REFERRER_FEES => Some(KlendInstruction::WithdrawReferrerFees),
393 d if d == INIT_REFERRER_STATE_AND_SHORT_URL => {
394 Some(KlendInstruction::InitReferrerStateAndShortUrl)
395 }
396 d if d == DELETE_REFERRER_STATE_AND_SHORT_URL => {
397 Some(KlendInstruction::DeleteReferrerStateAndShortUrl)
398 }
399 d if d == ENQUEUE_TO_WITHDRAW => Some(KlendInstruction::EnqueueToWithdraw),
400 d if d == WITHDRAW_QUEUED_LIQUIDITY => Some(KlendInstruction::WithdrawQueuedLiquidity),
401 d if d == RECOVER_INVALID_TICKET_COLLATERAL => {
402 Some(KlendInstruction::RecoverInvalidTicketCollateral)
403 }
404 d if d == CANCEL_WITHDRAW_TICKET => Some(KlendInstruction::CancelWithdrawTicket),
405 d if d == ROLLOVER_FIXED_TERM_BORROW => Some(KlendInstruction::RolloverFixedTermBorrow),
406 d if d == UPDATE_OBLIGATION_CONFIG => Some(KlendInstruction::UpdateObligationConfig),
407 d if d == CLONE_RESERVE_CONFIG => Some(KlendInstruction::CloneReserveConfig),
408 d if d == INITIATE_OBLIGATION_OWNERSHIP_TRANSFER => {
409 Some(KlendInstruction::InitiateObligationOwnershipTransfer)
410 }
411 d if d == APPROVE_OBLIGATION_OWNERSHIP_TRANSFER => {
412 Some(KlendInstruction::ApproveObligationOwnershipTransfer)
413 }
414 d if d == ACCEPT_OBLIGATION_OWNERSHIP => Some(KlendInstruction::AcceptObligationOwnership),
415 d if d == ABORT_OBLIGATION_OWNERSHIP_TRANSFER => {
416 Some(KlendInstruction::AbortObligationOwnershipTransfer)
417 }
418 _ => None,
419 }
420}
421
422#[cfg(test)]
423mod tests {
424 use super::*;
425
426 macro_rules! check_disc {
427 ($name:expr, $constant:ident) => {
428 assert_eq!(
429 compute_discriminator($name),
430 $constant,
431 "Discriminator mismatch for {}",
432 $name
433 );
434 };
435 }
436
437 #[test]
438 fn verify_all_discriminators() {
439 check_disc!("refresh_reserve", REFRESH_RESERVE);
440 check_disc!("refresh_reserves_batch", REFRESH_RESERVES_BATCH);
441 check_disc!("refresh_obligation", REFRESH_OBLIGATION);
442 check_disc!("deposit_reserve_liquidity", DEPOSIT_RESERVE_LIQUIDITY);
443 check_disc!(
444 "deposit_obligation_collateral_v2",
445 DEPOSIT_OBLIGATION_COLLATERAL_V2
446 );
447 check_disc!(
448 "deposit_reserve_liquidity_and_obligation_collateral_v2",
449 DEPOSIT_RESERVE_LIQUIDITY_AND_OBLIGATION_COLLATERAL_V2
450 );
451 check_disc!("redeem_reserve_collateral", REDEEM_RESERVE_COLLATERAL);
452 check_disc!(
453 "withdraw_obligation_collateral_v2",
454 WITHDRAW_OBLIGATION_COLLATERAL_V2
455 );
456 check_disc!(
457 "withdraw_obligation_collateral_and_redeem_reserve_collateral_v2",
458 WITHDRAW_OBLIGATION_COLLATERAL_AND_REDEEM_RESERVE_COLLATERAL_V2
459 );
460 check_disc!(
461 "borrow_obligation_liquidity_v2",
462 BORROW_OBLIGATION_LIQUIDITY_V2
463 );
464 check_disc!(
465 "repay_obligation_liquidity_v2",
466 REPAY_OBLIGATION_LIQUIDITY_V2
467 );
468 check_disc!(
469 "repay_and_withdraw_and_redeem",
470 REPAY_AND_WITHDRAW_AND_REDEEM
471 );
472 check_disc!("deposit_and_withdraw", DEPOSIT_AND_WITHDRAW);
473 check_disc!(
474 "liquidate_obligation_and_redeem_reserve_collateral_v2",
475 LIQUIDATE_OBLIGATION_AND_REDEEM_RESERVE_COLLATERAL_V2
476 );
477 check_disc!(
478 "flash_borrow_reserve_liquidity",
479 FLASH_BORROW_RESERVE_LIQUIDITY
480 );
481 check_disc!(
482 "flash_repay_reserve_liquidity",
483 FLASH_REPAY_RESERVE_LIQUIDITY
484 );
485 check_disc!("init_obligation", INIT_OBLIGATION);
486 check_disc!(
487 "init_obligation_farms_for_reserve",
488 INIT_OBLIGATION_FARMS_FOR_RESERVE
489 );
490 check_disc!(
491 "refresh_obligation_farms_for_reserve",
492 REFRESH_OBLIGATION_FARMS_FOR_RESERVE
493 );
494 check_disc!("request_elevation_group", REQUEST_ELEVATION_GROUP);
495 check_disc!("set_obligation_order", SET_OBLIGATION_ORDER);
496 check_disc!("set_borrow_order", SET_BORROW_ORDER);
497 check_disc!("fill_borrow_order", FILL_BORROW_ORDER);
498 check_disc!("init_referrer_token_state", INIT_REFERRER_TOKEN_STATE);
499 check_disc!("init_user_metadata", INIT_USER_METADATA);
500 check_disc!("withdraw_referrer_fees", WITHDRAW_REFERRER_FEES);
501 check_disc!(
502 "init_referrer_state_and_short_url",
503 INIT_REFERRER_STATE_AND_SHORT_URL
504 );
505 check_disc!(
506 "delete_referrer_state_and_short_url",
507 DELETE_REFERRER_STATE_AND_SHORT_URL
508 );
509 check_disc!("enqueue_to_withdraw", ENQUEUE_TO_WITHDRAW);
510 check_disc!("withdraw_queued_liquidity", WITHDRAW_QUEUED_LIQUIDITY);
511 check_disc!(
512 "recover_invalid_ticket_collateral",
513 RECOVER_INVALID_TICKET_COLLATERAL
514 );
515 check_disc!("cancel_withdraw_ticket", CANCEL_WITHDRAW_TICKET);
516 check_disc!("rollover_fixed_term_borrow", ROLLOVER_FIXED_TERM_BORROW);
517 check_disc!("update_obligation_config", UPDATE_OBLIGATION_CONFIG);
518 check_disc!("clone_reserve_config", CLONE_RESERVE_CONFIG);
519 check_disc!(
520 "initiate_obligation_ownership_transfer",
521 INITIATE_OBLIGATION_OWNERSHIP_TRANSFER
522 );
523 check_disc!(
524 "approve_obligation_ownership_transfer",
525 APPROVE_OBLIGATION_OWNERSHIP_TRANSFER
526 );
527 check_disc!("accept_obligation_ownership", ACCEPT_OBLIGATION_OWNERSHIP);
528 check_disc!(
529 "abort_obligation_ownership_transfer",
530 ABORT_OBLIGATION_OWNERSHIP_TRANSFER
531 );
532 }
533}