1use anchor_lang::prelude::*;
41use anchor_spl::token::{self, Mint, Token, TokenAccount, Transfer};
42
43pub mod events;
45pub mod services;
46
47use events::{
49 Deposited, EscrowCancelled, EscrowCreated, EscrowReleased, LockExtended, TreasuryInitialized,
50 VaultCreated, Withdrawn,
51};
52
53declare_id!("HmWGA3JAF6basxGCvvGNHAdTBE3qCPhJCeFJAd7r5ra9");
54
55#[program]
63pub mod tribewarez_vault {
64 use super::*;
65
66 pub fn initialize_treasury(ctx: Context<InitializeTreasury>, treasury_bump: u8) -> Result<()> {
68 let treasury = &mut ctx.accounts.treasury;
69
70 treasury.authority = ctx.accounts.authority.key();
71 treasury.token_mint = ctx.accounts.token_mint.key();
72 treasury.vault_token_account = ctx.accounts.vault_token_account.key();
73 treasury.total_deposited = 0;
74 treasury.total_vaults = 0;
75 treasury.bump = treasury_bump;
76 treasury.is_active = true;
77 treasury.created_at = Clock::get()?.unix_timestamp;
78
79 emit!(TreasuryInitialized {
80 treasury: treasury.key(),
81 authority: treasury.authority,
82 token_mint: treasury.token_mint,
83 });
84
85 Ok(())
86 }
87
88 pub fn create_vault(
90 ctx: Context<CreateVault>,
91 vault_name: String,
92 lock_until: i64, ) -> Result<()> {
94 require!(vault_name.len() <= 32, VaultError::NameTooLong);
95
96 let vault = &mut ctx.accounts.user_vault;
97 let treasury = &mut ctx.accounts.treasury;
98
99 vault.owner = ctx.accounts.user.key();
100 vault.treasury = treasury.key();
101 vault.name = vault_name.clone();
102 vault.balance = 0;
103 vault.lock_until = lock_until;
104 vault.created_at = Clock::get()?.unix_timestamp;
105 vault.last_activity = Clock::get()?.unix_timestamp;
106 vault.is_locked = lock_until > 0;
107 vault.total_deposited = 0;
108 vault.total_withdrawn = 0;
109
110 treasury.total_vaults = treasury
111 .total_vaults
112 .checked_add(1)
113 .ok_or(VaultError::MathOverflow)?;
114
115 emit!(VaultCreated {
116 vault: vault.key(),
117 owner: vault.owner,
118 name: vault_name,
119 lock_until,
120 });
121
122 Ok(())
123 }
124
125 pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {
127 require!(amount > 0, VaultError::InvalidAmount);
128 require!(
129 ctx.accounts.treasury.is_active,
130 VaultError::TreasuryInactive
131 );
132
133 let vault = &mut ctx.accounts.user_vault;
134 let treasury = &mut ctx.accounts.treasury;
135
136 let cpi_accounts = Transfer {
138 from: ctx.accounts.user_token_account.to_account_info(),
139 to: ctx.accounts.vault_token_account.to_account_info(),
140 authority: ctx.accounts.user.to_account_info(),
141 };
142 let cpi_ctx = CpiContext::new(ctx.accounts.token_program.to_account_info(), cpi_accounts);
143 token::transfer(cpi_ctx, amount)?;
144
145 vault.balance = vault
147 .balance
148 .checked_add(amount)
149 .ok_or(VaultError::MathOverflow)?;
150 vault.total_deposited = vault
151 .total_deposited
152 .checked_add(amount)
153 .ok_or(VaultError::MathOverflow)?;
154 vault.last_activity = Clock::get()?.unix_timestamp;
155
156 treasury.total_deposited = treasury
158 .total_deposited
159 .checked_add(amount)
160 .ok_or(VaultError::MathOverflow)?;
161
162 emit!(Deposited {
163 vault: vault.key(),
164 user: ctx.accounts.user.key(),
165 amount,
166 new_balance: vault.balance,
167 });
168
169 Ok(())
170 }
171
172 pub fn withdraw(ctx: Context<Withdraw>, amount: u64) -> Result<()> {
174 require!(amount > 0, VaultError::InvalidAmount);
175
176 let clock = Clock::get()?;
177 let vault = &mut ctx.accounts.user_vault;
178 let treasury = &ctx.accounts.treasury;
179
180 require!(vault.balance >= amount, VaultError::InsufficientBalance);
181 require!(
182 !vault.is_locked || clock.unix_timestamp >= vault.lock_until,
183 VaultError::VaultLocked
184 );
185
186 let token_mint = treasury.token_mint;
188 let seeds = &[b"treasury", token_mint.as_ref(), &[treasury.bump]];
189 let signer = &[&seeds[..]];
190
191 let cpi_accounts = Transfer {
192 from: ctx.accounts.vault_token_account.to_account_info(),
193 to: ctx.accounts.user_token_account.to_account_info(),
194 authority: ctx.accounts.treasury.to_account_info(),
195 };
196 let cpi_ctx = CpiContext::new_with_signer(
197 ctx.accounts.token_program.to_account_info(),
198 cpi_accounts,
199 signer,
200 );
201 token::transfer(cpi_ctx, amount)?;
202
203 vault.balance = vault
205 .balance
206 .checked_sub(amount)
207 .ok_or(VaultError::MathOverflow)?;
208 vault.total_withdrawn = vault
209 .total_withdrawn
210 .checked_add(amount)
211 .ok_or(VaultError::MathOverflow)?;
212 vault.last_activity = clock.unix_timestamp;
213
214 emit!(Withdrawn {
215 vault: vault.key(),
216 user: ctx.accounts.user.key(),
217 amount,
218 new_balance: vault.balance,
219 });
220
221 Ok(())
222 }
223
224 pub fn create_escrow(
226 ctx: Context<CreateEscrow>,
227 amount: u64,
228 release_time: i64,
229 escrow_bump: u8,
230 ) -> Result<()> {
231 require!(amount > 0, VaultError::InvalidAmount);
232 require!(
233 release_time > Clock::get()?.unix_timestamp,
234 VaultError::InvalidReleaseTime
235 );
236
237 let escrow = &mut ctx.accounts.escrow;
238
239 let cpi_accounts = Transfer {
241 from: ctx.accounts.depositor_token_account.to_account_info(),
242 to: ctx.accounts.escrow_token_account.to_account_info(),
243 authority: ctx.accounts.depositor.to_account_info(),
244 };
245 let cpi_ctx = CpiContext::new(ctx.accounts.token_program.to_account_info(), cpi_accounts);
246 token::transfer(cpi_ctx, amount)?;
247
248 escrow.depositor = ctx.accounts.depositor.key();
249 escrow.beneficiary = ctx.accounts.beneficiary.key();
250 escrow.token_mint = ctx.accounts.token_mint.key();
251 escrow.escrow_token_account = ctx.accounts.escrow_token_account.key();
252 escrow.amount = amount;
253 escrow.release_time = release_time;
254 escrow.created_at = Clock::get()?.unix_timestamp;
255 escrow.is_released = false;
256 escrow.is_cancelled = false;
257 escrow.bump = escrow_bump;
258
259 emit!(EscrowCreated {
260 escrow: escrow.key(),
261 depositor: escrow.depositor,
262 beneficiary: escrow.beneficiary,
263 amount,
264 release_time,
265 });
266
267 Ok(())
268 }
269
270 pub fn release_escrow(ctx: Context<ReleaseEscrow>) -> Result<()> {
272 let clock = Clock::get()?;
273 let escrow = &mut ctx.accounts.escrow;
274
275 require!(!escrow.is_released, VaultError::AlreadyReleased);
276 require!(!escrow.is_cancelled, VaultError::EscrowCancelled);
277 require!(
278 clock.unix_timestamp >= escrow.release_time,
279 VaultError::EscrowNotReady
280 );
281
282 let depositor = escrow.depositor;
284 let beneficiary = escrow.beneficiary;
285 let seeds = &[
286 b"escrow",
287 depositor.as_ref(),
288 beneficiary.as_ref(),
289 &[escrow.bump],
290 ];
291 let signer = &[&seeds[..]];
292
293 let cpi_accounts = Transfer {
294 from: ctx.accounts.escrow_token_account.to_account_info(),
295 to: ctx.accounts.beneficiary_token_account.to_account_info(),
296 authority: escrow.to_account_info(),
297 };
298 let cpi_ctx = CpiContext::new_with_signer(
299 ctx.accounts.token_program.to_account_info(),
300 cpi_accounts,
301 signer,
302 );
303 token::transfer(cpi_ctx, escrow.amount)?;
304
305 escrow.is_released = true;
306
307 emit!(EscrowReleased {
308 escrow: escrow.key(),
309 beneficiary: escrow.beneficiary,
310 amount: escrow.amount,
311 });
312
313 Ok(())
314 }
315
316 pub fn cancel_escrow(ctx: Context<CancelEscrow>) -> Result<()> {
318 let clock = Clock::get()?;
319 let escrow = &mut ctx.accounts.escrow;
320
321 require!(!escrow.is_released, VaultError::AlreadyReleased);
322 require!(!escrow.is_cancelled, VaultError::EscrowCancelled);
323 require!(
324 clock.unix_timestamp < escrow.release_time,
325 VaultError::CannotCancelAfterRelease
326 );
327
328 let depositor = escrow.depositor;
330 let beneficiary = escrow.beneficiary;
331 let seeds = &[
332 b"escrow",
333 depositor.as_ref(),
334 beneficiary.as_ref(),
335 &[escrow.bump],
336 ];
337 let signer = &[&seeds[..]];
338
339 let cpi_accounts = Transfer {
340 from: ctx.accounts.escrow_token_account.to_account_info(),
341 to: ctx.accounts.depositor_token_account.to_account_info(),
342 authority: escrow.to_account_info(),
343 };
344 let cpi_ctx = CpiContext::new_with_signer(
345 ctx.accounts.token_program.to_account_info(),
346 cpi_accounts,
347 signer,
348 );
349 token::transfer(cpi_ctx, escrow.amount)?;
350
351 escrow.is_cancelled = true;
352
353 emit!(EscrowCancelled {
354 escrow: escrow.key(),
355 depositor: escrow.depositor,
356 amount: escrow.amount,
357 });
358
359 Ok(())
360 }
361
362 pub fn extend_lock(ctx: Context<UpdateVault>, new_lock_until: i64) -> Result<()> {
364 let vault = &mut ctx.accounts.user_vault;
365
366 require!(
367 new_lock_until > vault.lock_until,
368 VaultError::CannotReduceLock
369 );
370
371 vault.lock_until = new_lock_until;
372 vault.is_locked = true;
373
374 emit!(LockExtended {
375 vault: vault.key(),
376 owner: vault.owner,
377 new_lock_until,
378 });
379
380 Ok(())
381 }
382}
383
384#[derive(Accounts)]
387#[instruction(treasury_bump: u8)]
388pub struct InitializeTreasury<'info> {
389 #[account(mut)]
390 pub authority: Signer<'info>,
391
392 #[account(
393 init,
394 payer = authority,
395 space = 8 + Treasury::INIT_SPACE,
396 seeds = [b"treasury", token_mint.key().as_ref()],
397 bump,
398 )]
399 pub treasury: Account<'info, Treasury>,
400
401 pub token_mint: Account<'info, Mint>,
402
403 #[account(
404 init,
405 payer = authority,
406 token::mint = token_mint,
407 token::authority = treasury,
408 )]
409 pub vault_token_account: Account<'info, TokenAccount>,
410
411 pub token_program: Program<'info, Token>,
412 pub system_program: Program<'info, System>,
413 pub rent: Sysvar<'info, Rent>,
414}
415
416#[derive(Accounts)]
417#[instruction(vault_name: String)]
418pub struct CreateVault<'info> {
419 #[account(mut)]
420 pub user: Signer<'info>,
421
422 #[account(
423 mut,
424 seeds = [b"treasury", treasury.token_mint.as_ref()],
425 bump = treasury.bump,
426 )]
427 pub treasury: Account<'info, Treasury>,
428
429 #[account(
430 init,
431 payer = user,
432 space = 8 + UserVault::INIT_SPACE,
433 seeds = [b"user_vault", treasury.key().as_ref(), user.key().as_ref()],
434 bump,
435 )]
436 pub user_vault: Account<'info, UserVault>,
437
438 pub system_program: Program<'info, System>,
439}
440
441#[derive(Accounts)]
442pub struct Deposit<'info> {
443 #[account(mut)]
444 pub user: Signer<'info>,
445
446 #[account(
447 mut,
448 seeds = [b"treasury", treasury.token_mint.as_ref()],
449 bump = treasury.bump,
450 )]
451 pub treasury: Account<'info, Treasury>,
452
453 #[account(
454 mut,
455 seeds = [b"user_vault", treasury.key().as_ref(), user.key().as_ref()],
456 bump,
457 constraint = user_vault.owner == user.key(),
458 )]
459 pub user_vault: Account<'info, UserVault>,
460
461 #[account(
462 mut,
463 constraint = user_token_account.owner == user.key(),
464 constraint = user_token_account.mint == treasury.token_mint,
465 )]
466 pub user_token_account: Account<'info, TokenAccount>,
467
468 #[account(
469 mut,
470 constraint = vault_token_account.key() == treasury.vault_token_account,
471 )]
472 pub vault_token_account: Account<'info, TokenAccount>,
473
474 pub token_program: Program<'info, Token>,
475}
476
477#[derive(Accounts)]
478pub struct Withdraw<'info> {
479 #[account(mut)]
480 pub user: Signer<'info>,
481
482 #[account(
483 seeds = [b"treasury", treasury.token_mint.as_ref()],
484 bump = treasury.bump,
485 )]
486 pub treasury: Account<'info, Treasury>,
487
488 #[account(
489 mut,
490 seeds = [b"user_vault", treasury.key().as_ref(), user.key().as_ref()],
491 bump,
492 constraint = user_vault.owner == user.key(),
493 )]
494 pub user_vault: Account<'info, UserVault>,
495
496 #[account(
497 mut,
498 constraint = user_token_account.owner == user.key(),
499 constraint = user_token_account.mint == treasury.token_mint,
500 )]
501 pub user_token_account: Account<'info, TokenAccount>,
502
503 #[account(
504 mut,
505 constraint = vault_token_account.key() == treasury.vault_token_account,
506 )]
507 pub vault_token_account: Account<'info, TokenAccount>,
508
509 pub token_program: Program<'info, Token>,
510}
511
512#[derive(Accounts)]
513#[instruction(amount: u64, release_time: i64, escrow_bump: u8)]
514pub struct CreateEscrow<'info> {
515 #[account(mut)]
516 pub depositor: Signer<'info>,
517
518 pub beneficiary: AccountInfo<'info>,
520
521 pub token_mint: Account<'info, Mint>,
522
523 #[account(
524 init,
525 payer = depositor,
526 space = 8 + Escrow::INIT_SPACE,
527 seeds = [b"escrow", depositor.key().as_ref(), beneficiary.key().as_ref()],
528 bump,
529 )]
530 pub escrow: Account<'info, Escrow>,
531
532 #[account(
533 init,
534 payer = depositor,
535 token::mint = token_mint,
536 token::authority = escrow,
537 )]
538 pub escrow_token_account: Account<'info, TokenAccount>,
539
540 #[account(
541 mut,
542 constraint = depositor_token_account.owner == depositor.key(),
543 constraint = depositor_token_account.mint == token_mint.key(),
544 )]
545 pub depositor_token_account: Account<'info, TokenAccount>,
546
547 pub token_program: Program<'info, Token>,
548 pub system_program: Program<'info, System>,
549 pub rent: Sysvar<'info, Rent>,
550}
551
552#[derive(Accounts)]
553pub struct ReleaseEscrow<'info> {
554 pub caller: Signer<'info>,
556
557 #[account(
558 mut,
559 seeds = [b"escrow", escrow.depositor.as_ref(), escrow.beneficiary.as_ref()],
560 bump = escrow.bump,
561 )]
562 pub escrow: Account<'info, Escrow>,
563
564 #[account(
565 mut,
566 constraint = escrow_token_account.key() == escrow.escrow_token_account,
567 )]
568 pub escrow_token_account: Account<'info, TokenAccount>,
569
570 #[account(
571 mut,
572 constraint = beneficiary_token_account.owner == escrow.beneficiary,
573 constraint = beneficiary_token_account.mint == escrow.token_mint,
574 )]
575 pub beneficiary_token_account: Account<'info, TokenAccount>,
576
577 pub token_program: Program<'info, Token>,
578}
579
580#[derive(Accounts)]
581pub struct CancelEscrow<'info> {
582 #[account(
583 constraint = depositor.key() == escrow.depositor,
584 )]
585 pub depositor: Signer<'info>,
586
587 #[account(
588 mut,
589 seeds = [b"escrow", escrow.depositor.as_ref(), escrow.beneficiary.as_ref()],
590 bump = escrow.bump,
591 )]
592 pub escrow: Account<'info, Escrow>,
593
594 #[account(
595 mut,
596 constraint = escrow_token_account.key() == escrow.escrow_token_account,
597 )]
598 pub escrow_token_account: Account<'info, TokenAccount>,
599
600 #[account(
601 mut,
602 constraint = depositor_token_account.owner == escrow.depositor,
603 constraint = depositor_token_account.mint == escrow.token_mint,
604 )]
605 pub depositor_token_account: Account<'info, TokenAccount>,
606
607 pub token_program: Program<'info, Token>,
608}
609
610#[derive(Accounts)]
611pub struct UpdateVault<'info> {
612 pub user: Signer<'info>,
613
614 #[account(
615 mut,
616 constraint = user_vault.owner == user.key(),
617 )]
618 pub user_vault: Account<'info, UserVault>,
619}
620
621#[account]
626#[derive(InitSpace)]
627pub struct Treasury {
628 pub authority: Pubkey,
630 pub token_mint: Pubkey,
632 pub vault_token_account: Pubkey,
634 pub total_deposited: u64,
636 pub total_vaults: u64,
638 pub bump: u8,
640 pub is_active: bool,
642 pub created_at: i64,
644}
645
646#[account]
649#[derive(InitSpace)]
650pub struct UserVault {
651 pub owner: Pubkey,
653 pub treasury: Pubkey,
655 #[max_len(32)]
657 pub name: String,
658 pub balance: u64,
660 pub lock_until: i64,
662 pub created_at: i64,
664 pub last_activity: i64,
666 pub is_locked: bool,
668 pub total_deposited: u64,
670 pub total_withdrawn: u64,
672}
673
674#[account]
677#[derive(InitSpace)]
678pub struct Escrow {
679 pub depositor: Pubkey,
681 pub beneficiary: Pubkey,
683 pub token_mint: Pubkey,
685 pub escrow_token_account: Pubkey,
687 pub amount: u64,
689 pub release_time: i64,
691 pub created_at: i64,
693 pub is_released: bool,
695 pub is_cancelled: bool,
697 pub bump: u8,
699}
700
701#[error_code]
704pub enum VaultError {
705 #[msg("Vault name too long (max 32 characters)")]
706 NameTooLong,
707 #[msg("Invalid amount")]
708 InvalidAmount,
709 #[msg("Treasury is not active")]
710 TreasuryInactive,
711 #[msg("Insufficient balance")]
712 InsufficientBalance,
713 #[msg("Vault is locked")]
714 VaultLocked,
715 #[msg("Invalid release time")]
716 InvalidReleaseTime,
717 #[msg("Escrow already released")]
718 AlreadyReleased,
719 #[msg("Escrow was cancelled")]
720 EscrowCancelled,
721 #[msg("Escrow not ready for release")]
722 EscrowNotReady,
723 #[msg("Cannot cancel escrow after release time")]
724 CannotCancelAfterRelease,
725 #[msg("Cannot reduce lock time")]
726 CannotReduceLock,
727 #[msg("Math overflow")]
728 MathOverflow,
729}