lib.rs 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341
  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. #[account(signer)]
  534. authority: AccountInfo<'info>,
  535. }
  536. #[derive(Accounts)]
  537. pub struct CreateMember<'info> {
  538. // Stake instance.
  539. registrar: Box<Account<'info, Registrar>>,
  540. // Member.
  541. #[account(zero)]
  542. member: Box<Account<'info, Member>>,
  543. #[account(signer)]
  544. beneficiary: AccountInfo<'info>,
  545. #[account(
  546. "&balances.spt.owner == member_signer.key",
  547. "balances.spt.mint == registrar.pool_mint",
  548. "balances.vault.mint == registrar.mint"
  549. )]
  550. balances: BalanceSandboxAccounts<'info>,
  551. #[account(
  552. "&balances_locked.spt.owner == member_signer.key",
  553. "balances_locked.spt.mint == registrar.pool_mint",
  554. "balances_locked.vault.mint == registrar.mint"
  555. )]
  556. balances_locked: BalanceSandboxAccounts<'info>,
  557. member_signer: AccountInfo<'info>,
  558. // Misc.
  559. #[account("token_program.key == &token::ID")]
  560. token_program: AccountInfo<'info>,
  561. }
  562. impl<'info> CreateMember<'info> {
  563. fn accounts(ctx: &Context<CreateMember>, nonce: u8) -> Result<()> {
  564. let seeds = &[
  565. ctx.accounts.registrar.to_account_info().key.as_ref(),
  566. ctx.accounts.member.to_account_info().key.as_ref(),
  567. &[nonce],
  568. ];
  569. let member_signer = Pubkey::create_program_address(seeds, ctx.program_id)
  570. .map_err(|_| ErrorCode::InvalidNonce)?;
  571. if &member_signer != ctx.accounts.member_signer.to_account_info().key {
  572. return Err(ErrorCode::InvalidMemberSigner.into());
  573. }
  574. Ok(())
  575. }
  576. }
  577. // When creating a member, the mints and owners of these accounts are correct.
  578. // Upon creation, we assign the accounts. A onetime operation.
  579. // When using a member, we check these accounts addresess are equal to the
  580. // addresses stored on the member. If so, the correct accounts were given are
  581. // correct.
  582. #[derive(Accounts, Clone)]
  583. pub struct BalanceSandboxAccounts<'info> {
  584. #[account(mut)]
  585. spt: Box<Account<'info, TokenAccount>>,
  586. #[account(mut, constraint = vault.owner == spt.owner)]
  587. vault: Box<Account<'info, TokenAccount>>,
  588. #[account(
  589. mut,
  590. constraint = vault_stake.owner == spt.owner,
  591. constraint = vault_stake.mint == vault.mint
  592. )]
  593. vault_stake: Box<Account<'info, TokenAccount>>,
  594. #[account(mut, constraint = vault_pw.owner == spt.owner, constraint = vault_pw.mint == vault.mint)]
  595. vault_pw: Box<Account<'info, TokenAccount>>,
  596. }
  597. #[derive(Accounts)]
  598. pub struct Ctor<'info> {
  599. lockup_program: AccountInfo<'info>,
  600. }
  601. #[derive(Accounts)]
  602. pub struct SetLockupProgram<'info> {
  603. #[account(signer)]
  604. authority: AccountInfo<'info>,
  605. }
  606. #[derive(Accounts)]
  607. pub struct IsRealized<'info> {
  608. #[account(
  609. constraint = &member.balances.spt == member_spt.to_account_info().key,
  610. constraint = &member.balances_locked.spt == member_spt_locked.to_account_info().key
  611. )]
  612. member: Account<'info, Member>,
  613. member_spt: Account<'info, TokenAccount>,
  614. member_spt_locked: Account<'info, TokenAccount>,
  615. }
  616. #[derive(Accounts)]
  617. pub struct UpdateMember<'info> {
  618. #[account(mut, has_one = beneficiary)]
  619. member: Account<'info, Member>,
  620. #[account(signer)]
  621. beneficiary: AccountInfo<'info>,
  622. }
  623. #[derive(Accounts)]
  624. pub struct Deposit<'info> {
  625. // Member.
  626. #[account(has_one = beneficiary)]
  627. member: Account<'info, Member>,
  628. #[account(signer)]
  629. beneficiary: AccountInfo<'info>,
  630. #[account(mut, constraint = vault.to_account_info().key == &member.balances.vault)]
  631. vault: Account<'info, TokenAccount>,
  632. // Depositor.
  633. #[account(mut)]
  634. depositor: AccountInfo<'info>,
  635. #[account(signer, constraint = depositor_authority.key == &member.beneficiary)]
  636. depositor_authority: AccountInfo<'info>,
  637. // Misc.
  638. #[account(constraint = token_program.key == &token::ID)]
  639. token_program: AccountInfo<'info>,
  640. }
  641. #[derive(Accounts)]
  642. pub struct DepositLocked<'info> {
  643. // Lockup whitelist relay interface.
  644. #[account(
  645. constraint = vesting.to_account_info().owner == &registry.lockup_program,
  646. constraint = vesting.beneficiary == member.beneficiary
  647. )]
  648. vesting: Box<Account<'info, Vesting>>,
  649. #[account(mut, constraint = vesting_vault.key == &vesting.vault)]
  650. vesting_vault: AccountInfo<'info>,
  651. // Note: no need to verify the depositor_authority since the SPL program
  652. // will fail the transaction if it's not correct.
  653. #[account(signer)]
  654. depositor_authority: AccountInfo<'info>,
  655. #[account(constraint = token_program.key == &token::ID)]
  656. token_program: AccountInfo<'info>,
  657. #[account(
  658. mut,
  659. constraint = member_vault.to_account_info().key == &member.balances_locked.vault
  660. )]
  661. member_vault: Box<Account<'info, TokenAccount>>,
  662. #[account(
  663. seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()],
  664. bump = member.nonce,
  665. )]
  666. member_signer: AccountInfo<'info>,
  667. // Program specific.
  668. registry: ProgramState<'info, Registry>,
  669. registrar: Box<Account<'info, Registrar>>,
  670. #[account(has_one = registrar, has_one = beneficiary)]
  671. member: Box<Account<'info, Member>>,
  672. #[account(signer)]
  673. beneficiary: AccountInfo<'info>,
  674. }
  675. #[derive(Accounts)]
  676. pub struct Stake<'info> {
  677. // Global accounts for the staking instance.
  678. #[account(has_one = pool_mint, has_one = reward_event_q)]
  679. registrar: Account<'info, Registrar>,
  680. reward_event_q: Account<'info, RewardQueue>,
  681. #[account(mut)]
  682. pool_mint: Account<'info, Mint>,
  683. // Member.
  684. #[account(mut, has_one = beneficiary, has_one = registrar)]
  685. member: Account<'info, Member>,
  686. #[account(signer)]
  687. beneficiary: AccountInfo<'info>,
  688. #[account(constraint = BalanceSandbox::from(&balances) == member.balances)]
  689. balances: BalanceSandboxAccounts<'info>,
  690. #[account(constraint = BalanceSandbox::from(&balances_locked) == member.balances_locked)]
  691. balances_locked: BalanceSandboxAccounts<'info>,
  692. // Program signers.
  693. #[account(
  694. seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()],
  695. bump = member.nonce,
  696. )]
  697. member_signer: AccountInfo<'info>,
  698. #[account(
  699. seeds = [registrar.to_account_info().key.as_ref()],
  700. bump = registrar.nonce,
  701. )]
  702. registrar_signer: AccountInfo<'info>,
  703. // Misc.
  704. clock: Sysvar<'info, Clock>,
  705. #[account(constraint = token_program.key == &token::ID)]
  706. token_program: AccountInfo<'info>,
  707. }
  708. #[derive(Accounts)]
  709. pub struct StartUnstake<'info> {
  710. // Stake instance globals.
  711. #[account(has_one = reward_event_q)]
  712. registrar: Account<'info, Registrar>,
  713. reward_event_q: Account<'info, RewardQueue>,
  714. #[account(mut)]
  715. pool_mint: AccountInfo<'info>,
  716. // Member.
  717. #[account(zero)]
  718. pending_withdrawal: Account<'info, PendingWithdrawal>,
  719. #[account(has_one = beneficiary, has_one = registrar)]
  720. member: Account<'info, Member>,
  721. #[account(signer)]
  722. beneficiary: AccountInfo<'info>,
  723. #[account(constraint = BalanceSandbox::from(&balances) == member.balances)]
  724. balances: BalanceSandboxAccounts<'info>,
  725. #[account(constraint = BalanceSandbox::from(&balances_locked) == member.balances_locked)]
  726. balances_locked: BalanceSandboxAccounts<'info>,
  727. // Programmatic signers.
  728. #[account(
  729. seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()],
  730. bump = member.nonce,
  731. )]
  732. member_signer: AccountInfo<'info>,
  733. // Misc.
  734. #[account(constraint = token_program.key == &token::ID)]
  735. token_program: AccountInfo<'info>,
  736. clock: Sysvar<'info, Clock>,
  737. }
  738. #[derive(Accounts)]
  739. pub struct EndUnstake<'info> {
  740. registrar: Account<'info, Registrar>,
  741. #[account(has_one = registrar, has_one = beneficiary)]
  742. member: Account<'info, Member>,
  743. #[account(signer)]
  744. beneficiary: AccountInfo<'info>,
  745. #[account(mut, has_one = registrar, has_one = member, constraint = !pending_withdrawal.burned)]
  746. pending_withdrawal: Account<'info, PendingWithdrawal>,
  747. // If we had ordered maps implementing Accounts we could do a constraint like
  748. // balances.get(pending_withdrawal.balance_id).vault == vault.key.
  749. //
  750. // Note: we do the constraints check in the handler, not here.
  751. #[account(mut)]
  752. vault: AccountInfo<'info>,
  753. #[account(mut)]
  754. vault_pw: AccountInfo<'info>,
  755. #[account(
  756. seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()],
  757. bump = member.nonce,
  758. )]
  759. member_signer: AccountInfo<'info>,
  760. clock: Sysvar<'info, Clock>,
  761. #[account(constraint = token_program.key == &token::ID)]
  762. token_program: AccountInfo<'info>,
  763. }
  764. #[derive(Accounts)]
  765. pub struct Withdraw<'info> {
  766. // Stake instance.
  767. registrar: Account<'info, Registrar>,
  768. // Member.
  769. #[account(has_one = registrar, has_one = beneficiary)]
  770. member: Account<'info, Member>,
  771. #[account(signer)]
  772. beneficiary: AccountInfo<'info>,
  773. #[account(mut, constraint = vault.to_account_info().key == &member.balances.vault)]
  774. vault: Account<'info, TokenAccount>,
  775. #[account(
  776. seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()],
  777. bump = member.nonce,
  778. )]
  779. member_signer: AccountInfo<'info>,
  780. // Receiver.
  781. #[account(mut)]
  782. depositor: AccountInfo<'info>,
  783. // Misc.
  784. #[account(constraint = token_program.key == &token::ID)]
  785. token_program: AccountInfo<'info>,
  786. }
  787. #[derive(Accounts)]
  788. pub struct WithdrawLocked<'info> {
  789. // Lockup whitelist relay interface.
  790. #[account(
  791. constraint = vesting.to_account_info().owner == &registry.lockup_program,
  792. constraint = vesting.beneficiary == member.beneficiary,
  793. )]
  794. vesting: Box<Account<'info, Vesting>>,
  795. #[account(mut, constraint = vesting_vault.key == &vesting.vault)]
  796. vesting_vault: AccountInfo<'info>,
  797. #[account(signer)]
  798. vesting_signer: AccountInfo<'info>,
  799. #[account(constraint = token_program.key == &token::ID)]
  800. token_program: AccountInfo<'info>,
  801. #[account(
  802. mut,
  803. constraint = member_vault.to_account_info().key == &member.balances_locked.vault
  804. )]
  805. member_vault: Box<Account<'info, TokenAccount>>,
  806. #[account(
  807. seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()],
  808. bump = member.nonce,
  809. )]
  810. member_signer: AccountInfo<'info>,
  811. // Program specific.
  812. registry: ProgramState<'info, Registry>,
  813. registrar: Box<Account<'info, Registrar>>,
  814. #[account(has_one = registrar, has_one = beneficiary)]
  815. member: Box<Account<'info, Member>>,
  816. #[account(signer)]
  817. beneficiary: AccountInfo<'info>,
  818. }
  819. #[derive(Accounts)]
  820. pub struct DropReward<'info> {
  821. // Staking instance.
  822. #[account(has_one = reward_event_q, has_one = pool_mint)]
  823. registrar: Account<'info, Registrar>,
  824. #[account(mut)]
  825. reward_event_q: Account<'info, RewardQueue>,
  826. pool_mint: Account<'info, Mint>,
  827. // Vendor.
  828. #[account(zero)]
  829. vendor: Account<'info, RewardVendor>,
  830. #[account(mut)]
  831. vendor_vault: Account<'info, TokenAccount>,
  832. // Depositor.
  833. #[account(mut)]
  834. depositor: AccountInfo<'info>,
  835. #[account(signer)]
  836. depositor_authority: AccountInfo<'info>,
  837. // Misc.
  838. #[account(constraint = token_program.key == &token::ID)]
  839. token_program: AccountInfo<'info>,
  840. clock: Sysvar<'info, Clock>,
  841. }
  842. impl<'info> DropReward<'info> {
  843. fn accounts(ctx: &Context<DropReward>, nonce: u8) -> Result<()> {
  844. let vendor_signer = Pubkey::create_program_address(
  845. &[
  846. ctx.accounts.registrar.to_account_info().key.as_ref(),
  847. ctx.accounts.vendor.to_account_info().key.as_ref(),
  848. &[nonce],
  849. ],
  850. ctx.program_id,
  851. )
  852. .map_err(|_| ErrorCode::InvalidNonce)?;
  853. if vendor_signer != ctx.accounts.vendor_vault.owner {
  854. return Err(ErrorCode::InvalidVaultOwner.into());
  855. }
  856. Ok(())
  857. }
  858. }
  859. #[derive(Accounts)]
  860. pub struct ClaimReward<'info> {
  861. cmn: ClaimRewardCommon<'info>,
  862. // Account to send reward to.
  863. #[account(mut)]
  864. to: AccountInfo<'info>,
  865. }
  866. #[derive(Accounts)]
  867. pub struct ClaimRewardLocked<'info> {
  868. cmn: ClaimRewardCommon<'info>,
  869. registry: ProgramState<'info, Registry>,
  870. #[account("lockup_program.key == &registry.lockup_program")]
  871. lockup_program: AccountInfo<'info>,
  872. }
  873. // Accounts common to both claim reward locked/unlocked instructions.
  874. #[derive(Accounts)]
  875. pub struct ClaimRewardCommon<'info> {
  876. // Stake instance.
  877. registrar: Account<'info, Registrar>,
  878. // Member.
  879. #[account(mut, has_one = registrar, has_one = beneficiary)]
  880. member: Account<'info, Member>,
  881. #[account(signer)]
  882. beneficiary: AccountInfo<'info>,
  883. #[account(constraint = BalanceSandbox::from(&balances) == member.balances)]
  884. balances: BalanceSandboxAccounts<'info>,
  885. #[account(constraint = BalanceSandbox::from(&balances_locked) == member.balances_locked)]
  886. balances_locked: BalanceSandboxAccounts<'info>,
  887. // Vendor.
  888. #[account(has_one = registrar, has_one = vault)]
  889. vendor: Account<'info, RewardVendor>,
  890. #[account(mut)]
  891. vault: AccountInfo<'info>,
  892. #[account(
  893. seeds = [registrar.to_account_info().key.as_ref(), vendor.to_account_info().key.as_ref()],
  894. bump = vendor.nonce,
  895. )]
  896. vendor_signer: AccountInfo<'info>,
  897. // Misc.
  898. #[account(constraint = token_program.key == &token::ID)]
  899. token_program: AccountInfo<'info>,
  900. clock: Sysvar<'info, Clock>,
  901. }
  902. #[derive(Accounts)]
  903. pub struct ExpireReward<'info> {
  904. // Staking instance globals.
  905. registrar: Account<'info, Registrar>,
  906. // Vendor.
  907. #[account(mut, has_one = registrar, has_one = vault, has_one = expiry_receiver)]
  908. vendor: Account<'info, RewardVendor>,
  909. #[account(mut)]
  910. vault: Account<'info, TokenAccount>,
  911. #[account(
  912. seeds = [registrar.to_account_info().key.as_ref(), vendor.to_account_info().key.as_ref()],
  913. bump = vendor.nonce
  914. )]
  915. vendor_signer: AccountInfo<'info>,
  916. // Receiver.
  917. #[account(signer)]
  918. expiry_receiver: AccountInfo<'info>,
  919. #[account(mut)]
  920. expiry_receiver_token: AccountInfo<'info>,
  921. // Misc.
  922. #[account(constraint = token_program.key == &token::ID)]
  923. token_program: AccountInfo<'info>,
  924. clock: Sysvar<'info, Clock>,
  925. }
  926. #[account]
  927. pub struct Registrar {
  928. /// Priviledged account.
  929. pub authority: Pubkey,
  930. /// Nonce to derive the program-derived address owning the vaults.
  931. pub nonce: u8,
  932. /// Number of seconds that must pass for a withdrawal to complete.
  933. pub withdrawal_timelock: i64,
  934. /// Global event queue for reward vendoring.
  935. pub reward_event_q: Pubkey,
  936. /// Mint of the tokens that can be staked.
  937. pub mint: Pubkey,
  938. /// Staking pool token mint.
  939. pub pool_mint: Pubkey,
  940. /// The amount of tokens (not decimal) that must be staked to get a single
  941. /// staking pool token.
  942. pub stake_rate: u64,
  943. }
  944. #[account]
  945. pub struct Member {
  946. /// Registrar the member belongs to.
  947. pub registrar: Pubkey,
  948. /// The effective owner of the Member account.
  949. pub beneficiary: Pubkey,
  950. /// Arbitrary metadata account owned by any program.
  951. pub metadata: Pubkey,
  952. /// Sets of balances owned by the Member.
  953. pub balances: BalanceSandbox,
  954. /// Locked balances owned by the Member.
  955. pub balances_locked: BalanceSandbox,
  956. /// Next position in the rewards event queue to process.
  957. pub rewards_cursor: u32,
  958. /// The clock timestamp of the last time this account staked or switched
  959. /// entities. Used as a proof to reward vendors that the Member account
  960. /// was staked at a given point in time.
  961. pub last_stake_ts: i64,
  962. /// Signer nonce.
  963. pub nonce: u8,
  964. }
  965. // BalanceSandbox defines isolated funds that can only be deposited/withdrawn
  966. // into the program.
  967. //
  968. // Once controlled by the program, the associated `Member` account's beneficiary
  969. // can send funds to/from any of the accounts within the sandbox, e.g., to
  970. // stake.
  971. #[derive(AnchorSerialize, AnchorDeserialize, Default, Debug, Clone, PartialEq)]
  972. pub struct BalanceSandbox {
  973. // Staking pool token.
  974. pub spt: Pubkey,
  975. // Free balance (deposit) vaults.
  976. pub vault: Pubkey,
  977. // Stake vaults.
  978. pub vault_stake: Pubkey,
  979. // Pending withdrawal vaults.
  980. pub vault_pw: Pubkey,
  981. }
  982. #[account]
  983. pub struct PendingWithdrawal {
  984. /// Registrar this account belongs to.
  985. pub registrar: Pubkey,
  986. /// Member this account belongs to.
  987. pub member: Pubkey,
  988. /// One time token. True if the withdrawal has been completed.
  989. pub burned: bool,
  990. /// The pool being withdrawn from.
  991. pub pool: Pubkey,
  992. /// Unix timestamp when this account was initialized.
  993. pub start_ts: i64,
  994. /// Timestamp when the pending withdrawal completes.
  995. pub end_ts: i64,
  996. /// The number of tokens redeemed from the staking pool.
  997. pub amount: u64,
  998. /// True if the withdrawal applies to locked balances.
  999. pub locked: bool,
  1000. }
  1001. #[account]
  1002. pub struct RewardQueue {
  1003. // Invariant: index is position of the next available slot.
  1004. head: u32,
  1005. // Invariant: index is position of the first (oldest) taken slot.
  1006. // Invariant: head == tail => queue is initialized.
  1007. // Invariant: index_of(head + 1) == index_of(tail) => queue is full.
  1008. tail: u32,
  1009. // Although a vec is used, the size is immutable.
  1010. events: Vec<RewardEvent>,
  1011. }
  1012. impl RewardQueue {
  1013. pub fn append(&mut self, event: RewardEvent) -> Result<u32> {
  1014. let cursor = self.head;
  1015. // Insert into next available slot.
  1016. let h_idx = self.index_of(self.head);
  1017. self.events[h_idx] = event;
  1018. // Update head and tail counters.
  1019. let is_full = self.index_of(self.head + 1) == self.index_of(self.tail);
  1020. if is_full {
  1021. self.tail += 1;
  1022. }
  1023. self.head += 1;
  1024. Ok(cursor)
  1025. }
  1026. pub fn index_of(&self, counter: u32) -> usize {
  1027. counter as usize % self.capacity()
  1028. }
  1029. pub fn capacity(&self) -> usize {
  1030. self.events.len()
  1031. }
  1032. pub fn get(&self, cursor: u32) -> &RewardEvent {
  1033. &self.events[cursor as usize % self.capacity()]
  1034. }
  1035. pub fn head(&self) -> u32 {
  1036. self.head
  1037. }
  1038. pub fn tail(&self) -> u32 {
  1039. self.tail
  1040. }
  1041. }
  1042. #[derive(Default, Clone, Copy, Debug, AnchorSerialize, AnchorDeserialize)]
  1043. pub struct RewardEvent {
  1044. vendor: Pubkey,
  1045. ts: i64,
  1046. locked: bool,
  1047. }
  1048. #[account]
  1049. pub struct RewardVendor {
  1050. pub registrar: Pubkey,
  1051. pub vault: Pubkey,
  1052. pub mint: Pubkey,
  1053. pub nonce: u8,
  1054. pub pool_token_supply: u64,
  1055. pub reward_event_q_cursor: u32,
  1056. pub start_ts: i64,
  1057. pub expiry_ts: i64,
  1058. pub expiry_receiver: Pubkey,
  1059. pub from: Pubkey,
  1060. pub total: u64,
  1061. pub expired: bool,
  1062. pub kind: RewardVendorKind,
  1063. }
  1064. #[derive(AnchorSerialize, AnchorDeserialize, Clone, PartialEq)]
  1065. pub enum RewardVendorKind {
  1066. Unlocked,
  1067. Locked {
  1068. start_ts: i64,
  1069. end_ts: i64,
  1070. period_count: u64,
  1071. },
  1072. }
  1073. #[error]
  1074. pub enum ErrorCode {
  1075. #[msg("The given reward queue has already been initialized.")]
  1076. RewardQAlreadyInitialized,
  1077. #[msg("The nonce given doesn't derive a valid program address.")]
  1078. InvalidNonce,
  1079. #[msg("Invalid pool mint authority")]
  1080. InvalidPoolMintAuthority,
  1081. #[msg("Member signer doesn't match the derived address.")]
  1082. InvalidMemberSigner,
  1083. #[msg("The given vault owner must match the signing depositor.")]
  1084. InvalidVaultDeposit,
  1085. #[msg("The signing depositor doesn't match either of the balance accounts")]
  1086. InvalidDepositor,
  1087. #[msg("The vault given does not match the vault expected.")]
  1088. InvalidVault,
  1089. #[msg("Invalid vault owner.")]
  1090. InvalidVaultOwner,
  1091. #[msg("An unknown error has occured.")]
  1092. Unknown,
  1093. #[msg("The unstake timelock has not yet expired.")]
  1094. UnstakeTimelock,
  1095. #[msg("Reward vendors must have at least one token unit per pool token")]
  1096. InsufficientReward,
  1097. #[msg("Reward expiry must be after the current clock timestamp.")]
  1098. InvalidExpiry,
  1099. #[msg("The reward vendor has been expired.")]
  1100. VendorExpired,
  1101. #[msg("This reward has already been processed.")]
  1102. CursorAlreadyProcessed,
  1103. #[msg("The account was not staked at the time of this reward.")]
  1104. NotStakedDuringDrop,
  1105. #[msg("The vendor is not yet eligible for expiry.")]
  1106. VendorNotYetExpired,
  1107. #[msg("Please collect your reward before otherwise using the program.")]
  1108. RewardsNeedsProcessing,
  1109. #[msg("Locked reward vendor expected but an unlocked vendor was given.")]
  1110. ExpectedLockedVendor,
  1111. #[msg("Unlocked reward vendor expected but a locked vendor was given.")]
  1112. ExpectedUnlockedVendor,
  1113. #[msg("Locked deposit from an invalid deposit authority.")]
  1114. InvalidVestingSigner,
  1115. #[msg("Locked rewards cannot be realized until one unstaked all tokens.")]
  1116. UnrealizedReward,
  1117. #[msg("The beneficiary doesn't match.")]
  1118. InvalidBeneficiary,
  1119. #[msg("The given member account does not match the realizor metadata.")]
  1120. InvalidRealizorMetadata,
  1121. #[msg("Invalid vesting schedule for the locked reward.")]
  1122. InvalidVestingSchedule,
  1123. #[msg("Please specify the correct authority for this program.")]
  1124. InvalidProgramAuthority,
  1125. }
  1126. impl<'a, 'b, 'c, 'info> From<&mut Deposit<'info>>
  1127. for CpiContext<'a, 'b, 'c, 'info, Transfer<'info>>
  1128. {
  1129. fn from(accounts: &mut Deposit<'info>) -> CpiContext<'a, 'b, 'c, 'info, Transfer<'info>> {
  1130. let cpi_accounts = Transfer {
  1131. from: accounts.depositor.clone(),
  1132. to: accounts.vault.to_account_info(),
  1133. authority: accounts.depositor_authority.clone(),
  1134. };
  1135. let cpi_program = accounts.token_program.clone();
  1136. CpiContext::new(cpi_program, cpi_accounts)
  1137. }
  1138. }
  1139. impl<'a, 'b, 'c, 'info> From<&mut DepositLocked<'info>>
  1140. for CpiContext<'a, 'b, 'c, 'info, Transfer<'info>>
  1141. {
  1142. fn from(accounts: &mut DepositLocked<'info>) -> CpiContext<'a, 'b, 'c, 'info, Transfer<'info>> {
  1143. let cpi_accounts = Transfer {
  1144. from: accounts.vesting_vault.clone(),
  1145. to: accounts.member_vault.to_account_info(),
  1146. authority: accounts.depositor_authority.clone(),
  1147. };
  1148. let cpi_program = accounts.token_program.clone();
  1149. CpiContext::new(cpi_program, cpi_accounts)
  1150. }
  1151. }
  1152. impl<'a, 'b, 'c, 'info> From<&mut DropReward<'info>>
  1153. for CpiContext<'a, 'b, 'c, 'info, Transfer<'info>>
  1154. {
  1155. fn from(accounts: &mut DropReward<'info>) -> CpiContext<'a, 'b, 'c, 'info, Transfer<'info>> {
  1156. let cpi_accounts = Transfer {
  1157. from: accounts.depositor.clone(),
  1158. to: accounts.vendor_vault.to_account_info(),
  1159. authority: accounts.depositor_authority.clone(),
  1160. };
  1161. let cpi_program = accounts.token_program.clone();
  1162. CpiContext::new(cpi_program, cpi_accounts)
  1163. }
  1164. }
  1165. impl<'info> From<&BalanceSandboxAccounts<'info>> for BalanceSandbox {
  1166. fn from(accs: &BalanceSandboxAccounts<'info>) -> Self {
  1167. Self {
  1168. spt: *accs.spt.to_account_info().key,
  1169. vault: *accs.vault.to_account_info().key,
  1170. vault_stake: *accs.vault_stake.to_account_info().key,
  1171. vault_pw: *accs.vault_pw.to_account_info().key,
  1172. }
  1173. }
  1174. }
  1175. fn reward_eligible(cmn: &ClaimRewardCommon) -> Result<()> {
  1176. let vendor = &cmn.vendor;
  1177. let member = &cmn.member;
  1178. if vendor.expired {
  1179. return Err(ErrorCode::VendorExpired.into());
  1180. }
  1181. if member.rewards_cursor > vendor.reward_event_q_cursor {
  1182. return Err(ErrorCode::CursorAlreadyProcessed.into());
  1183. }
  1184. if member.last_stake_ts > vendor.start_ts {
  1185. return Err(ErrorCode::NotStakedDuringDrop.into());
  1186. }
  1187. Ok(())
  1188. }
  1189. // Asserts the user calling the `Stake` instruction has no rewards available
  1190. // in the reward queue.
  1191. pub fn no_available_rewards<'info>(
  1192. reward_q: &Account<'info, RewardQueue>,
  1193. member: &Account<'info, Member>,
  1194. balances: &BalanceSandboxAccounts<'info>,
  1195. balances_locked: &BalanceSandboxAccounts<'info>,
  1196. ) -> Result<()> {
  1197. let mut cursor = member.rewards_cursor;
  1198. // If the member's cursor is less then the tail, then the ring buffer has
  1199. // overwritten those entries, so jump to the tail.
  1200. let tail = reward_q.tail();
  1201. if cursor < tail {
  1202. cursor = tail;
  1203. }
  1204. while cursor < reward_q.head() {
  1205. let r_event = reward_q.get(cursor);
  1206. if member.last_stake_ts < r_event.ts {
  1207. if balances.spt.amount > 0 || balances_locked.spt.amount > 0 {
  1208. return Err(ErrorCode::RewardsNeedsProcessing.into());
  1209. }
  1210. }
  1211. cursor += 1;
  1212. }
  1213. Ok(())
  1214. }