1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
//! SAVE Token: A Simple Agreement for Vote-Escrowed Tokens
//!
//! # About
//!
//! The Simple Agreement for Vote-Escrowed Tokens, **SAVE**, is a derivative which enforces that tokens must be locked for a specific period of time.
//!
//! As they are not directly convertible to the underlying token, SAVEs are a powerful primitive for issuing grants for DAO participants that do not restrict transferability of tokens.
//!
//! We're in active development. For the latest updates, please join our community:
//!
//! - Twitter: <https://twitter.com/TribecaDAO>
//!
//! # Architecture
//!
//! SAVE tokens consist of three tokens:
//!
//! - The underlying token, which is the token intended to be staked into the DAO.
//! - The [Yi token](https://github.com/CrateProtocol/yi), which backs the SAVE token. This token is usually backed 1:1 by the underlying token; however, one can increase the conversion rate of the SAVE tokens by increasing the Yi's conversion rate.
//! - The SAVE token, which is the primary token issued by this protocol.
//!
//! # Note
//!
//! - **SAVE is in active development, so all APIs are subject to change.**
//! - **This code is unaudited. Use at your own risk.**
//!
//! # Addresses
//!
//! Program addresses are the same on devnet, testnet, and mainnet-beta.
//!
//! - SAVE: [`SAVEd9pHcncknnMWdP8RSbhDUhw3nrzwmZ6F6RAUiio`](https://explorer.solana.com/address/SAVEd9pHcncknnMWdP8RSbhDUhw3nrzwmZ6F6RAUiio)
//!
//! # Contribution
//!
//! Thank you for your interest in contributing to Tribeca Protocol! All contributions are welcome no matter how big or small. This includes
//! (but is not limited to) filing issues, adding documentation, fixing bugs, creating examples, and implementing features.
//!
//! When contributing, please make sure your code adheres to some basic coding guidlines:
//!
//! - Code must be formatted with the configured formatters (e.g. `rustfmt` and `prettier`).
//! - Comment lines should be no longer than 80 characters and written with proper grammar and punctuation.
//! - Commit messages should be prefixed with the package(s) they modify. Changes affecting multiple packages should list all packages. In rare cases, changes may omit the package name prefix.
//!
//! # License
//!
//! Tribeca Protocol is licensed under the GNU Affero General Public License v3.0.
#![deny(rustdoc::all)]
#![allow(rustdoc::missing_doc_code_examples)]
#![deny(clippy::unwrap_used)]

mod macros;

use anchor_lang::prelude::*;
use vipers::prelude::*;

mod common;
mod instructions;
mod state;

pub(crate) use common::*;
use instructions::*;
pub use state::*;

declare_id!("SAVEd9pHcncknnMWdP8RSbhDUhw3nrzwmZ6F6RAUiio");

/// The [save_token] program.
#[program]
pub mod save_token {
    use super::*;

    /// Creates a new [Save].
    #[access_control(ctx.accounts.validate())]
    pub fn create_save(ctx: Context<CreateSAVE>, min_lock_duration: u64) -> Result<()> {
        instructions::create_save::handler(ctx, min_lock_duration)
    }

    /// Locks [Save] tokens for their underlying.
    #[access_control(ctx.accounts.validate())]
    pub fn lock(ctx: Context<Lock>, amount: u64, duration: u64) -> Result<()> {
        instructions::lock::handler(ctx, amount, duration)
    }

    /// Mints [Save] tokens by locking up Yi tokens.
    #[access_control(ctx.accounts.validate())]
    pub fn mint_from_yi(ctx: Context<MintFromYi>, yi_amount: u64) -> Result<()> {
        instructions::mint_from_yi::handler(ctx, yi_amount)
    }

    /// Mints [Save] tokens by locking up underlying tokens.
    #[access_control(ctx.accounts.validate())]
    pub fn mint_from_underlying(
        ctx: Context<MintFromUnderlying>,
        underlying_amount: u64,
    ) -> Result<()> {
        instructions::mint_from_underlying::handler(ctx, underlying_amount)
    }
}

/// Errors.
#[error_code]
pub enum ErrorCode {
    #[msg("SAVE minimum duration not met.")]
    DurationExceeded,
    #[msg("Tokens may only be locked in the SAVE's specified locker.")]
    LockerMismatch,
}