lib.rs 46 KB

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