lib.rs 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326
  1. //! A relatively advanced example of a staking program. If you're new to Anchor,
  2. //! it's suggested to start with the other examples.
  3. use anchor_lang::accounts::state::ProgramState;
  4. use anchor_lang::prelude::*;
  5. use anchor_lang::solana_program::account_info::next_account_info;
  6. use anchor_lang::solana_program::program_option::COption;
  7. use anchor_spl::token::{self, Mint, TokenAccount, Transfer};
  8. use lockup::{CreateVesting, RealizeLock, Realizor, Vesting};
  9. use std::convert::Into;
  10. declare_id!("HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L");
  11. #[program]
  12. mod registry {
  13. use super::*;
  14. #[state]
  15. pub struct Registry {
  16. pub lockup_program: Pubkey,
  17. }
  18. impl Registry {
  19. pub fn new(ctx: Context<Ctor>) -> Result<Self> {
  20. Ok(Registry {
  21. lockup_program: *ctx.accounts.lockup_program.key,
  22. })
  23. }
  24. pub fn set_lockup_program(
  25. &mut self,
  26. ctx: Context<SetLockupProgram>,
  27. lockup_program: Pubkey,
  28. ) -> Result<()> {
  29. // Hard code the authority because the first version of this program
  30. // did not set an authority account in the global state.
  31. //
  32. // When removing the program's upgrade authority, one should remove
  33. // this method first, redeploy, then remove the upgrade authority.
  34. let expected: Pubkey = "HUgFuN4PbvF5YzjDSw9dQ8uTJUcwm2ANsMXwvRdY4ABx"
  35. .parse()
  36. .unwrap();
  37. if ctx.accounts.authority.key != &expected {
  38. return Err(ErrorCode::InvalidProgramAuthority.into());
  39. }
  40. self.lockup_program = lockup_program;
  41. Ok(())
  42. }
  43. }
  44. impl<'info> RealizeLock<'info, IsRealized<'info>> for Registry {
  45. fn is_realized(ctx: Context<IsRealized>, v: Vesting) -> ProgramResult {
  46. if let Some(realizor) = &v.realizor {
  47. if &realizor.metadata != ctx.accounts.member.to_account_info().key {
  48. return Err(ErrorCode::InvalidRealizorMetadata.into());
  49. }
  50. assert!(ctx.accounts.member.beneficiary == v.beneficiary);
  51. let total_staked =
  52. ctx.accounts.member_spt.amount + ctx.accounts.member_spt_locked.amount;
  53. if total_staked != 0 {
  54. return Err(ErrorCode::UnrealizedReward.into());
  55. }
  56. }
  57. Ok(())
  58. }
  59. }
  60. #[access_control(Initialize::accounts(&ctx, nonce))]
  61. pub fn initialize(
  62. ctx: Context<Initialize>,
  63. mint: Pubkey,
  64. authority: Pubkey,
  65. nonce: u8,
  66. withdrawal_timelock: i64,
  67. stake_rate: u64,
  68. reward_q_len: u32,
  69. ) -> Result<()> {
  70. let registrar = &mut ctx.accounts.registrar;
  71. registrar.authority = authority;
  72. registrar.nonce = nonce;
  73. registrar.mint = mint;
  74. registrar.pool_mint = *ctx.accounts.pool_mint.to_account_info().key;
  75. registrar.stake_rate = stake_rate;
  76. registrar.reward_event_q = *ctx.accounts.reward_event_q.to_account_info().key;
  77. registrar.withdrawal_timelock = withdrawal_timelock;
  78. let reward_q = &mut ctx.accounts.reward_event_q;
  79. reward_q
  80. .events
  81. .resize(reward_q_len as usize, Default::default());
  82. Ok(())
  83. }
  84. pub fn update_registrar(
  85. ctx: Context<UpdateRegistrar>,
  86. new_authority: Option<Pubkey>,
  87. withdrawal_timelock: Option<i64>,
  88. ) -> Result<()> {
  89. let registrar = &mut ctx.accounts.registrar;
  90. if let Some(new_authority) = new_authority {
  91. registrar.authority = new_authority;
  92. }
  93. if let Some(withdrawal_timelock) = withdrawal_timelock {
  94. registrar.withdrawal_timelock = withdrawal_timelock;
  95. }
  96. Ok(())
  97. }
  98. #[access_control(CreateMember::accounts(&ctx, nonce))]
  99. pub fn create_member(ctx: Context<CreateMember>, nonce: u8) -> Result<()> {
  100. let member = &mut ctx.accounts.member;
  101. member.registrar = *ctx.accounts.registrar.to_account_info().key;
  102. member.beneficiary = *ctx.accounts.beneficiary.key;
  103. member.balances = (&ctx.accounts.balances).into();
  104. member.balances_locked = (&ctx.accounts.balances_locked).into();
  105. member.nonce = nonce;
  106. Ok(())
  107. }
  108. pub fn update_member(ctx: Context<UpdateMember>, metadata: Option<Pubkey>) -> Result<()> {
  109. let member = &mut ctx.accounts.member;
  110. if let Some(m) = metadata {
  111. member.metadata = m;
  112. }
  113. Ok(())
  114. }
  115. // Deposits that can only come directly from the member beneficiary.
  116. pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {
  117. token::transfer(ctx.accounts.into(), amount).map_err(Into::into)
  118. }
  119. // Deposits that can only come from the beneficiary's vesting accounts.
  120. pub fn deposit_locked(ctx: Context<DepositLocked>, amount: u64) -> Result<()> {
  121. token::transfer(ctx.accounts.into(), amount).map_err(Into::into)
  122. }
  123. #[access_control(no_available_rewards(
  124. &ctx.accounts.reward_event_q,
  125. &ctx.accounts.member,
  126. &ctx.accounts.balances,
  127. &ctx.accounts.balances_locked,
  128. ))]
  129. pub fn stake(ctx: Context<Stake>, spt_amount: u64, locked: bool) -> Result<()> {
  130. let balances = {
  131. if locked {
  132. &ctx.accounts.balances_locked
  133. } else {
  134. &ctx.accounts.balances
  135. }
  136. };
  137. // Transfer tokens into the stake vault.
  138. {
  139. let seeds = &[
  140. ctx.accounts.registrar.to_account_info().key.as_ref(),
  141. ctx.accounts.member.to_account_info().key.as_ref(),
  142. &[ctx.accounts.member.nonce],
  143. ];
  144. let member_signer = &[&seeds[..]];
  145. let cpi_ctx = CpiContext::new_with_signer(
  146. ctx.accounts.token_program.clone(),
  147. token::Transfer {
  148. from: balances.vault.to_account_info(),
  149. to: balances.vault_stake.to_account_info(),
  150. authority: ctx.accounts.member_signer.to_account_info(),
  151. },
  152. member_signer,
  153. );
  154. // Convert from stake-token units to mint-token units.
  155. let token_amount = spt_amount
  156. .checked_mul(ctx.accounts.registrar.stake_rate)
  157. .unwrap();
  158. token::transfer(cpi_ctx, token_amount)?;
  159. }
  160. // Mint pool tokens to the staker.
  161. {
  162. let seeds = &[
  163. ctx.accounts.registrar.to_account_info().key.as_ref(),
  164. &[ctx.accounts.registrar.nonce],
  165. ];
  166. let registrar_signer = &[&seeds[..]];
  167. let cpi_ctx = CpiContext::new_with_signer(
  168. ctx.accounts.token_program.clone(),
  169. token::MintTo {
  170. mint: ctx.accounts.pool_mint.to_account_info(),
  171. to: balances.spt.to_account_info(),
  172. authority: ctx.accounts.registrar_signer.to_account_info(),
  173. },
  174. registrar_signer,
  175. );
  176. token::mint_to(cpi_ctx, spt_amount)?;
  177. }
  178. // Update stake timestamp.
  179. let member = &mut ctx.accounts.member;
  180. member.last_stake_ts = ctx.accounts.clock.unix_timestamp;
  181. Ok(())
  182. }
  183. #[access_control(no_available_rewards(
  184. &ctx.accounts.reward_event_q,
  185. &ctx.accounts.member,
  186. &ctx.accounts.balances,
  187. &ctx.accounts.balances_locked,
  188. ))]
  189. pub fn start_unstake(ctx: Context<StartUnstake>, spt_amount: u64, locked: bool) -> Result<()> {
  190. let balances = {
  191. if locked {
  192. &ctx.accounts.balances_locked
  193. } else {
  194. &ctx.accounts.balances
  195. }
  196. };
  197. // Program signer.
  198. let seeds = &[
  199. ctx.accounts.registrar.to_account_info().key.as_ref(),
  200. ctx.accounts.member.to_account_info().key.as_ref(),
  201. &[ctx.accounts.member.nonce],
  202. ];
  203. let member_signer = &[&seeds[..]];
  204. // Burn pool tokens.
  205. {
  206. let cpi_ctx = CpiContext::new_with_signer(
  207. ctx.accounts.token_program.clone(),
  208. token::Burn {
  209. mint: ctx.accounts.pool_mint.to_account_info(),
  210. to: balances.spt.to_account_info(),
  211. authority: ctx.accounts.member_signer.to_account_info(),
  212. },
  213. member_signer,
  214. );
  215. token::burn(cpi_ctx, spt_amount)?;
  216. }
  217. // Convert from stake-token units to mint-token units.
  218. let token_amount = spt_amount
  219. .checked_mul(ctx.accounts.registrar.stake_rate)
  220. .unwrap();
  221. // Transfer tokens from the stake to pending vault.
  222. {
  223. let cpi_ctx = CpiContext::new_with_signer(
  224. ctx.accounts.token_program.clone(),
  225. token::Transfer {
  226. from: balances.vault_stake.to_account_info(),
  227. to: balances.vault_pw.to_account_info(),
  228. authority: ctx.accounts.member_signer.to_account_info(),
  229. },
  230. member_signer,
  231. );
  232. token::transfer(cpi_ctx, token_amount)?;
  233. }
  234. // Print receipt.
  235. let pending_withdrawal = &mut ctx.accounts.pending_withdrawal;
  236. pending_withdrawal.burned = false;
  237. pending_withdrawal.member = *ctx.accounts.member.to_account_info().key;
  238. pending_withdrawal.start_ts = ctx.accounts.clock.unix_timestamp;
  239. pending_withdrawal.end_ts =
  240. ctx.accounts.clock.unix_timestamp + ctx.accounts.registrar.withdrawal_timelock;
  241. pending_withdrawal.amount = token_amount;
  242. pending_withdrawal.pool = ctx.accounts.registrar.pool_mint;
  243. pending_withdrawal.registrar = *ctx.accounts.registrar.to_account_info().key;
  244. pending_withdrawal.locked = locked;
  245. // Update stake timestamp.
  246. let member = &mut ctx.accounts.member;
  247. member.last_stake_ts = ctx.accounts.clock.unix_timestamp;
  248. Ok(())
  249. }
  250. pub fn end_unstake(ctx: Context<EndUnstake>) -> Result<()> {
  251. if ctx.accounts.pending_withdrawal.end_ts > ctx.accounts.clock.unix_timestamp {
  252. return Err(ErrorCode::UnstakeTimelock.into());
  253. }
  254. // Select which balance set this affects.
  255. let balances = {
  256. if ctx.accounts.pending_withdrawal.locked {
  257. &ctx.accounts.member.balances_locked
  258. } else {
  259. &ctx.accounts.member.balances
  260. }
  261. };
  262. // Check the vaults given are corrrect.
  263. if &balances.vault != ctx.accounts.vault.key {
  264. return Err(ErrorCode::InvalidVault.into());
  265. }
  266. if &balances.vault_pw != ctx.accounts.vault_pw.key {
  267. return Err(ErrorCode::InvalidVault.into());
  268. }
  269. // Transfer tokens between vaults.
  270. {
  271. let seeds = &[
  272. ctx.accounts.registrar.to_account_info().key.as_ref(),
  273. ctx.accounts.member.to_account_info().key.as_ref(),
  274. &[ctx.accounts.member.nonce],
  275. ];
  276. let signer = &[&seeds[..]];
  277. let cpi_ctx = CpiContext::new_with_signer(
  278. ctx.accounts.token_program.clone(),
  279. Transfer {
  280. from: ctx.accounts.vault_pw.to_account_info(),
  281. to: ctx.accounts.vault.to_account_info(),
  282. authority: ctx.accounts.member_signer.clone(),
  283. },
  284. signer,
  285. );
  286. token::transfer(cpi_ctx, ctx.accounts.pending_withdrawal.amount)?;
  287. }
  288. // Burn the pending withdrawal receipt.
  289. let pending_withdrawal = &mut ctx.accounts.pending_withdrawal;
  290. pending_withdrawal.burned = true;
  291. Ok(())
  292. }
  293. pub fn withdraw(ctx: Context<Withdraw>, amount: u64) -> Result<()> {
  294. let seeds = &[
  295. ctx.accounts.registrar.to_account_info().key.as_ref(),
  296. ctx.accounts.member.to_account_info().key.as_ref(),
  297. &[ctx.accounts.member.nonce],
  298. ];
  299. let signer = &[&seeds[..]];
  300. let cpi_accounts = Transfer {
  301. from: ctx.accounts.vault.to_account_info(),
  302. to: ctx.accounts.depositor.to_account_info(),
  303. authority: ctx.accounts.member_signer.clone(),
  304. };
  305. let cpi_program = ctx.accounts.token_program.clone();
  306. let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer);
  307. token::transfer(cpi_ctx, amount).map_err(Into::into)
  308. }
  309. pub fn withdraw_locked(ctx: Context<WithdrawLocked>, amount: u64) -> Result<()> {
  310. let seeds = &[
  311. ctx.accounts.registrar.to_account_info().key.as_ref(),
  312. ctx.accounts.member.to_account_info().key.as_ref(),
  313. &[ctx.accounts.member.nonce],
  314. ];
  315. let signer = &[&seeds[..]];
  316. let cpi_accounts = Transfer {
  317. from: ctx.accounts.member_vault.to_account_info(),
  318. to: ctx.accounts.vesting_vault.to_account_info(),
  319. authority: ctx.accounts.member_signer.clone(),
  320. };
  321. let cpi_program = ctx.accounts.token_program.clone();
  322. let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer);
  323. token::transfer(cpi_ctx, amount).map_err(Into::into)
  324. }
  325. #[access_control(DropReward::accounts(&ctx, nonce))]
  326. pub fn drop_reward(
  327. ctx: Context<DropReward>,
  328. kind: RewardVendorKind,
  329. total: u64,
  330. expiry_ts: i64,
  331. expiry_receiver: Pubkey,
  332. nonce: u8,
  333. ) -> Result<()> {
  334. if total < ctx.accounts.pool_mint.supply {
  335. return Err(ErrorCode::InsufficientReward.into());
  336. }
  337. if ctx.accounts.clock.unix_timestamp >= expiry_ts {
  338. return Err(ErrorCode::InvalidExpiry.into());
  339. }
  340. if let RewardVendorKind::Locked {
  341. start_ts,
  342. end_ts,
  343. period_count,
  344. } = kind
  345. {
  346. if !lockup::is_valid_schedule(start_ts, end_ts, period_count) {
  347. return Err(ErrorCode::InvalidVestingSchedule.into());
  348. }
  349. }
  350. // Transfer funds into the vendor's vault.
  351. token::transfer(ctx.accounts.into(), total)?;
  352. // Add the event to the reward queue.
  353. let reward_q = &mut ctx.accounts.reward_event_q;
  354. let cursor = reward_q.append(RewardEvent {
  355. vendor: *ctx.accounts.vendor.to_account_info().key,
  356. ts: ctx.accounts.clock.unix_timestamp,
  357. locked: kind != RewardVendorKind::Unlocked,
  358. })?;
  359. // Initialize the vendor.
  360. let vendor = &mut ctx.accounts.vendor;
  361. vendor.registrar = *ctx.accounts.registrar.to_account_info().key;
  362. vendor.vault = *ctx.accounts.vendor_vault.to_account_info().key;
  363. vendor.mint = ctx.accounts.vendor_vault.mint;
  364. vendor.nonce = nonce;
  365. vendor.pool_token_supply = ctx.accounts.pool_mint.supply;
  366. vendor.reward_event_q_cursor = cursor;
  367. vendor.start_ts = ctx.accounts.clock.unix_timestamp;
  368. vendor.expiry_ts = expiry_ts;
  369. vendor.expiry_receiver = expiry_receiver;
  370. vendor.from = *ctx.accounts.depositor_authority.key;
  371. vendor.total = total;
  372. vendor.expired = false;
  373. vendor.kind = kind;
  374. Ok(())
  375. }
  376. #[access_control(reward_eligible(&ctx.accounts.cmn))]
  377. pub fn claim_reward(ctx: Context<ClaimReward>) -> Result<()> {
  378. if RewardVendorKind::Unlocked != ctx.accounts.cmn.vendor.kind {
  379. return Err(ErrorCode::ExpectedUnlockedVendor.into());
  380. }
  381. // Reward distribution.
  382. let spt_total =
  383. ctx.accounts.cmn.balances.spt.amount + ctx.accounts.cmn.balances_locked.spt.amount;
  384. let reward_amount = spt_total
  385. .checked_mul(ctx.accounts.cmn.vendor.total)
  386. .unwrap()
  387. .checked_div(ctx.accounts.cmn.vendor.pool_token_supply)
  388. .unwrap();
  389. assert!(reward_amount > 0);
  390. // Send reward to the given token account.
  391. let seeds = &[
  392. ctx.accounts.cmn.registrar.to_account_info().key.as_ref(),
  393. ctx.accounts.cmn.vendor.to_account_info().key.as_ref(),
  394. &[ctx.accounts.cmn.vendor.nonce],
  395. ];
  396. let signer = &[&seeds[..]];
  397. let cpi_ctx = CpiContext::new_with_signer(
  398. ctx.accounts.cmn.token_program.clone(),
  399. token::Transfer {
  400. from: ctx.accounts.cmn.vault.to_account_info(),
  401. to: ctx.accounts.to.to_account_info(),
  402. authority: ctx.accounts.cmn.vendor_signer.to_account_info(),
  403. },
  404. signer,
  405. );
  406. token::transfer(cpi_ctx, reward_amount)?;
  407. // Update member as having processed the reward.
  408. let member = &mut ctx.accounts.cmn.member;
  409. member.rewards_cursor = ctx.accounts.cmn.vendor.reward_event_q_cursor + 1;
  410. Ok(())
  411. }
  412. #[access_control(reward_eligible(&ctx.accounts.cmn))]
  413. pub fn claim_reward_locked<'a, 'b, 'c, 'info>(
  414. ctx: Context<'a, 'b, 'c, 'info, ClaimRewardLocked<'info>>,
  415. nonce: u8,
  416. ) -> Result<()> {
  417. let (start_ts, end_ts, period_count) = match ctx.accounts.cmn.vendor.kind {
  418. RewardVendorKind::Unlocked => return Err(ErrorCode::ExpectedLockedVendor.into()),
  419. RewardVendorKind::Locked {
  420. start_ts,
  421. end_ts,
  422. period_count,
  423. } => (start_ts, end_ts, period_count),
  424. };
  425. // Reward distribution.
  426. let spt_total =
  427. ctx.accounts.cmn.balances.spt.amount + ctx.accounts.cmn.balances_locked.spt.amount;
  428. let reward_amount = spt_total
  429. .checked_mul(ctx.accounts.cmn.vendor.total)
  430. .unwrap()
  431. .checked_div(ctx.accounts.cmn.vendor.pool_token_supply)
  432. .unwrap();
  433. assert!(reward_amount > 0);
  434. // Specify the vesting account's realizor, so that unlocks can only
  435. // execute once completely unstaked.
  436. let realizor = Some(Realizor {
  437. program: *ctx.program_id,
  438. metadata: *ctx.accounts.cmn.member.to_account_info().key,
  439. });
  440. // CPI: Create lockup account for the member's beneficiary.
  441. let seeds = &[
  442. ctx.accounts.cmn.registrar.to_account_info().key.as_ref(),
  443. ctx.accounts.cmn.vendor.to_account_info().key.as_ref(),
  444. &[ctx.accounts.cmn.vendor.nonce],
  445. ];
  446. let signer = &[&seeds[..]];
  447. let remaining_accounts: &[AccountInfo] = ctx.remaining_accounts;
  448. let cpi_program = ctx.accounts.lockup_program.clone();
  449. let cpi_accounts = {
  450. let accs = &mut remaining_accounts.iter();
  451. lockup::cpi::accounts::CreateVesting {
  452. vesting: next_account_info(accs)?.to_account_info(),
  453. vault: next_account_info(accs)?.to_account_info(),
  454. depositor: next_account_info(accs)?.to_account_info(),
  455. depositor_authority: next_account_info(accs)?.to_account_info(),
  456. token_program: next_account_info(accs)?.to_account_info(),
  457. clock: next_account_info(accs)?.to_account_info(),
  458. }
  459. };
  460. let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer);
  461. lockup::cpi::create_vesting(
  462. cpi_ctx,
  463. ctx.accounts.cmn.member.beneficiary,
  464. reward_amount,
  465. nonce,
  466. start_ts,
  467. end_ts,
  468. period_count,
  469. realizor,
  470. )?;
  471. // Make sure this reward can't be processed more than once.
  472. let member = &mut ctx.accounts.cmn.member;
  473. member.rewards_cursor = ctx.accounts.cmn.vendor.reward_event_q_cursor + 1;
  474. Ok(())
  475. }
  476. pub fn expire_reward(ctx: Context<ExpireReward>) -> Result<()> {
  477. if ctx.accounts.clock.unix_timestamp < ctx.accounts.vendor.expiry_ts {
  478. return Err(ErrorCode::VendorNotYetExpired.into());
  479. }
  480. // Send all remaining funds to the expiry receiver's token.
  481. let seeds = &[
  482. ctx.accounts.registrar.to_account_info().key.as_ref(),
  483. ctx.accounts.vendor.to_account_info().key.as_ref(),
  484. &[ctx.accounts.vendor.nonce],
  485. ];
  486. let signer = &[&seeds[..]];
  487. let cpi_ctx = CpiContext::new_with_signer(
  488. ctx.accounts.token_program.clone(),
  489. token::Transfer {
  490. to: ctx.accounts.expiry_receiver_token.to_account_info(),
  491. from: ctx.accounts.vault.to_account_info(),
  492. authority: ctx.accounts.vendor_signer.to_account_info(),
  493. },
  494. signer,
  495. );
  496. token::transfer(cpi_ctx, ctx.accounts.vault.amount)?;
  497. // Burn the vendor.
  498. let vendor = &mut ctx.accounts.vendor;
  499. vendor.expired = true;
  500. Ok(())
  501. }
  502. }
  503. #[derive(Accounts)]
  504. pub struct Initialize<'info> {
  505. #[account(zero)]
  506. registrar: Account<'info, Registrar>,
  507. #[account(zero)]
  508. reward_event_q: Account<'info, RewardQueue>,
  509. #[account("pool_mint.decimals == 0")]
  510. pool_mint: Account<'info, Mint>,
  511. }
  512. impl<'info> Initialize<'info> {
  513. fn accounts(ctx: &Context<Initialize<'info>>, nonce: u8) -> Result<()> {
  514. let registrar_signer = Pubkey::create_program_address(
  515. &[
  516. ctx.accounts.registrar.to_account_info().key.as_ref(),
  517. &[nonce],
  518. ],
  519. ctx.program_id,
  520. )
  521. .map_err(|_| ErrorCode::InvalidNonce)?;
  522. if ctx.accounts.pool_mint.mint_authority != COption::Some(registrar_signer) {
  523. return Err(ErrorCode::InvalidPoolMintAuthority.into());
  524. }
  525. assert!(ctx.accounts.pool_mint.supply == 0);
  526. Ok(())
  527. }
  528. }
  529. #[derive(Accounts)]
  530. pub struct UpdateRegistrar<'info> {
  531. #[account(mut, has_one = authority)]
  532. registrar: Account<'info, Registrar>,
  533. authority: Signer<'info>,
  534. }
  535. #[derive(Accounts)]
  536. pub struct CreateMember<'info> {
  537. // Stake instance.
  538. registrar: Box<Account<'info, Registrar>>,
  539. // Member.
  540. #[account(zero)]
  541. member: Box<Account<'info, Member>>,
  542. beneficiary: Signer<'info>,
  543. #[account(
  544. "&balances.spt.owner == member_signer.key",
  545. "balances.spt.mint == registrar.pool_mint",
  546. "balances.vault.mint == registrar.mint"
  547. )]
  548. balances: BalanceSandboxAccounts<'info>,
  549. #[account(
  550. "&balances_locked.spt.owner == member_signer.key",
  551. "balances_locked.spt.mint == registrar.pool_mint",
  552. "balances_locked.vault.mint == registrar.mint"
  553. )]
  554. balances_locked: BalanceSandboxAccounts<'info>,
  555. member_signer: AccountInfo<'info>,
  556. // Misc.
  557. #[account("token_program.key == &token::ID")]
  558. token_program: AccountInfo<'info>,
  559. }
  560. impl<'info> CreateMember<'info> {
  561. fn accounts(ctx: &Context<CreateMember>, nonce: u8) -> Result<()> {
  562. let seeds = &[
  563. ctx.accounts.registrar.to_account_info().key.as_ref(),
  564. ctx.accounts.member.to_account_info().key.as_ref(),
  565. &[nonce],
  566. ];
  567. let member_signer = Pubkey::create_program_address(seeds, ctx.program_id)
  568. .map_err(|_| ErrorCode::InvalidNonce)?;
  569. if &member_signer != ctx.accounts.member_signer.to_account_info().key {
  570. return Err(ErrorCode::InvalidMemberSigner.into());
  571. }
  572. Ok(())
  573. }
  574. }
  575. // When creating a member, the mints and owners of these accounts are correct.
  576. // Upon creation, we assign the accounts. A onetime operation.
  577. // When using a member, we check these accounts addresess are equal to the
  578. // addresses stored on the member. If so, the correct accounts were given are
  579. // correct.
  580. #[derive(Accounts, Clone)]
  581. pub struct BalanceSandboxAccounts<'info> {
  582. #[account(mut)]
  583. spt: Box<Account<'info, TokenAccount>>,
  584. #[account(mut, constraint = vault.owner == spt.owner)]
  585. vault: Box<Account<'info, TokenAccount>>,
  586. #[account(
  587. mut,
  588. constraint = vault_stake.owner == spt.owner,
  589. constraint = vault_stake.mint == vault.mint
  590. )]
  591. vault_stake: Box<Account<'info, TokenAccount>>,
  592. #[account(mut, constraint = vault_pw.owner == spt.owner, constraint = vault_pw.mint == vault.mint)]
  593. vault_pw: Box<Account<'info, TokenAccount>>,
  594. }
  595. #[derive(Accounts)]
  596. pub struct Ctor<'info> {
  597. lockup_program: AccountInfo<'info>,
  598. }
  599. #[derive(Accounts)]
  600. pub struct SetLockupProgram<'info> {
  601. authority: Signer<'info>,
  602. }
  603. #[derive(Accounts)]
  604. pub struct IsRealized<'info> {
  605. #[account(
  606. constraint = &member.balances.spt == member_spt.to_account_info().key,
  607. constraint = &member.balances_locked.spt == member_spt_locked.to_account_info().key
  608. )]
  609. member: Account<'info, Member>,
  610. member_spt: Account<'info, TokenAccount>,
  611. member_spt_locked: Account<'info, TokenAccount>,
  612. }
  613. #[derive(Accounts)]
  614. pub struct UpdateMember<'info> {
  615. #[account(mut, has_one = beneficiary)]
  616. member: Account<'info, Member>,
  617. beneficiary: Signer<'info>,
  618. }
  619. #[derive(Accounts)]
  620. pub struct Deposit<'info> {
  621. // Member.
  622. #[account(has_one = beneficiary)]
  623. member: Account<'info, Member>,
  624. beneficiary: Signer<'info>,
  625. #[account(mut, constraint = vault.to_account_info().key == &member.balances.vault)]
  626. vault: Account<'info, TokenAccount>,
  627. // Depositor.
  628. #[account(mut)]
  629. depositor: AccountInfo<'info>,
  630. #[account(signer, constraint = depositor_authority.key == &member.beneficiary)]
  631. depositor_authority: AccountInfo<'info>,
  632. // Misc.
  633. #[account(constraint = token_program.key == &token::ID)]
  634. token_program: AccountInfo<'info>,
  635. }
  636. #[derive(Accounts)]
  637. pub struct DepositLocked<'info> {
  638. // Lockup whitelist relay interface.
  639. #[account(
  640. constraint = vesting.to_account_info().owner == &registry.lockup_program,
  641. constraint = vesting.beneficiary == member.beneficiary
  642. )]
  643. vesting: Box<Account<'info, Vesting>>,
  644. #[account(mut, constraint = vesting_vault.key == &vesting.vault)]
  645. vesting_vault: AccountInfo<'info>,
  646. // Note: no need to verify the depositor_authority since the SPL program
  647. // will fail the transaction if it's not correct.
  648. pub depositor_authority: Signer<'info>,
  649. #[account(constraint = token_program.key == &token::ID)]
  650. token_program: AccountInfo<'info>,
  651. #[account(
  652. mut,
  653. constraint = member_vault.to_account_info().key == &member.balances_locked.vault
  654. )]
  655. member_vault: Box<Account<'info, TokenAccount>>,
  656. #[account(
  657. seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()],
  658. bump = member.nonce,
  659. )]
  660. member_signer: AccountInfo<'info>,
  661. // Program specific.
  662. registry: ProgramState<'info, Registry>,
  663. registrar: Box<Account<'info, Registrar>>,
  664. #[account(has_one = registrar, has_one = beneficiary)]
  665. member: Box<Account<'info, Member>>,
  666. beneficiary: Signer<'info>,
  667. }
  668. #[derive(Accounts)]
  669. pub struct Stake<'info> {
  670. // Global accounts for the staking instance.
  671. #[account(has_one = pool_mint, has_one = reward_event_q)]
  672. registrar: Account<'info, Registrar>,
  673. reward_event_q: Account<'info, RewardQueue>,
  674. #[account(mut)]
  675. pool_mint: Account<'info, Mint>,
  676. // Member.
  677. #[account(mut, has_one = beneficiary, has_one = registrar)]
  678. member: Account<'info, Member>,
  679. beneficiary: Signer<'info>,
  680. #[account(constraint = BalanceSandbox::from(&balances) == member.balances)]
  681. balances: BalanceSandboxAccounts<'info>,
  682. #[account(constraint = BalanceSandbox::from(&balances_locked) == member.balances_locked)]
  683. balances_locked: BalanceSandboxAccounts<'info>,
  684. // Program signers.
  685. #[account(
  686. seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()],
  687. bump = member.nonce,
  688. )]
  689. member_signer: AccountInfo<'info>,
  690. #[account(
  691. seeds = [registrar.to_account_info().key.as_ref()],
  692. bump = registrar.nonce,
  693. )]
  694. registrar_signer: AccountInfo<'info>,
  695. // Misc.
  696. clock: Sysvar<'info, Clock>,
  697. #[account(constraint = token_program.key == &token::ID)]
  698. token_program: AccountInfo<'info>,
  699. }
  700. #[derive(Accounts)]
  701. pub struct StartUnstake<'info> {
  702. // Stake instance globals.
  703. #[account(has_one = reward_event_q)]
  704. registrar: Account<'info, Registrar>,
  705. reward_event_q: Account<'info, RewardQueue>,
  706. #[account(mut)]
  707. pool_mint: AccountInfo<'info>,
  708. // Member.
  709. #[account(zero)]
  710. pending_withdrawal: Account<'info, PendingWithdrawal>,
  711. #[account(has_one = beneficiary, has_one = registrar)]
  712. member: Account<'info, Member>,
  713. beneficiary: Signer<'info>,
  714. #[account(constraint = BalanceSandbox::from(&balances) == member.balances)]
  715. balances: BalanceSandboxAccounts<'info>,
  716. #[account(constraint = BalanceSandbox::from(&balances_locked) == member.balances_locked)]
  717. balances_locked: BalanceSandboxAccounts<'info>,
  718. // Programmatic signers.
  719. #[account(
  720. seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()],
  721. bump = member.nonce,
  722. )]
  723. member_signer: AccountInfo<'info>,
  724. // Misc.
  725. #[account(constraint = token_program.key == &token::ID)]
  726. token_program: AccountInfo<'info>,
  727. clock: Sysvar<'info, Clock>,
  728. }
  729. #[derive(Accounts)]
  730. pub struct EndUnstake<'info> {
  731. registrar: Account<'info, Registrar>,
  732. #[account(has_one = registrar, has_one = beneficiary)]
  733. member: Account<'info, Member>,
  734. beneficiary: Signer<'info>,
  735. #[account(mut, has_one = registrar, has_one = member, constraint = !pending_withdrawal.burned)]
  736. pending_withdrawal: Account<'info, PendingWithdrawal>,
  737. // If we had ordered maps implementing Accounts we could do a constraint like
  738. // balances.get(pending_withdrawal.balance_id).vault == vault.key.
  739. //
  740. // Note: we do the constraints check in the handler, not here.
  741. #[account(mut)]
  742. vault: AccountInfo<'info>,
  743. #[account(mut)]
  744. vault_pw: AccountInfo<'info>,
  745. #[account(
  746. seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()],
  747. bump = member.nonce,
  748. )]
  749. member_signer: AccountInfo<'info>,
  750. clock: Sysvar<'info, Clock>,
  751. #[account(constraint = token_program.key == &token::ID)]
  752. token_program: AccountInfo<'info>,
  753. }
  754. #[derive(Accounts)]
  755. pub struct Withdraw<'info> {
  756. // Stake instance.
  757. registrar: Account<'info, Registrar>,
  758. // Member.
  759. #[account(has_one = registrar, has_one = beneficiary)]
  760. member: Account<'info, Member>,
  761. beneficiary: Signer<'info>,
  762. #[account(mut, constraint = vault.to_account_info().key == &member.balances.vault)]
  763. vault: Account<'info, TokenAccount>,
  764. #[account(
  765. seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()],
  766. bump = member.nonce,
  767. )]
  768. member_signer: AccountInfo<'info>,
  769. // Receiver.
  770. #[account(mut)]
  771. depositor: AccountInfo<'info>,
  772. // Misc.
  773. #[account(constraint = token_program.key == &token::ID)]
  774. token_program: AccountInfo<'info>,
  775. }
  776. #[derive(Accounts)]
  777. pub struct WithdrawLocked<'info> {
  778. // Lockup whitelist relay interface.
  779. #[account(
  780. constraint = vesting.to_account_info().owner == &registry.lockup_program,
  781. constraint = vesting.beneficiary == member.beneficiary,
  782. )]
  783. vesting: Box<Account<'info, Vesting>>,
  784. #[account(mut, constraint = vesting_vault.key == &vesting.vault)]
  785. vesting_vault: AccountInfo<'info>,
  786. vesting_signer: Signer<'info>,
  787. #[account(constraint = token_program.key == &token::ID)]
  788. token_program: AccountInfo<'info>,
  789. #[account(
  790. mut,
  791. constraint = member_vault.to_account_info().key == &member.balances_locked.vault
  792. )]
  793. member_vault: Box<Account<'info, TokenAccount>>,
  794. #[account(
  795. seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()],
  796. bump = member.nonce,
  797. )]
  798. member_signer: AccountInfo<'info>,
  799. // Program specific.
  800. registry: ProgramState<'info, Registry>,
  801. registrar: Box<Account<'info, Registrar>>,
  802. #[account(has_one = registrar, has_one = beneficiary)]
  803. member: Box<Account<'info, Member>>,
  804. beneficiary: Signer<'info>,
  805. }
  806. #[derive(Accounts)]
  807. pub struct DropReward<'info> {
  808. // Staking instance.
  809. #[account(has_one = reward_event_q, has_one = pool_mint)]
  810. registrar: Account<'info, Registrar>,
  811. #[account(mut)]
  812. reward_event_q: Account<'info, RewardQueue>,
  813. pool_mint: Account<'info, Mint>,
  814. // Vendor.
  815. #[account(zero)]
  816. vendor: Account<'info, RewardVendor>,
  817. #[account(mut)]
  818. vendor_vault: Account<'info, TokenAccount>,
  819. // Depositor.
  820. #[account(mut)]
  821. depositor: AccountInfo<'info>,
  822. #[account(signer)]
  823. depositor_authority: AccountInfo<'info>,
  824. // Misc.
  825. #[account(constraint = token_program.key == &token::ID)]
  826. token_program: AccountInfo<'info>,
  827. clock: Sysvar<'info, Clock>,
  828. }
  829. impl<'info> DropReward<'info> {
  830. fn accounts(ctx: &Context<DropReward>, nonce: u8) -> Result<()> {
  831. let vendor_signer = Pubkey::create_program_address(
  832. &[
  833. ctx.accounts.registrar.to_account_info().key.as_ref(),
  834. ctx.accounts.vendor.to_account_info().key.as_ref(),
  835. &[nonce],
  836. ],
  837. ctx.program_id,
  838. )
  839. .map_err(|_| ErrorCode::InvalidNonce)?;
  840. if vendor_signer != ctx.accounts.vendor_vault.owner {
  841. return Err(ErrorCode::InvalidVaultOwner.into());
  842. }
  843. Ok(())
  844. }
  845. }
  846. #[derive(Accounts)]
  847. pub struct ClaimReward<'info> {
  848. cmn: ClaimRewardCommon<'info>,
  849. // Account to send reward to.
  850. #[account(mut)]
  851. to: AccountInfo<'info>,
  852. }
  853. #[derive(Accounts)]
  854. pub struct ClaimRewardLocked<'info> {
  855. cmn: ClaimRewardCommon<'info>,
  856. registry: ProgramState<'info, Registry>,
  857. #[account("lockup_program.key == &registry.lockup_program")]
  858. lockup_program: AccountInfo<'info>,
  859. }
  860. // Accounts common to both claim reward locked/unlocked instructions.
  861. #[derive(Accounts)]
  862. pub struct ClaimRewardCommon<'info> {
  863. // Stake instance.
  864. registrar: Account<'info, Registrar>,
  865. // Member.
  866. #[account(mut, has_one = registrar, has_one = beneficiary)]
  867. member: Account<'info, Member>,
  868. beneficiary: Signer<'info>,
  869. #[account(constraint = BalanceSandbox::from(&balances) == member.balances)]
  870. balances: BalanceSandboxAccounts<'info>,
  871. #[account(constraint = BalanceSandbox::from(&balances_locked) == member.balances_locked)]
  872. balances_locked: BalanceSandboxAccounts<'info>,
  873. // Vendor.
  874. #[account(has_one = registrar, has_one = vault)]
  875. vendor: Account<'info, RewardVendor>,
  876. #[account(mut)]
  877. vault: AccountInfo<'info>,
  878. #[account(
  879. seeds = [registrar.to_account_info().key.as_ref(), vendor.to_account_info().key.as_ref()],
  880. bump = vendor.nonce,
  881. )]
  882. vendor_signer: AccountInfo<'info>,
  883. // Misc.
  884. #[account(constraint = token_program.key == &token::ID)]
  885. token_program: AccountInfo<'info>,
  886. clock: Sysvar<'info, Clock>,
  887. }
  888. #[derive(Accounts)]
  889. pub struct ExpireReward<'info> {
  890. // Staking instance globals.
  891. registrar: Account<'info, Registrar>,
  892. // Vendor.
  893. #[account(mut, has_one = registrar, has_one = vault, has_one = expiry_receiver)]
  894. vendor: Account<'info, RewardVendor>,
  895. #[account(mut)]
  896. vault: Account<'info, TokenAccount>,
  897. #[account(
  898. seeds = [registrar.to_account_info().key.as_ref(), vendor.to_account_info().key.as_ref()],
  899. bump = vendor.nonce
  900. )]
  901. vendor_signer: AccountInfo<'info>,
  902. // Receiver.
  903. expiry_receiver: Signer<'info>,
  904. #[account(mut)]
  905. expiry_receiver_token: AccountInfo<'info>,
  906. // Misc.
  907. #[account(constraint = token_program.key == &token::ID)]
  908. token_program: AccountInfo<'info>,
  909. clock: Sysvar<'info, Clock>,
  910. }
  911. #[account]
  912. pub struct Registrar {
  913. /// Priviledged account.
  914. pub authority: Pubkey,
  915. /// Nonce to derive the program-derived address owning the vaults.
  916. pub nonce: u8,
  917. /// Number of seconds that must pass for a withdrawal to complete.
  918. pub withdrawal_timelock: i64,
  919. /// Global event queue for reward vendoring.
  920. pub reward_event_q: Pubkey,
  921. /// Mint of the tokens that can be staked.
  922. pub mint: Pubkey,
  923. /// Staking pool token mint.
  924. pub pool_mint: Pubkey,
  925. /// The amount of tokens (not decimal) that must be staked to get a single
  926. /// staking pool token.
  927. pub stake_rate: u64,
  928. }
  929. #[account]
  930. pub struct Member {
  931. /// Registrar the member belongs to.
  932. pub registrar: Pubkey,
  933. /// The effective owner of the Member account.
  934. pub beneficiary: Pubkey,
  935. /// Arbitrary metadata account owned by any program.
  936. pub metadata: Pubkey,
  937. /// Sets of balances owned by the Member.
  938. pub balances: BalanceSandbox,
  939. /// Locked balances owned by the Member.
  940. pub balances_locked: BalanceSandbox,
  941. /// Next position in the rewards event queue to process.
  942. pub rewards_cursor: u32,
  943. /// The clock timestamp of the last time this account staked or switched
  944. /// entities. Used as a proof to reward vendors that the Member account
  945. /// was staked at a given point in time.
  946. pub last_stake_ts: i64,
  947. /// Signer nonce.
  948. pub nonce: u8,
  949. }
  950. // BalanceSandbox defines isolated funds that can only be deposited/withdrawn
  951. // into the program.
  952. //
  953. // Once controlled by the program, the associated `Member` account's beneficiary
  954. // can send funds to/from any of the accounts within the sandbox, e.g., to
  955. // stake.
  956. #[derive(AnchorSerialize, AnchorDeserialize, Default, Debug, Clone, PartialEq)]
  957. pub struct BalanceSandbox {
  958. // Staking pool token.
  959. pub spt: Pubkey,
  960. // Free balance (deposit) vaults.
  961. pub vault: Pubkey,
  962. // Stake vaults.
  963. pub vault_stake: Pubkey,
  964. // Pending withdrawal vaults.
  965. pub vault_pw: Pubkey,
  966. }
  967. #[account]
  968. pub struct PendingWithdrawal {
  969. /// Registrar this account belongs to.
  970. pub registrar: Pubkey,
  971. /// Member this account belongs to.
  972. pub member: Pubkey,
  973. /// One time token. True if the withdrawal has been completed.
  974. pub burned: bool,
  975. /// The pool being withdrawn from.
  976. pub pool: Pubkey,
  977. /// Unix timestamp when this account was initialized.
  978. pub start_ts: i64,
  979. /// Timestamp when the pending withdrawal completes.
  980. pub end_ts: i64,
  981. /// The number of tokens redeemed from the staking pool.
  982. pub amount: u64,
  983. /// True if the withdrawal applies to locked balances.
  984. pub locked: bool,
  985. }
  986. #[account]
  987. pub struct RewardQueue {
  988. // Invariant: index is position of the next available slot.
  989. head: u32,
  990. // Invariant: index is position of the first (oldest) taken slot.
  991. // Invariant: head == tail => queue is initialized.
  992. // Invariant: index_of(head + 1) == index_of(tail) => queue is full.
  993. tail: u32,
  994. // Although a vec is used, the size is immutable.
  995. events: Vec<RewardEvent>,
  996. }
  997. impl RewardQueue {
  998. pub fn append(&mut self, event: RewardEvent) -> Result<u32> {
  999. let cursor = self.head;
  1000. // Insert into next available slot.
  1001. let h_idx = self.index_of(self.head);
  1002. self.events[h_idx] = event;
  1003. // Update head and tail counters.
  1004. let is_full = self.index_of(self.head + 1) == self.index_of(self.tail);
  1005. if is_full {
  1006. self.tail += 1;
  1007. }
  1008. self.head += 1;
  1009. Ok(cursor)
  1010. }
  1011. pub fn index_of(&self, counter: u32) -> usize {
  1012. counter as usize % self.capacity()
  1013. }
  1014. pub fn capacity(&self) -> usize {
  1015. self.events.len()
  1016. }
  1017. pub fn get(&self, cursor: u32) -> &RewardEvent {
  1018. &self.events[cursor as usize % self.capacity()]
  1019. }
  1020. pub fn head(&self) -> u32 {
  1021. self.head
  1022. }
  1023. pub fn tail(&self) -> u32 {
  1024. self.tail
  1025. }
  1026. }
  1027. #[derive(Default, Clone, Copy, Debug, AnchorSerialize, AnchorDeserialize)]
  1028. pub struct RewardEvent {
  1029. vendor: Pubkey,
  1030. ts: i64,
  1031. locked: bool,
  1032. }
  1033. #[account]
  1034. pub struct RewardVendor {
  1035. pub registrar: Pubkey,
  1036. pub vault: Pubkey,
  1037. pub mint: Pubkey,
  1038. pub nonce: u8,
  1039. pub pool_token_supply: u64,
  1040. pub reward_event_q_cursor: u32,
  1041. pub start_ts: i64,
  1042. pub expiry_ts: i64,
  1043. pub expiry_receiver: Pubkey,
  1044. pub from: Pubkey,
  1045. pub total: u64,
  1046. pub expired: bool,
  1047. pub kind: RewardVendorKind,
  1048. }
  1049. #[derive(AnchorSerialize, AnchorDeserialize, Clone, PartialEq)]
  1050. pub enum RewardVendorKind {
  1051. Unlocked,
  1052. Locked {
  1053. start_ts: i64,
  1054. end_ts: i64,
  1055. period_count: u64,
  1056. },
  1057. }
  1058. #[error]
  1059. pub enum ErrorCode {
  1060. #[msg("The given reward queue has already been initialized.")]
  1061. RewardQAlreadyInitialized,
  1062. #[msg("The nonce given doesn't derive a valid program address.")]
  1063. InvalidNonce,
  1064. #[msg("Invalid pool mint authority")]
  1065. InvalidPoolMintAuthority,
  1066. #[msg("Member signer doesn't match the derived address.")]
  1067. InvalidMemberSigner,
  1068. #[msg("The given vault owner must match the signing depositor.")]
  1069. InvalidVaultDeposit,
  1070. #[msg("The signing depositor doesn't match either of the balance accounts")]
  1071. InvalidDepositor,
  1072. #[msg("The vault given does not match the vault expected.")]
  1073. InvalidVault,
  1074. #[msg("Invalid vault owner.")]
  1075. InvalidVaultOwner,
  1076. #[msg("An unknown error has occured.")]
  1077. Unknown,
  1078. #[msg("The unstake timelock has not yet expired.")]
  1079. UnstakeTimelock,
  1080. #[msg("Reward vendors must have at least one token unit per pool token")]
  1081. InsufficientReward,
  1082. #[msg("Reward expiry must be after the current clock timestamp.")]
  1083. InvalidExpiry,
  1084. #[msg("The reward vendor has been expired.")]
  1085. VendorExpired,
  1086. #[msg("This reward has already been processed.")]
  1087. CursorAlreadyProcessed,
  1088. #[msg("The account was not staked at the time of this reward.")]
  1089. NotStakedDuringDrop,
  1090. #[msg("The vendor is not yet eligible for expiry.")]
  1091. VendorNotYetExpired,
  1092. #[msg("Please collect your reward before otherwise using the program.")]
  1093. RewardsNeedsProcessing,
  1094. #[msg("Locked reward vendor expected but an unlocked vendor was given.")]
  1095. ExpectedLockedVendor,
  1096. #[msg("Unlocked reward vendor expected but a locked vendor was given.")]
  1097. ExpectedUnlockedVendor,
  1098. #[msg("Locked deposit from an invalid deposit authority.")]
  1099. InvalidVestingSigner,
  1100. #[msg("Locked rewards cannot be realized until one unstaked all tokens.")]
  1101. UnrealizedReward,
  1102. #[msg("The beneficiary doesn't match.")]
  1103. InvalidBeneficiary,
  1104. #[msg("The given member account does not match the realizor metadata.")]
  1105. InvalidRealizorMetadata,
  1106. #[msg("Invalid vesting schedule for the locked reward.")]
  1107. InvalidVestingSchedule,
  1108. #[msg("Please specify the correct authority for this program.")]
  1109. InvalidProgramAuthority,
  1110. }
  1111. impl<'a, 'b, 'c, 'info> From<&mut Deposit<'info>>
  1112. for CpiContext<'a, 'b, 'c, 'info, Transfer<'info>>
  1113. {
  1114. fn from(accounts: &mut Deposit<'info>) -> CpiContext<'a, 'b, 'c, 'info, Transfer<'info>> {
  1115. let cpi_accounts = Transfer {
  1116. from: accounts.depositor.clone(),
  1117. to: accounts.vault.to_account_info(),
  1118. authority: accounts.depositor_authority.clone(),
  1119. };
  1120. let cpi_program = accounts.token_program.clone();
  1121. CpiContext::new(cpi_program, cpi_accounts)
  1122. }
  1123. }
  1124. impl<'a, 'b, 'c, 'info> From<&mut DepositLocked<'info>>
  1125. for CpiContext<'a, 'b, 'c, 'info, Transfer<'info>>
  1126. {
  1127. fn from(accounts: &mut DepositLocked<'info>) -> CpiContext<'a, 'b, 'c, 'info, Transfer<'info>> {
  1128. let cpi_accounts = Transfer {
  1129. from: accounts.vesting_vault.clone(),
  1130. to: accounts.member_vault.to_account_info(),
  1131. authority: accounts.depositor_authority.to_account_info().clone(),
  1132. };
  1133. let cpi_program = accounts.token_program.clone();
  1134. CpiContext::new(cpi_program, cpi_accounts)
  1135. }
  1136. }
  1137. impl<'a, 'b, 'c, 'info> From<&mut DropReward<'info>>
  1138. for CpiContext<'a, 'b, 'c, 'info, Transfer<'info>>
  1139. {
  1140. fn from(accounts: &mut DropReward<'info>) -> CpiContext<'a, 'b, 'c, 'info, Transfer<'info>> {
  1141. let cpi_accounts = Transfer {
  1142. from: accounts.depositor.clone(),
  1143. to: accounts.vendor_vault.to_account_info(),
  1144. authority: accounts.depositor_authority.clone(),
  1145. };
  1146. let cpi_program = accounts.token_program.clone();
  1147. CpiContext::new(cpi_program, cpi_accounts)
  1148. }
  1149. }
  1150. impl<'info> From<&BalanceSandboxAccounts<'info>> for BalanceSandbox {
  1151. fn from(accs: &BalanceSandboxAccounts<'info>) -> Self {
  1152. Self {
  1153. spt: *accs.spt.to_account_info().key,
  1154. vault: *accs.vault.to_account_info().key,
  1155. vault_stake: *accs.vault_stake.to_account_info().key,
  1156. vault_pw: *accs.vault_pw.to_account_info().key,
  1157. }
  1158. }
  1159. }
  1160. fn reward_eligible(cmn: &ClaimRewardCommon) -> Result<()> {
  1161. let vendor = &cmn.vendor;
  1162. let member = &cmn.member;
  1163. if vendor.expired {
  1164. return Err(ErrorCode::VendorExpired.into());
  1165. }
  1166. if member.rewards_cursor > vendor.reward_event_q_cursor {
  1167. return Err(ErrorCode::CursorAlreadyProcessed.into());
  1168. }
  1169. if member.last_stake_ts > vendor.start_ts {
  1170. return Err(ErrorCode::NotStakedDuringDrop.into());
  1171. }
  1172. Ok(())
  1173. }
  1174. // Asserts the user calling the `Stake` instruction has no rewards available
  1175. // in the reward queue.
  1176. pub fn no_available_rewards<'info>(
  1177. reward_q: &Account<'info, RewardQueue>,
  1178. member: &Account<'info, Member>,
  1179. balances: &BalanceSandboxAccounts<'info>,
  1180. balances_locked: &BalanceSandboxAccounts<'info>,
  1181. ) -> Result<()> {
  1182. let mut cursor = member.rewards_cursor;
  1183. // If the member's cursor is less then the tail, then the ring buffer has
  1184. // overwritten those entries, so jump to the tail.
  1185. let tail = reward_q.tail();
  1186. if cursor < tail {
  1187. cursor = tail;
  1188. }
  1189. while cursor < reward_q.head() {
  1190. let r_event = reward_q.get(cursor);
  1191. if member.last_stake_ts < r_event.ts {
  1192. if balances.spt.amount > 0 || balances_locked.spt.amount > 0 {
  1193. return Err(ErrorCode::RewardsNeedsProcessing.into());
  1194. }
  1195. }
  1196. cursor += 1;
  1197. }
  1198. Ok(())
  1199. }