Skip to main content

klend_interface/
discriminators.rs

1use sha2::{Digest, Sha256};
2
3/// Compute the 8-byte Anchor discriminator for a given instruction name.
4/// Uses the convention: `sha256("global:<snake_case_name>")[..8]`
5pub 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
34/// Compress a single 64-byte block into the state, returning the new state.
35/// Uses value semantics to avoid `&mut` (not available in const fn on Rust 1.74).
36const 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
96/// Const-compatible SHA-256 returning only the first 8 bytes.
97/// Supports messages up to 119 bytes (two 64-byte blocks).
98pub 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    // First block
124    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    // Second block (if needed)
133    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
157// Refresh
158disc!(REFRESH_RESERVE, "refresh_reserve");
159disc!(REFRESH_RESERVES_BATCH, "refresh_reserves_batch");
160disc!(REFRESH_OBLIGATION, "refresh_obligation");
161
162// Deposit
163disc!(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
173// Withdraw
174disc!(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
184// Borrow
185disc!(
186    BORROW_OBLIGATION_LIQUIDITY_V2,
187    "borrow_obligation_liquidity_v2"
188);
189
190// Repay
191disc!(
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
200// Compound
201disc!(DEPOSIT_AND_WITHDRAW, "deposit_and_withdraw");
202
203// Liquidate
204disc!(
205    LIQUIDATE_OBLIGATION_AND_REDEEM_RESERVE_COLLATERAL_V2,
206    "liquidate_obligation_and_redeem_reserve_collateral_v2"
207);
208
209// Flash
210disc!(
211    FLASH_BORROW_RESERVE_LIQUIDITY,
212    "flash_borrow_reserve_liquidity"
213);
214disc!(
215    FLASH_REPAY_RESERVE_LIQUIDITY,
216    "flash_repay_reserve_liquidity"
217);
218
219// Obligation lifecycle
220disc!(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
231// Orders
232disc!(SET_OBLIGATION_ORDER, "set_obligation_order");
233disc!(SET_BORROW_ORDER, "set_borrow_order");
234disc!(FILL_BORROW_ORDER, "fill_borrow_order");
235
236// Referrer
237disc!(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
249// Withdraw queue
250disc!(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
258// Rollover / obligation config
259disc!(ROLLOVER_FIXED_TERM_BORROW, "rollover_fixed_term_borrow");
260disc!(UPDATE_OBLIGATION_CONFIG, "update_obligation_config");
261
262// Admin
263disc!(CLONE_RESERVE_CONFIG, "clone_reserve_config");
264
265// Obligation ownership transfer
266disc!(
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/// Known Klend instruction types, identified by their 8-byte discriminator.
281#[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
331/// Identify a Klend instruction from its raw instruction data.
332///
333/// Returns `Some(variant)` if the first 8 bytes match a known discriminator,
334/// `None` otherwise.
335pub 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}