├── .github
└── workflows
│ └── rust-checks.yml
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .vscode
├── extensions.json
└── settings.json
├── Anchor.toml
├── CODE_OF_CONDUCT.md
├── Cargo.lock
├── Cargo.toml
├── LICENSE
├── README.md
├── SECURITY.md
├── addresses.md
├── libs
├── vyper-macros
│ ├── Cargo.toml
│ └── src
│ │ └── lib.rs
└── vyper-utils
│ ├── Cargo.toml
│ ├── Xargo.toml
│ └── src
│ ├── constants.rs
│ ├── lib.rs
│ ├── rate_common.rs
│ └── redeem_logic_common.rs
├── migrations
└── deploy.ts
├── package.json
├── programs
├── rate-mock
│ ├── Cargo.toml
│ ├── Xargo.toml
│ └── src
│ │ ├── errors.rs
│ │ └── lib.rs
├── rate-poolv2
│ ├── Cargo.toml
│ ├── Xargo.toml
│ └── src
│ │ ├── errors.rs
│ │ ├── lib.rs
│ │ └── state.rs
├── rate-pyth
│ ├── Cargo.toml
│ ├── Xargo.toml
│ └── src
│ │ ├── errors.rs
│ │ └── lib.rs
├── rate-switchboard
│ ├── Cargo.toml
│ ├── Xargo.toml
│ └── src
│ │ ├── errors.rs
│ │ └── lib.rs
├── rate-twap
│ ├── Cargo.toml
│ ├── Xargo.toml
│ └── src
│ │ ├── errors.rs
│ │ ├── instructions
│ │ ├── initialize.rs
│ │ ├── mod.rs
│ │ └── refresh.rs
│ │ ├── lib.rs
│ │ └── state
│ │ ├── mod.rs
│ │ ├── rate_state.rs
│ │ ├── sample_record.rs
│ │ └── sampling_data.rs
├── redeem-logic-digital
│ ├── Cargo.toml
│ ├── Xargo.toml
│ └── src
│ │ └── lib.rs
├── redeem-logic-farming
│ ├── Cargo.toml
│ ├── Xargo.toml
│ └── src
│ │ └── lib.rs
├── redeem-logic-fila
│ ├── Cargo.toml
│ ├── Xargo.toml
│ └── src
│ │ └── lib.rs
├── redeem-logic-forward
│ ├── Cargo.toml
│ ├── Xargo.toml
│ └── src
│ │ └── lib.rs
├── redeem-logic-lending-fee
│ ├── Cargo.toml
│ ├── Xargo.toml
│ └── src
│ │ └── lib.rs
├── redeem-logic-lending
│ ├── Cargo.toml
│ ├── Xargo.toml
│ └── src
│ │ └── lib.rs
├── redeem-logic-settled-forward
│ ├── Cargo.toml
│ ├── Xargo.toml
│ └── src
│ │ └── lib.rs
├── redeem-logic-vanilla-option
│ ├── Cargo.toml
│ ├── Xargo.toml
│ └── src
│ │ └── lib.rs
└── vyper-core
│ ├── Cargo.toml
│ ├── Xargo.toml
│ └── src
│ ├── errors.rs
│ ├── instructions
│ ├── collect_fee.rs
│ ├── deposit.rs
│ ├── initialize.rs
│ ├── mod.rs
│ ├── redeem.rs
│ ├── refresh_tranche_fair_value.rs
│ └── update_tranche_data.rs
│ ├── lib.rs
│ ├── state
│ ├── last_update.rs
│ ├── mod.rs
│ ├── owner_restricted_ix_flags.rs
│ ├── reserve_fair_value.rs
│ ├── slot_tracking.rs
│ ├── tranche_config.rs
│ ├── tranche_data.rs
│ ├── tranche_fair_value.rs
│ └── tranche_halt_flags.rs
│ └── utils.rs
├── rustfmt.toml
├── scripts
├── create_git_tag.sh
├── deploy_devnet.sh
├── rate_mock
│ ├── create.ts
│ ├── read_state.ts
│ └── set_fair_value.ts
├── rate_poolv2
│ ├── create.ts
│ ├── read_state.ts
│ └── refresh.ts
├── rate_switchboard
│ ├── create.ts
│ ├── read_aggregator.ts
│ ├── read_state.ts
│ └── refresh.ts
├── readme.md
├── redeem_logic_farming
│ ├── create.ts
│ ├── read_state.ts
│ └── update.ts
├── redeem_logic_lending_fee
│ ├── create.ts
│ └── read_state.ts
└── redeem_logic_vanilla_option
│ ├── create.ts
│ └── read_state.ts
├── sdk
├── .eslintrc.js
├── .gitignore
├── package.json
├── src
│ ├── HaltFlags.ts
│ ├── LastUpdate.ts
│ ├── OwnerRestrictedIxFlags.ts
│ ├── ReserveFairValue.ts
│ ├── SlotTracking.ts
│ ├── TrancheConfig.ts
│ ├── TrancheData.ts
│ ├── TrancheFairValue.ts
│ ├── TrancheInitData.ts
│ ├── UpdateTrancheConfigFlags.ts
│ ├── Vyper.ts
│ ├── index.ts
│ └── plugins
│ │ ├── ratePlugin
│ │ ├── IRatePlugin.ts
│ │ └── rateMock
│ │ │ ├── Rate.ts
│ │ │ └── RateState.ts
│ │ └── redeemLogicPlugin
│ │ ├── IReedeemLogicPlugin.ts
│ │ ├── redeemLogicFarming
│ │ ├── RedeemLogicFarming.ts
│ │ └── RedeemLogicFarmingState.ts
│ │ ├── redeemLogicLending
│ │ ├── RedeemLogicLending.ts
│ │ └── RedeemLogicLendingState.ts
│ │ ├── redeemLogicLendingFee
│ │ ├── RedeemLogicLendingFee.ts
│ │ └── RedeemLogicLendingFeeState.ts
│ │ └── redeemLogicVanillaOption
│ │ ├── RedeemLogicVanillaOption.ts
│ │ └── RedeemLogicVanillaOptionState.ts
├── tests
│ ├── RateMockPlugin.ts
│ ├── RedeemLendingPlugin.ts
│ ├── RedeemLogicFarmingPlugin.ts
│ ├── RedeemLogicLendingFee.ts
│ ├── RedeemVanillaOptionPlugin.ts
│ └── TrancheConfig.ts
└── tsconfig.json
├── tests
├── rate-mock.ts
├── rate-poolv2.ts
├── rate-pyth.ts
├── rate-switchboard.ts
├── rate-twap.ts
├── redeem-logic-lending.ts
├── sdk
│ ├── Vyper.ts
│ └── plugins
│ │ ├── rates
│ │ └── RateMockPlugin.ts
│ │ └── redeemLogic
│ │ ├── RedeemLogicLendingPlugin.ts
│ │ └── RedeemLogicVanillaOptionPlugin.ts
├── utils.ts
└── vyper-core.ts
├── tsconfig.json
└── yarn.lock
/.github/workflows/rust-checks.yml:
--------------------------------------------------------------------------------
1 | name: Rust Tests
2 | on:
3 | push:
4 | branches:
5 | - "main"
6 | - "dev"
7 | paths:
8 | - "**.rs"
9 | - "**.toml"
10 | pull_request:
11 | branches: "*"
12 | paths:
13 | - "**.rs"
14 | - "**.toml"
15 | jobs:
16 | cargo_check:
17 | name: cargo check
18 | runs-on: ubuntu-latest
19 | steps:
20 | - uses: actions/checkout@v2
21 | - uses: actions-rs/toolchain@v1
22 | with:
23 | profile: minimal
24 | toolchain: stable
25 | override: true
26 | - uses: actions-rs/cargo@v1
27 | with:
28 | command: check
29 |
30 | cargo_test:
31 | name: cargo test
32 | runs-on: ubuntu-latest
33 | steps:
34 | - uses: actions/checkout@v2
35 | - uses: actions-rs/toolchain@v1
36 | with:
37 | profile: minimal
38 | toolchain: stable
39 | override: true
40 | - uses: actions-rs/cargo@v1
41 | with:
42 | command: test
43 |
44 | clippy_check:
45 | name: clippy check
46 | runs-on: ubuntu-latest
47 | steps:
48 | - uses: actions/checkout@v1
49 | - run: rustup component add clippy
50 | - uses: actions-rs/clippy-check@v1
51 | with:
52 | token: ${{ secrets.GITHUB_TOKEN }}
53 | args: --all-features
54 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .anchor
2 | .coderrect
3 | .DS_Store
4 | target
5 | **/*.rs.bk
6 | node_modules
7 |
8 | Cargo.lock
9 | out.csv
10 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # Ignore all rust files
2 | *.rs
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "overrides": [
3 | {
4 | "files": "*.ts",
5 | "options": {
6 | "tabWidth": 4,
7 | "printWidth": 120
8 | }
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
4 |
5 | // List of extensions which should be recommended for users of this workspace.
6 | "recommendations": ["bungcip.better-toml", "rust-lang.rust-analyzer"],
7 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace.
8 | "unwantedRecommendations": []
9 | }
10 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "prettier.printWidth": 128,
3 | "editor.formatOnSave": true,
4 | "[rust]": {
5 | "editor.defaultFormatter": "rust-lang.rust-analyzer" // rust-lang.rust", // Makes the magic
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Anchor.toml:
--------------------------------------------------------------------------------
1 | [features]
2 | seeds = false
3 |
4 | [programs.localnet]
5 | vyper_core = "vyPErCcGJKQQBeeQ59gXcWrDyU4vBrq8qQfacwmsAsp"
6 | rate_mock = "FB7HErqohbgaVV21BRiiMTuiBpeUYT8Yw7Z6EdEL7FAG"
7 | rate_switchboard = "2hGXiH1oEQwjCXRx8bNdHTi49ScZp7Mj2bxcjxtULKe1"
8 | rate_pyth = "3mxtC2cGVhHucUg4p58MVzVqUKLyiy1zWqRkRQdgUBPT"
9 | rate_poolv2 = "5Vm2YZK3SeGbXbtQpKVByP9EvYy78ahnjFXKkf9B3yzW"
10 | redeem_logic_farming = "Fd87TGcYmWs1Gfa7XXZycJwt9kXjRs8axMtxCWtCmowN"
11 | redeem_logic_fila = "9pPodGZL2EWjkj3c7yWjfWrVDfn8Jxss9tEXTEmKLTmM"
12 | redeem_logic_forward = "BrpV1re8MshA8qskKVxcEG8zXG3vf2uLX6myeTKAyhsK"
13 | redeem_logic_lending = "Gc2ZKNuCpdNKhAzEGS2G9rBSiz4z8MULuC3M3t8EqdWA"
14 | redeem_logic_lending_fee = "3mq416it8YJsd5DKNuWeoCCAH8GYJfpuefHSNkSP6LyS"
15 | redeem_logic_vanilla_option = "8fSeRtFseNrjdf8quE2YELhuzLkHV7WEGRPA9Jz8xEVe"
16 | redeem_logic_settled_forward = "6vBg1GMtKj7EYDLWWt6tkHoDWLAAksNPbKWiXMic99qU"
17 | redeem_logic_digital = "5Dq9PjUJUG5dM9DzYFqKA4YZYeKJfGaM5Gy7NjpY3p5r"
18 |
19 | [registry]
20 | url = "https://anchor.projectserum.com"
21 |
22 | [provider]
23 | cluster = "localnet"
24 | wallet = "~/.config/solana/id.json"
25 |
26 | [scripts]
27 | test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
28 |
29 | [test.validator]
30 | url = "https://api.mainnet-beta.solana.com"
31 |
32 | # BTC USD Switchboard: https://switchboard.xyz/explorer/3/8SXvChNYFhRq4EZuZvnhjrB3jJRQCv4k3P4W6hesH3Ee
33 | [[test.validator.clone]]
34 | address = "8SXvChNYFhRq4EZuZvnhjrB3jJRQCv4k3P4W6hesH3Ee"
35 |
36 | # USDC USD Switchboard: https://switchboard.xyz/explorer/3/BjUgj6YCnFBZ49wF54ddBVA9qu8TeqkFtkbqmZcee8uW
37 | [[test.validator.clone]]
38 | address = "BjUgj6YCnFBZ49wF54ddBVA9qu8TeqkFtkbqmZcee8uW"
39 |
40 | # XTZ USD Switchboard: https://switchboard.xyz/explorer/3/F11LACseaLXuRaPSvnD6w15vSPHtx73YaGZv9293rQQm
41 | [[test.validator.clone]]
42 | address = "F11LACseaLXuRaPSvnD6w15vSPHtx73YaGZv9293rQQm"
43 |
44 | # BTC USD Pyth: https://pyth.network/price-feeds/crypto-btc-usd
45 | [[test.validator.clone]]
46 | address = "GVXRSBjFk6e6J3NbVPXohDJetcTjaeeuykUpbQF8UoMU"
47 |
48 | # SOL USD Pyth: https://pyth.network/price-feeds/crypto-sol-usd
49 | [[test.validator.clone]]
50 | address = "H6ARHf6YXhGYeQfUzQNGk6rDNnLBQKrenN712K4AQJEG"
51 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | info@vyperprotocol.io.
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | members = [
3 | "programs/*",
4 | ]
5 |
6 | exclude = []
7 |
8 | [profile.release]
9 | opt-level = 3
10 | codegen-units = 1
11 | lto = "fat"
12 | overflow-checks = true
13 | incremental = false
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2022 Vyper Protocol
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vyper Core works as a set of smart contract which can take any SPL token and a custom payoff function, and redistribute the tokens to match the payoff upon certain conditions. For example, people can deposit farming LP tokens, which after some time (e.g. a week) are redistributed to reflect the the impermanent loss vs fees generated.
8 |
9 | There are three main smart contracts:
10 |
11 | - **Vyper Core**: manages position IOUs creation and redemption, accepts only fungible tokens (e.g. LP tokens or cTokens). It redistributes collateral deposited consuming data from the rate calculator and redeem logic contracts
12 | - **Rate Calculator**: updates the fair price of the collateral deposited (e.g. USD value of LP token). Supports up to 10 different underlyings for sophisticated payoffs
13 | - **Redeem Logic**: payoff formula which specifies how collateral should be distributed, based on initial collateral deposited, initial prices, final prices, and other parameters (e.g. strike, duration)
14 |
15 | # Repository Structure
16 |
17 | Following the Vyper suite
18 |
19 | ## Solana Programs
20 |
21 | | Name | Type | Version | Path |
22 | | ----------------------------------| ------------------- | ------- | -------------------------------------- |
23 | | **Vyper Core** | Core Primitive | `0.1.0` | `programs/vyper-core` |
24 | | **Rate Switchboard** | Rate Plugin | `0.1.0` | `programs/rate-switchboard` |
25 | | **Rate Pyth** | Rate Plugin | `0.1.0` | `programs/rate-pyth` |
26 | | **Rate Mock** | Rate Plugin | `0.1.0` | `programs/rate-mock` |
27 | | **Redeem Logic Lending** | Redeem Logic Plugin | `1.0.0` | `programs/redeem-logic-lending` |
28 | | **Redeem Logic Lending Fee** | Redeem Logic Plugin | `1.0.0` | `programs/redeem-logic-lending-fee` |
29 | | **Redeem Logic Farming** | Redeem Logic Plugin | `2.0.0` | `programs/redeem-logic-farming` |
30 | | **Redeem Logic Vanilla Option** | Redeem Logic Plugin | `2.0.0` | `programs/redeem-logic-vanilla-option` |
31 | | **Redeem Logic Forward** | Redeem Logic Plugin | `1.0.0` | `programs/redeem-logic-forward` |
32 | | **Redeem Logic Settled Forward** | Redeem Logic Plugin | `1.0.0` | `programs/redeem-logic-settled-forward`|
33 | | **Redeem Logic Fila** | Redeem Logic Plugin | `1.0.0` | `programs/redeem-logic-fila` |
34 | | **Redeem Logic Digital** | Redeem Logic Plugin | `1.0.0` | `programs/redeem-logic-digital` |
35 |
36 | ## Rust Libraries
37 |
38 | | Name | Version | Path |
39 | | ------------ | ------- | ------------------- |
40 | | Vyper Utils | `0.1.0` | `libs/vyper-utils` |
41 | | Vyper Macros | `0.1.0` | `libs/vyper-macros` |
42 |
43 | ## Typescript SDK
44 |
45 | We're currently working on a typescript sdk for frontend integrations. This is still a WIP, but it's available at the path `/sdk`.
46 |
47 | Once finished it'll be published as a npm module.
48 |
49 | # Setup, Build, and Test
50 |
51 | First, install dependencies:
52 |
53 | ```
54 | $ yarn install
55 | ```
56 |
57 | And install Anchor by following the [instructions here](https://github.com/coral-xyz/anchor/blob/master/docs/src/getting-started/installation.md).
58 |
59 | Build the program:
60 |
61 | ```
62 | $ anchor build
63 | ```
64 |
65 | Finally, run the tests:
66 |
67 | ```
68 | $ cargo test
69 | $ anchor test
70 | ```
71 |
72 | # Documentation
73 |
74 | General Vyper documentation can be found [here](https://docs.vyperprotocol.io/).
75 |
76 | # Getting Help
77 |
78 | Join [our Discord channel](https://discord.gg/KYaXgwetcK) and post a message
79 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vyper-protocol/vyper-core/2fa059ea7f9f106a7240f70c767f436034b8de0f/SECURITY.md
--------------------------------------------------------------------------------
/addresses.md:
--------------------------------------------------------------------------------
1 | # Addresses
2 |
3 | | | Env | Program | Upgrade authority |
4 | | -------------------------------- | ---------------- | ---------------------------------------------- | ---------------------------------------------- |
5 | | Vyper Core | Mainnet Main | `vyPErCcGJKQQBeeQ59gXcWrDyU4vBrq8qQfacwmsAsp` | `DpfQodEMtBjx7X8Y8VhC9THo18YVZmUtvm6ASCVFThxh` |
6 | | Vyper Core | Devnnet Main | `vyPErCcGJKQQBeeQ59gXcWrDyU4vBrq8qQfacwmsAsp` | `DpfQodEMtBjx7X8Y8VhC9THo18YVZmUtvm6ASCVFThxh` |
7 | | Vyper Core | Devnet Staging | `mb9NrZKiC3ZYUutgGhXwwkAL6Jkvmu5WLDbxWRZ8L9U` | `DpfQodEMtBjx7X8Y8VhC9THo18YVZmUtvm6ASCVFThxh` |
8 | | Vyper Core | Devnet Hackathon | `29HSW1bCUHF9Kz1U9894nc9ycFCWTTTLMmKZmZ5qLwgT` | `DpfQodEMtBjx7X8Y8VhC9THo18YVZmUtvm6ASCVFThxh` |
9 | | RateMock Plugin | Devnet | `FB7HErqohbgaVV21BRiiMTuiBpeUYT8Yw7Z6EdEL7FAG` | `DpfQodEMtBjx7X8Y8VhC9THo18YVZmUtvm6ASCVFThxh` |
10 | | RateSwitchboard Plugin | Mainnet | `2hGXiH1oEQwjCXRx8bNdHTi49ScZp7Mj2bxcjxtULKe1` | `DpfQodEMtBjx7X8Y8VhC9THo18YVZmUtvm6ASCVFThxh` |
11 | | RateSwitchboard Plugin | Devnet | `2hGXiH1oEQwjCXRx8bNdHTi49ScZp7Mj2bxcjxtULKe1` | `DpfQodEMtBjx7X8Y8VhC9THo18YVZmUtvm6ASCVFThxh` |
12 | | RateSwitchboard Plugin | Devnet Hackathon | `EMg9whXe9Yk1vdmEwBmEfnucRAaombxC4HW1LyRy1tWD` | `DpfQodEMtBjx7X8Y8VhC9THo18YVZmUtvm6ASCVFThxh` |
13 | | RatePyth Plugin | Devnet | `3mxtC2cGVhHucUg4p58MVzVqUKLyiy1zWqRkRQdgUBPT` | `DpfQodEMtBjx7X8Y8VhC9THo18YVZmUtvm6ASCVFThxh` |
14 | | RatePyth Plugin | Mainnet | `3mxtC2cGVhHucUg4p58MVzVqUKLyiy1zWqRkRQdgUBPT` | `DpfQodEMtBjx7X8Y8VhC9THo18YVZmUtvm6ASCVFThxh` |
15 | | RatePoolv2 Plugin | Mainnet | `5Vm2YZK3SeGbXbtQpKVByP9EvYy78ahnjFXKkf9B3yzW` | `DpfQodEMtBjx7X8Y8VhC9THo18YVZmUtvm6ASCVFThxh` |
16 | | RatePoolv2 Plugin | Devnet | `5Vm2YZK3SeGbXbtQpKVByP9EvYy78ahnjFXKkf9B3yzW` | `DpfQodEMtBjx7X8Y8VhC9THo18YVZmUtvm6ASCVFThxh` |
17 | | RedeemLogicLendingFee Plugin | Devnet | `3mq416it8YJsd5DKNuWeoCCAH8GYJfpuefHSNkSP6LyS` | `DpfQodEMtBjx7X8Y8VhC9THo18YVZmUtvm6ASCVFThxh` |
18 | | RedeemLogicFarming Plugin | Mainnet | `Fd87TGcYmWs1Gfa7XXZycJwt9kXjRs8axMtxCWtCmowN` | `DpfQodEMtBjx7X8Y8VhC9THo18YVZmUtvm6ASCVFThxh` |
19 | | RedeemLogicFarming Plugin | Devnet | `Fd87TGcYmWs1Gfa7XXZycJwt9kXjRs8axMtxCWtCmowN` | `DpfQodEMtBjx7X8Y8VhC9THo18YVZmUtvm6ASCVFThxh` |
20 | | RedeemLogicVanillaOption Plugin | Mainnet | `8fSeRtFseNrjdf8quE2YELhuzLkHV7WEGRPA9Jz8xEVe` | `DpfQodEMtBjx7X8Y8VhC9THo18YVZmUtvm6ASCVFThxh` |
21 | | RedeemLogicVanillaOption Plugin | Devnet | `8fSeRtFseNrjdf8quE2YELhuzLkHV7WEGRPA9Jz8xEVe` | `DpfQodEMtBjx7X8Y8VhC9THo18YVZmUtvm6ASCVFThxh` |
22 | | RedeemLogicForward Plugin | Devnet Hackathon | `BrpV1re8MshA8qskKVxcEG8zXG3vf2uLX6myeTKAyhsK` | `DpfQodEMtBjx7X8Y8VhC9THo18YVZmUtvm6ASCVFThxh` |
23 | | RedeemLogicSettledForward Plugin | Mainnet | `6vBg1GMtKj7EYDLWWt6tkHoDWLAAksNPbKWiXMic99qU` | `DpfQodEMtBjx7X8Y8VhC9THo18YVZmUtvm6ASCVFThxh` |
24 | | RedeemLogicSettledForward Plugin | Devnet | `6vBg1GMtKj7EYDLWWt6tkHoDWLAAksNPbKWiXMic99qU` | `DpfQodEMtBjx7X8Y8VhC9THo18YVZmUtvm6ASCVFThxh` |
25 | | RedeemLogicDigital Plugin | Mainnet | `5Dq9PjUJUG5dM9DzYFqKA4YZYeKJfGaM5Gy7NjpY3p5r` | `DpfQodEMtBjx7X8Y8VhC9THo18YVZmUtvm6ASCVFThxh` |
26 | | RedeemLogicDigital Plugin | Devnet | `5Dq9PjUJUG5dM9DzYFqKA4YZYeKJfGaM5Gy7NjpY3p5r` | `DpfQodEMtBjx7X8Y8VhC9THo18YVZmUtvm6ASCVFThxh` |
27 |
--------------------------------------------------------------------------------
/libs/vyper-macros/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "vyper-macros"
3 | version = "0.1.0"
4 | edition = "2021"
5 | repository = "https://github.com/vyper-protocol/vyper-core/"
6 | homepage = "https://www.vyperprotocol.io/"
7 | authors = ["Vyper Labs ltd"]
8 |
9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
10 |
11 | [lib]
12 | proc-macro = true
13 |
14 | [dependencies]
15 | syn = { version = "1.0.96", features=["full"] }
16 | quote = "1.0.18"
--------------------------------------------------------------------------------
/libs/vyper-macros/src/lib.rs:
--------------------------------------------------------------------------------
1 | use proc_macro::TokenStream;
2 | use quote::quote;
3 | use syn::{parse_macro_input, ItemFn};
4 |
5 | #[proc_macro_attribute]
6 | pub fn log_wrap_ix(_attr: TokenStream, stream: TokenStream) -> TokenStream {
7 | let input = parse_macro_input!(stream as ItemFn);
8 | let ItemFn {
9 | attrs,
10 | vis,
11 | sig,
12 | block,
13 | } = input;
14 | let stmts = &block.stmts;
15 | let name = &sig.ident;
16 | let gen = quote! {
17 | #(#attrs)* #vis #sig {
18 | msg!("{} begin", stringify!(#name));
19 |
20 | #(#stmts)*
21 | }
22 | };
23 |
24 | gen.into()
25 | }
26 |
--------------------------------------------------------------------------------
/libs/vyper-utils/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "vyper-utils"
3 | version = "0.1.0"
4 | edition = "2021"
5 | repository = "https://github.com/vyper-protocol/vyper-core/"
6 | homepage = "https://www.vyperprotocol.io/"
7 | authors = ["Vyper Labs ltd"]
8 |
9 | [lib]
10 | crate-type = ["cdylib", "lib"]
11 | name = "vyper_utils"
12 |
13 | [dependencies]
14 | anchor-lang = "0.24.2"
15 | anchor-spl = "0.24.2"
16 | rust_decimal = { version="1.24", features=["borsh"] }
17 | rust_decimal_macros = "1.24"
--------------------------------------------------------------------------------
/libs/vyper-utils/Xargo.toml:
--------------------------------------------------------------------------------
1 | [target.bpfel-unknown-unknown.dependencies.std]
2 | features = []
3 |
--------------------------------------------------------------------------------
/libs/vyper-utils/src/constants.rs:
--------------------------------------------------------------------------------
1 | pub const SENIOR: &[u8] = b"senior";
2 | pub const JUNIOR: &[u8] = b"junior";
3 |
--------------------------------------------------------------------------------
/libs/vyper-utils/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub mod constants;
2 | pub mod rate_common;
3 | pub mod redeem_logic_common;
4 |
--------------------------------------------------------------------------------
/libs/vyper-utils/src/rate_common.rs:
--------------------------------------------------------------------------------
1 | use anchor_lang::prelude::*;
2 |
3 | #[error_code]
4 | pub enum RateErrors {
5 | #[msg("generic error")]
6 | GenericError,
7 |
8 | #[msg("invalid input")]
9 | InvalidInput,
10 |
11 | #[msg("failed to perform some math operation safely")]
12 | MathError,
13 | }
14 |
--------------------------------------------------------------------------------
/libs/vyper-utils/src/redeem_logic_common.rs:
--------------------------------------------------------------------------------
1 | use anchor_lang::prelude::*;
2 |
3 | #[derive(AnchorSerialize, AnchorDeserialize, Debug)]
4 | pub struct RedeemLogicExecuteInput {
5 | pub old_quantity: [u64; 2],
6 | pub old_reserve_fair_value: [[u8; 16]; 10],
7 | pub new_reserve_fair_value: [[u8; 16]; 10],
8 | }
9 |
10 | #[derive(AnchorSerialize, AnchorDeserialize, Debug)]
11 | pub struct RedeemLogicExecuteResult {
12 | pub new_quantity: [u64; 2],
13 | pub fee_quantity: u64,
14 | }
15 |
16 | #[error_code]
17 | pub enum RedeemLogicErrors {
18 | #[msg("generic error")]
19 | GenericError,
20 |
21 | #[msg("invalid input")]
22 | InvalidInput,
23 |
24 | #[msg("failed to perform some math operation safely")]
25 | MathError,
26 | }
27 |
--------------------------------------------------------------------------------
/migrations/deploy.ts:
--------------------------------------------------------------------------------
1 | // Migrations are an early feature. Currently, they're nothing more than this
2 | // single deploy script that's invoked from the CLI, injecting a provider
3 | // configured from the workspace's Anchor.toml.
4 |
5 | const anchor = require("@project-serum/anchor");
6 |
7 | module.exports = async function (provider) {
8 | // Configure client to use the provider.
9 | anchor.setProvider(provider);
10 |
11 | // Add your deploy script here.
12 | }
13 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w",
4 | "lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check"
5 | },
6 | "dependencies": {
7 | "@project-serum/anchor": "^0.24.2",
8 | "@solana/spl-token": "^0.2.0",
9 | "@solana/web3.js": "^1.44.0",
10 | "@switchboard-xyz/switchboard-v2": "^0.0.133",
11 | "@vyper-protocol/rust-decimal-wrapper": "^0.1.3"
12 | },
13 | "devDependencies": {
14 | "@types/bn.js": "^5.1.0",
15 | "@types/chai": "^4.3.0",
16 | "@types/mocha": "^9.0.0",
17 | "chai": "^4.3.4",
18 | "mocha": "^9.0.3",
19 | "prettier": "^2.6.2",
20 | "ts-mocha": "^8.0.0",
21 | "typescript": "^4.3.5"
22 | },
23 | "license": "MIT"
24 | }
25 |
--------------------------------------------------------------------------------
/programs/rate-mock/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "rate-mock"
3 | version = "0.1.1"
4 | edition = "2021"
5 | repository = "https://github.com/vyper-protocol/vyper-core/"
6 | homepage = "https://www.vyperprotocol.io/"
7 | authors = ["Vyper Labs ltd"]
8 |
9 | [lib]
10 | crate-type = ["cdylib", "lib"]
11 | name = "rate_mock"
12 |
13 | [features]
14 | no-entrypoint = []
15 | no-idl = []
16 | no-log-ix-name = []
17 | cpi = ["no-entrypoint"]
18 | default = []
19 |
20 | [dependencies]
21 | anchor-lang = "0.24.2"
22 | vyper-utils = { path = "../../libs/vyper-utils" }
23 | rust_decimal = { version="1.24", features=["borsh"] }
24 | rust_decimal_macros = "1.24"
25 | solana-security-txt = "1.0.1"
--------------------------------------------------------------------------------
/programs/rate-mock/Xargo.toml:
--------------------------------------------------------------------------------
1 | [target.bpfel-unknown-unknown.dependencies.std]
2 | features = []
3 |
--------------------------------------------------------------------------------
/programs/rate-mock/src/errors.rs:
--------------------------------------------------------------------------------
1 | use anchor_lang::prelude::*;
2 |
3 | #[error_code]
4 | pub enum RateMockErrorCode {
5 | #[msg("generic error")]
6 | GenericError,
7 |
8 | #[msg("math error")]
9 | MathError,
10 | }
11 |
--------------------------------------------------------------------------------
/programs/rate-mock/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub mod errors;
2 |
3 | use crate::errors::RateMockErrorCode;
4 |
5 | use anchor_lang::prelude::*;
6 | use rust_decimal::prelude::FromPrimitive;
7 | use rust_decimal::Decimal;
8 | use rust_decimal_macros::dec;
9 |
10 | #[cfg(not(feature = "no-entrypoint"))]
11 | solana_security_txt::security_txt! {
12 | name: "Rate Mock | Vyper Core",
13 | project_url: "https://vyperprotocol.io",
14 | contacts: "email:info@vyperprotocol.io,link:https://docs.vyperprotocol.io/",
15 | policy: "https://github.com/vyper-protocol/vyper-core/blob/master/SECURITY.md",
16 | preferred_languages: "en",
17 | source_code: "https://github.com/vyper-protocol/vyper-core/tree/main/programs/rate-mock"
18 | }
19 |
20 | declare_id!("FB7HErqohbgaVV21BRiiMTuiBpeUYT8Yw7Z6EdEL7FAG");
21 |
22 | #[program]
23 | pub mod rate_mock {
24 |
25 | use super::*;
26 |
27 | pub fn initialize(ctx: Context) -> Result<()> {
28 | msg!("rate-mock: initialize");
29 |
30 | let clock = Clock::get()?;
31 | let rate_data = &mut ctx.accounts.rate_data;
32 | rate_data.fair_value = [dec!(1).serialize(); 10];
33 | rate_data.refreshed_slot = clock.slot;
34 | rate_data.authority = ctx.accounts.authority.key();
35 |
36 | msg!("rate_data.fair_value: {:?}", rate_data.fair_value);
37 | msg!("rate_data.refreshed_slot: {}", rate_data.refreshed_slot);
38 | msg!("rate_data.authority: {}", rate_data.authority);
39 |
40 | Ok(())
41 | }
42 |
43 | pub fn set_fair_value(ctx: Context, fair_value: f64) -> Result<()> {
44 | msg!("rate-mock: set_fair_value");
45 |
46 | let clock = Clock::get()?;
47 | let rate_data = &mut ctx.accounts.rate_data;
48 | rate_data.fair_value[0] = Decimal::from_f64(fair_value)
49 | .ok_or(RateMockErrorCode::MathError)?
50 | .serialize();
51 | rate_data.refreshed_slot = clock.slot;
52 |
53 | msg!("rate_data.fair_value: {:?}", rate_data.fair_value);
54 | msg!("rate_data.refreshed_slot: {}", rate_data.refreshed_slot);
55 |
56 | Ok(())
57 | }
58 |
59 | pub fn refresh(ctx: Context) -> Result<()> {
60 | msg!("rate-mock: refresh");
61 |
62 | let clock = Clock::get()?;
63 | let rate_data = &mut ctx.accounts.rate_data;
64 | rate_data.refreshed_slot = clock.slot;
65 |
66 | msg!("rate_data.refreshed_slot: {}", rate_data.refreshed_slot);
67 |
68 | Ok(())
69 | }
70 | }
71 |
72 | #[derive(Accounts)]
73 | pub struct InitializeContext<'info> {
74 | /// Signer account
75 | #[account(mut)]
76 | pub signer: Signer<'info>,
77 |
78 | /// CHECK:
79 | #[account()]
80 | pub authority: AccountInfo<'info>,
81 |
82 | #[account(init, payer = signer, space = RateState::LEN)]
83 | pub rate_data: Account<'info, RateState>,
84 |
85 | pub system_program: Program<'info, System>,
86 | }
87 |
88 | #[derive(Accounts)]
89 | pub struct SetFairValueContext<'info> {
90 | /// Signer account
91 | #[account()]
92 | pub authority: Signer<'info>,
93 |
94 | /// CHECK:
95 | #[account(mut, has_one = authority)]
96 | pub rate_data: Account<'info, RateState>,
97 | }
98 |
99 | #[derive(Accounts)]
100 | pub struct RefreshRateContext<'info> {
101 | /// Signer account
102 | #[account()]
103 | pub authority: Signer<'info>,
104 |
105 | /// CHECK:
106 | #[account(mut)]
107 | pub rate_data: Account<'info, RateState>,
108 | }
109 |
110 | #[account]
111 | pub struct RateState {
112 | pub fair_value: [[u8; 16]; 10],
113 | pub refreshed_slot: u64,
114 | pub authority: Pubkey,
115 | }
116 |
117 | impl RateState {
118 | pub const LEN: usize = 8 + // discriminator
119 | 16*10 + // pub fair_value: [[u8; 16]; 10],
120 | 8 + // pub refreshed_slot: u64,
121 | 32 // pub authority: Pubkey,
122 | ;
123 | }
124 |
--------------------------------------------------------------------------------
/programs/rate-poolv2/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "rate-poolv2"
3 | version = "0.1.0"
4 | description = "Created with Anchor"
5 | edition = "2021"
6 |
7 | [lib]
8 | crate-type = ["cdylib", "lib"]
9 | name = "rate_poolv2"
10 |
11 | [features]
12 | no-entrypoint = []
13 | no-idl = []
14 | no-log-ix-name = []
15 | cpi = ["no-entrypoint"]
16 | default = []
17 |
18 | [dependencies]
19 | anchor-lang = "0.24.2"
20 | anchor-spl = "0.24.2"
21 | rust_decimal = { version="1.24", features=["maths", "borsh"] }
22 | rust_decimal_macros = "1.24"
23 | solana-security-txt = "1.0.1"
--------------------------------------------------------------------------------
/programs/rate-poolv2/Xargo.toml:
--------------------------------------------------------------------------------
1 | [target.bpfel-unknown-unknown.dependencies.std]
2 | features = []
3 |
--------------------------------------------------------------------------------
/programs/rate-poolv2/src/errors.rs:
--------------------------------------------------------------------------------
1 | use anchor_lang::prelude::*;
2 |
3 | #[error_code]
4 | pub enum RatePoolv2ErrorCode {
5 | #[msg("generic error")]
6 | GenericError,
7 |
8 | #[msg("math error")]
9 | MathError,
10 | }
11 |
--------------------------------------------------------------------------------
/programs/rate-poolv2/src/state.rs:
--------------------------------------------------------------------------------
1 | use anchor_lang::prelude::*;
2 | use rust_decimal::{Decimal, MathematicalOps};
3 | use rust_decimal_macros::dec;
4 | use std::fmt;
5 |
6 | use crate::errors::RatePoolv2ErrorCode;
7 |
8 | pub struct SupplyWrapper {
9 | pub supply: u64,
10 | pub decimals: u8,
11 | }
12 |
13 | impl SupplyWrapper {
14 | pub fn to_dec(&self) -> Result {
15 | // not so readable cause checked methods,
16 | // we're simply doing:
17 | // supply / 10**decimals
18 |
19 | Decimal::from(self.supply)
20 | .checked_div(
21 | dec!(10)
22 | .checked_powu(self.decimals.into())
23 | .ok_or(RatePoolv2ErrorCode::MathError)?,
24 | )
25 | .ok_or_else(|| RatePoolv2ErrorCode::MathError.into())
26 | }
27 | }
28 |
29 | impl fmt::Display for SupplyWrapper {
30 | // This trait requires `fmt` with this exact signature.
31 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32 | // Write strictly the first element into the supplied output
33 | // stream: `f`. Returns `fmt::Result` which indicates whether the
34 | // operation succeeded or failed. Note that `write!` uses syntax which
35 | // is very similar to `println!`.
36 | write!(f, "{} (decimals: {})", self.supply, self.decimals)
37 | }
38 | }
39 |
40 | #[cfg(test)]
41 | mod tests {
42 | use super::*;
43 | use rust_decimal_macros::dec;
44 |
45 | #[test]
46 | fn test() {
47 | let sw = SupplyWrapper {
48 | supply: 1_500_000,
49 | decimals: 6,
50 | };
51 | assert_eq!(sw.to_dec().unwrap(), dec!(1.5));
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/programs/rate-pyth/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "rate-pyth"
3 | version = "0.1.0"
4 | description = "Created with Anchor"
5 | edition = "2021"
6 |
7 | [lib]
8 | crate-type = ["cdylib", "lib"]
9 | name = "rate_pyth"
10 |
11 | [features]
12 | no-entrypoint = []
13 | no-idl = []
14 | no-log-ix-name = []
15 | cpi = ["no-entrypoint"]
16 | default = []
17 |
18 | [dependencies]
19 | anchor-lang = "0.24.2"
20 | pyth-sdk-solana = "0.6.1"
21 | rust_decimal = { version="1.24", features=["maths", "borsh"] }
22 | rust_decimal_macros = "1.24"
23 | solana-security-txt = "1.0.1"
--------------------------------------------------------------------------------
/programs/rate-pyth/Xargo.toml:
--------------------------------------------------------------------------------
1 | [target.bpfel-unknown-unknown.dependencies.std]
2 | features = []
3 |
--------------------------------------------------------------------------------
/programs/rate-pyth/src/errors.rs:
--------------------------------------------------------------------------------
1 | use anchor_lang::prelude::*;
2 |
3 | #[error_code]
4 | pub enum RatePythErrorCode {
5 | #[msg("generic error")]
6 | GenericError,
7 |
8 | // #[msg("invalid aggregator owner")]
9 | // InvalidAggregatorOwner,
10 | #[msg("invalid aggregators number")]
11 | InvalidAggregatorsNumber,
12 |
13 | #[msg("math error")]
14 | MathError,
15 | }
16 |
--------------------------------------------------------------------------------
/programs/rate-pyth/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub mod errors;
2 |
3 | use crate::errors::RatePythErrorCode;
4 |
5 | use anchor_lang::prelude::*;
6 | use pyth_sdk_solana::{load_price_feed_from_account_info, Price, PriceFeed};
7 | use rust_decimal::{Decimal, MathematicalOps};
8 | use rust_decimal_macros::dec;
9 |
10 | #[cfg(not(feature = "no-entrypoint"))]
11 | solana_security_txt::security_txt! {
12 | name: "Rate Pyth | Vyper Core",
13 | project_url: "https://vyperprotocol.io",
14 | contacts: "email:info@vyperprotocol.io,link:https://docs.vyperprotocol.io/",
15 | policy: "https://github.com/vyper-protocol/vyper-core/blob/master/SECURITY.md",
16 | preferred_languages: "en",
17 | source_code: "https://github.com/vyper-protocol/vyper-core/tree/main/programs/rate-pyth"
18 | }
19 |
20 | declare_id!("3mxtC2cGVhHucUg4p58MVzVqUKLyiy1zWqRkRQdgUBPT");
21 |
22 | #[program]
23 | pub mod rate_pyth {
24 |
25 | use super::*;
26 |
27 | pub fn initialize(ctx: Context) -> Result<()> {
28 | let aggregators = ctx.remaining_accounts;
29 |
30 | // check the correct number of provided aggregators
31 | if aggregators.is_empty() || aggregators.len() > 10 {
32 | return Err(error!(RatePythErrorCode::InvalidAggregatorsNumber));
33 | }
34 |
35 | // TODO check feed owner ?
36 |
37 | // build the rate data state
38 | let rate_data = &mut ctx.accounts.rate_data;
39 | rate_data.fair_value = [Decimal::ZERO.serialize(); 10];
40 | rate_data.pyth_oracles = [None; 10];
41 | for (i, aggr) in aggregators.iter().enumerate() {
42 | rate_data.pyth_oracles[i] = Some(aggr.key());
43 | }
44 |
45 | set_data_from_oracles(rate_data, aggregators)?;
46 |
47 | Ok(())
48 | }
49 |
50 | pub fn refresh(ctx: Context) -> Result<()> {
51 | let aggregators = ctx.remaining_accounts;
52 | let rate_data = &mut ctx.accounts.rate_data;
53 |
54 | // check that the number of oracles provided is eq to the one saved during init
55 | require_eq!(
56 | aggregators.len(),
57 | rate_data
58 | .pyth_oracles
59 | .iter()
60 | .filter(|&n| n.is_some())
61 | .count(),
62 | RatePythErrorCode::InvalidAggregatorsNumber
63 | );
64 |
65 | set_data_from_oracles(rate_data, aggregators)?;
66 |
67 | let clock = Clock::get()?;
68 | rate_data.refreshed_slot = clock.slot;
69 |
70 | Ok(())
71 | }
72 | }
73 |
74 | #[derive(Accounts)]
75 | pub struct InitializeContext<'info> {
76 | /// Signer account
77 | #[account(mut)]
78 | pub signer: Signer<'info>,
79 |
80 | #[account(init, payer = signer, space = RateState::LEN)]
81 | pub rate_data: Account<'info, RateState>,
82 |
83 | pub system_program: Program<'info, System>,
84 | }
85 |
86 | #[derive(Accounts)]
87 | pub struct RefreshRateContext<'info> {
88 | /// Signer account
89 | #[account()]
90 | pub signer: Signer<'info>,
91 |
92 | #[account(mut)]
93 | pub rate_data: Account<'info, RateState>,
94 | }
95 |
96 | #[account]
97 | pub struct RateState {
98 | pub fair_value: [[u8; 16]; 10],
99 | pub refreshed_slot: u64,
100 | pub pyth_oracles: [Option; 10],
101 | }
102 |
103 | impl RateState {
104 | pub const LEN: usize = 8 + // discriminator
105 | 16*10 + // pub fair_value: [[u8; 16]; 10],
106 | 8 + // pub refreshed_slot: u64,
107 | 10*(1+32) // pub pyth_oracles: [Option; 10],
108 | ;
109 | }
110 |
111 | fn set_data_from_oracles(
112 | rate_data: &mut Account,
113 | oracles: &[AccountInfo],
114 | ) -> Result<()> {
115 | for (i, oracle) in oracles.iter().enumerate() {
116 | if let Some(serialized_oracle) = rate_data.pyth_oracles[i] {
117 | // check that the provided aggregator is the correct one
118 | require_keys_eq!(serialized_oracle, oracle.key());
119 |
120 | // load and deserialize oracle
121 | let price_feed: PriceFeed = load_price_feed_from_account_info(oracle).unwrap();
122 | let current_price: Price = price_feed.get_current_price().unwrap();
123 |
124 | #[cfg(feature = "debug")]
125 | {
126 | msg!("+ current pyth feed: {:?}", price_feed);
127 | msg!("+ current pyth price: {:?}", current_price);
128 | }
129 |
130 | let current_price_mantissa = Decimal::from(current_price.price);
131 | let current_price_expo = Decimal::from(current_price.expo);
132 |
133 | let fair_value = current_price_mantissa * dec!(10).powd(current_price_expo);
134 | msg!("saving fair value {:?}", fair_value);
135 |
136 | rate_data.fair_value[i] = fair_value.serialize();
137 | } else {
138 | return Err(error!(RatePythErrorCode::InvalidAggregatorsNumber));
139 | }
140 | }
141 |
142 | // save current slot
143 | rate_data.refreshed_slot = Clock::get()?.slot;
144 |
145 | msg!("rate_data.fair_value {:?}", rate_data.fair_value);
146 | msg!("rate_data.refreshed_slot: {}", rate_data.refreshed_slot);
147 |
148 | Ok(())
149 | }
150 |
--------------------------------------------------------------------------------
/programs/rate-switchboard/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "rate-switchboard"
3 | version = "0.1.1"
4 | description = "Created with Anchor"
5 | edition = "2021"
6 |
7 | [lib]
8 | crate-type = ["cdylib", "lib"]
9 | name = "rate_switchboard"
10 |
11 | [features]
12 | no-entrypoint = []
13 | no-idl = []
14 | no-log-ix-name = []
15 | cpi = ["no-entrypoint"]
16 | default = []
17 |
18 | [dependencies]
19 | anchor-lang = "0.24.2"
20 | switchboard-v2 = "^0.1.11"
21 | rust_decimal = { version="1.24", features=["borsh"] }
22 | rust_decimal_macros = "1.24"
23 | vyper-utils = { path = "../../libs/vyper-utils" }
24 | solana-security-txt = "1.0.1"
--------------------------------------------------------------------------------
/programs/rate-switchboard/Xargo.toml:
--------------------------------------------------------------------------------
1 | [target.bpfel-unknown-unknown.dependencies.std]
2 | features = []
3 |
--------------------------------------------------------------------------------
/programs/rate-switchboard/src/errors.rs:
--------------------------------------------------------------------------------
1 | use anchor_lang::prelude::*;
2 |
3 | #[error_code]
4 | pub enum RateSwitchboardErrorCode {
5 | #[msg("generic error")]
6 | GenericError,
7 |
8 | #[msg("invalid aggregator owner")]
9 | InvalidAggregatorOwner,
10 |
11 | #[msg("invalid aggregators number")]
12 | InvalidAggregatorsNumber,
13 |
14 | #[msg("math error")]
15 | MathError,
16 | }
17 |
--------------------------------------------------------------------------------
/programs/rate-switchboard/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub mod errors;
2 |
3 | use crate::errors::RateSwitchboardErrorCode;
4 |
5 | use anchor_lang::prelude::*;
6 | use rust_decimal::{prelude::FromPrimitive, Decimal};
7 | use switchboard_v2::{AggregatorAccountData, SWITCHBOARD_V2_DEVNET, SWITCHBOARD_V2_MAINNET};
8 |
9 | #[cfg(not(feature = "no-entrypoint"))]
10 | solana_security_txt::security_txt! {
11 | name: "Rate Switchboard | Vyper Core",
12 | project_url: "https://vyperprotocol.io",
13 | contacts: "email:info@vyperprotocol.io,link:https://docs.vyperprotocol.io/",
14 | policy: "https://github.com/vyper-protocol/vyper-core/blob/master/SECURITY.md",
15 | preferred_languages: "en",
16 | source_code: "https://github.com/vyper-protocol/vyper-core/tree/main/programs/rate-switchboard"
17 | }
18 |
19 | declare_id!("2hGXiH1oEQwjCXRx8bNdHTi49ScZp7Mj2bxcjxtULKe1");
20 |
21 | #[program]
22 | pub mod rate_switchboard {
23 |
24 | use super::*;
25 |
26 | pub fn initialize(ctx: Context) -> Result<()> {
27 | let aggregators = ctx.remaining_accounts;
28 |
29 | // check the correct number of provided aggregators
30 | if aggregators.is_empty() || aggregators.len() > 10 {
31 | return Err(error!(RateSwitchboardErrorCode::InvalidAggregatorsNumber));
32 | }
33 |
34 | // check that all the switchboard aggregators have the correct owner
35 | for aggr in ctx.remaining_accounts {
36 | let owner = *aggr.owner;
37 | if owner != SWITCHBOARD_V2_DEVNET && owner != SWITCHBOARD_V2_MAINNET {
38 | return Err(error!(RateSwitchboardErrorCode::InvalidAggregatorOwner));
39 | }
40 | }
41 |
42 | // build the rate data state
43 | let rate_data = &mut ctx.accounts.rate_data;
44 | rate_data.fair_value = [Decimal::ZERO.serialize(); 10];
45 | rate_data.switchboard_aggregators = [None; 10];
46 | for (i, aggr) in aggregators.iter().enumerate() {
47 | rate_data.switchboard_aggregators[i] = Some(aggr.key());
48 | }
49 |
50 | set_data_from_aggregators(rate_data, aggregators)?;
51 |
52 | Ok(())
53 | }
54 |
55 | pub fn refresh(ctx: Context) -> Result<()> {
56 | let aggregators = ctx.remaining_accounts;
57 | let rate_data = &mut ctx.accounts.rate_data;
58 |
59 | // check that the number of aggregators provided is eq to the one saved during init
60 | require_eq!(
61 | aggregators.len(),
62 | rate_data
63 | .switchboard_aggregators
64 | .iter()
65 | .filter(|&n| n.is_some())
66 | .count(),
67 | RateSwitchboardErrorCode::InvalidAggregatorsNumber
68 | );
69 |
70 | set_data_from_aggregators(rate_data, aggregators)?;
71 |
72 | let clock = Clock::get()?;
73 | rate_data.refreshed_slot = clock.slot;
74 |
75 | Ok(())
76 | }
77 | }
78 |
79 | #[derive(Accounts)]
80 | pub struct InitializeContext<'info> {
81 | /// Signer account
82 | #[account(mut)]
83 | pub signer: Signer<'info>,
84 |
85 | #[account(init, payer = signer, space = RateState::LEN)]
86 | pub rate_data: Account<'info, RateState>,
87 |
88 | pub system_program: Program<'info, System>,
89 | }
90 |
91 | #[derive(Accounts)]
92 | pub struct RefreshRateContext<'info> {
93 | /// Signer account
94 | #[account()]
95 | pub signer: Signer<'info>,
96 |
97 | #[account(mut)]
98 | pub rate_data: Account<'info, RateState>,
99 | }
100 |
101 | #[account]
102 | pub struct RateState {
103 | pub fair_value: [[u8; 16]; 10],
104 | pub refreshed_slot: u64,
105 | pub switchboard_aggregators: [Option; 10],
106 | }
107 |
108 | impl RateState {
109 | pub const LEN: usize = 8 + // discriminator
110 | 16*10 + // pub fair_value: [[u8; 16]; 10],
111 | 8 + // pub refreshed_slot: u64,
112 | 10*(1+32) // pub switchboard_aggregators: [Option; 10],
113 | ;
114 | }
115 |
116 | fn set_data_from_aggregators(
117 | rate_data: &mut Account,
118 | aggregators: &[AccountInfo],
119 | ) -> Result<()> {
120 | // read the aggregators data
121 | let mut oldest_slot: Option = None;
122 | for (i, aggr) in aggregators.iter().enumerate() {
123 | if let Some(serialized_aggregator) = rate_data.switchboard_aggregators[i] {
124 | // check that the provided aggregator is the correct one
125 | require_keys_eq!(serialized_aggregator, aggr.key());
126 |
127 | // load and deserialize feed
128 | let feed = AggregatorAccountData::new(aggr)?;
129 | let latest_confirmed_round = feed.latest_confirmed_round;
130 | let val: f64 = latest_confirmed_round.result.try_into()?;
131 |
132 | if oldest_slot.is_none() || oldest_slot > Some(latest_confirmed_round.round_open_slot) {
133 | oldest_slot = Some(latest_confirmed_round.round_open_slot);
134 | }
135 |
136 | match std::str::from_utf8(&feed.name) {
137 | Ok(feed_name) => msg!("switchboard aggregator {}", feed_name),
138 | _ => msg!("switchboard aggregator"),
139 | };
140 | msg!("+ val: {}", val,);
141 | let latest_confirmed_round_slot = latest_confirmed_round.round_open_slot;
142 | msg!("+ confirmed_round_slot: {}", latest_confirmed_round_slot);
143 |
144 | rate_data.fair_value[i] = Decimal::from_f64(val)
145 | .ok_or(RateSwitchboardErrorCode::MathError)?
146 | .serialize();
147 | } else {
148 | return Err(error!(RateSwitchboardErrorCode::InvalidAggregatorsNumber));
149 | }
150 | }
151 |
152 | rate_data.refreshed_slot = oldest_slot.ok_or(RateSwitchboardErrorCode::GenericError)?;
153 |
154 | msg!("rate_data.fair_value {:?}", rate_data.fair_value);
155 | msg!("rate_data.refreshed_slot: {}", rate_data.refreshed_slot);
156 |
157 | Ok(())
158 | }
159 |
--------------------------------------------------------------------------------
/programs/rate-twap/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "rate-twap"
3 | version = "0.1.0"
4 | description = "Created with Anchor"
5 | edition = "2021"
6 |
7 | [lib]
8 | crate-type = ["cdylib", "lib"]
9 | name = "rate_twap"
10 |
11 | [features]
12 | no-entrypoint = []
13 | no-idl = []
14 | no-log-ix-name = []
15 | cpi = ["no-entrypoint"]
16 | default = []
17 |
18 | [dependencies]
19 | anchor-lang = "0.24.2"
20 | rust_decimal = { version="1.24", features=["borsh"] }
21 | rust_decimal_macros = "1.24"
22 | solana-security-txt = "1.0.1"
--------------------------------------------------------------------------------
/programs/rate-twap/Xargo.toml:
--------------------------------------------------------------------------------
1 | [target.bpfel-unknown-unknown.dependencies.std]
2 | features = []
3 |
--------------------------------------------------------------------------------
/programs/rate-twap/src/errors.rs:
--------------------------------------------------------------------------------
1 | use anchor_lang::prelude::*;
2 |
3 | #[error_code]
4 | pub enum RateTwapErrorCode {
5 | #[msg("generic error")]
6 | GenericError,
7 |
8 | #[msg("input error")]
9 | InputError,
10 |
11 | #[msg("empty samples")]
12 | EmptySamples,
13 |
14 | #[msg("another too recent sample")]
15 | AnotherTooRecentSample,
16 | }
17 |
--------------------------------------------------------------------------------
/programs/rate-twap/src/instructions/initialize.rs:
--------------------------------------------------------------------------------
1 | use crate::state::{RateState, SamplingData};
2 | use anchor_lang::prelude::*;
3 | use rust_decimal::Decimal;
4 |
5 | #[derive(Accounts)]
6 | #[instruction(input_data: InitializeInput)]
7 | pub struct InitializeContext<'info> {
8 | /// rate state data account
9 | #[account(init, payer = payer, space = RateState::len(input_data.sampling_size.try_into().unwrap()))]
10 | pub rate_state: Account<'info, RateState>,
11 |
12 | /// CHECK: source of rate values
13 | pub rate_state_source: AccountInfo<'info>,
14 |
15 | /// payer account
16 | #[account(mut)]
17 | pub payer: Signer<'info>,
18 |
19 | pub system_program: Program<'info, System>,
20 | }
21 |
22 | #[derive(AnchorDeserialize, AnchorSerialize, Clone, Copy, Debug)]
23 | pub struct InitializeInput {
24 | /// delta between two samples slot
25 | /// this is useful to avoid sampling burst
26 | pub min_slot_delta: u64,
27 |
28 | /// numbero of samples to keep in storage, on those samples will be computed the avg
29 | pub sampling_size: u32,
30 | }
31 |
32 | pub fn handler(ctx: Context, input_data: InitializeInput) -> Result<()> {
33 | let rate_state = &mut ctx.accounts.rate_state;
34 |
35 | rate_state.rate_state_source = ctx.accounts.rate_state_source.key();
36 | rate_state.sampling_data =
37 | SamplingData::new(input_data.min_slot_delta, input_data.sampling_size)?;
38 |
39 | let account_data = ctx.accounts.rate_state_source.try_borrow_data()?;
40 | let mut account_data_slice: &[u8] = &account_data;
41 | let rate_state_source = CommonRateState::try_deserialize_unchecked(&mut account_data_slice)?;
42 |
43 | rate_state.sampling_data.try_add(
44 | rate_state_source.fair_value.map(Decimal::deserialize),
45 | rate_state_source.refreshed_slot,
46 | )?;
47 |
48 | rate_state.compute_twap()?;
49 |
50 | #[cfg(feature = "debug")]
51 | msg!("sampling_data: {:?}", rate_state.sampling_data);
52 |
53 | Ok(())
54 | }
55 |
56 | #[account]
57 | pub struct CommonRateState {
58 | pub fair_value: [[u8; 16]; 10],
59 | pub refreshed_slot: u64,
60 | }
61 |
--------------------------------------------------------------------------------
/programs/rate-twap/src/instructions/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod initialize;
2 | pub use initialize::*;
3 |
4 | pub mod refresh;
5 | pub use refresh::*;
6 |
--------------------------------------------------------------------------------
/programs/rate-twap/src/instructions/refresh.rs:
--------------------------------------------------------------------------------
1 | use anchor_lang::prelude::*;
2 | use rust_decimal::Decimal;
3 |
4 | use crate::state::rate_state::RateState;
5 |
6 | use super::initialize::CommonRateState;
7 |
8 | #[derive(Accounts)]
9 | pub struct RefreshRateContext<'info> {
10 | /// Signer account
11 | #[account()]
12 | pub signer: Signer<'info>,
13 |
14 | #[account(mut, has_one = rate_state_source)]
15 | pub rate_state: Account<'info, RateState>,
16 |
17 | /// CHECK: source of rate values
18 | pub rate_state_source: AccountInfo<'info>,
19 | }
20 |
21 | pub fn handler(ctx: Context) -> Result<()> {
22 | let rate_state = &mut ctx.accounts.rate_state;
23 |
24 | let account_data = ctx.accounts.rate_state_source.try_borrow_data()?;
25 | let mut account_data_slice: &[u8] = &account_data;
26 | let rate_state_source = CommonRateState::try_deserialize_unchecked(&mut account_data_slice)?;
27 |
28 | rate_state.sampling_data.try_add(
29 | rate_state_source.fair_value.map(Decimal::deserialize),
30 | rate_state_source.refreshed_slot,
31 | )?;
32 |
33 | rate_state.compute_twap()?;
34 |
35 | #[cfg(feature = "debug")]
36 | {
37 | msg!("sampling_data: {:?}", rate_state.sampling_data);
38 | msg!(
39 | "rate_state fair_value: {:?}",
40 | rate_state.fair_value.map(Decimal::deserialize)
41 | );
42 | msg!(
43 | "rate_state refreshed_slot: {:#?}",
44 | rate_state.refreshed_slot
45 | );
46 | }
47 |
48 | Ok(())
49 | }
50 |
--------------------------------------------------------------------------------
/programs/rate-twap/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub mod errors;
2 | pub mod instructions;
3 | pub mod state;
4 |
5 | use anchor_lang::prelude::*;
6 | use instructions::*;
7 |
8 | #[cfg(not(feature = "no-entrypoint"))]
9 | solana_security_txt::security_txt! {
10 | name: "Rate TWAP | Vyper Core",
11 | project_url: "https://vyperprotocol.io",
12 | contacts: "email:info@vyperprotocol.io,link:https://docs.vyperprotocol.io/",
13 | policy: "https://github.com/vyper-protocol/vyper-core/blob/master/SECURITY.md",
14 | preferred_languages: "en",
15 | source_code: "https://github.com/vyper-protocol/vyper-core/tree/main/programs/rate-twap"
16 | }
17 |
18 | declare_id!("8szo8C2w8rvtn1X7sux5PmxeErG5t2fxcV2Utugay4ct");
19 |
20 | #[program]
21 | pub mod rate_twap {
22 |
23 | use super::*;
24 |
25 | pub fn initialize(ctx: Context, input_data: InitializeInput) -> Result<()> {
26 | instructions::initialize::handler(ctx, input_data)
27 | }
28 |
29 | pub fn refresh(ctx: Context) -> Result<()> {
30 | instructions::refresh::handler(ctx)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/programs/rate-twap/src/state/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod sampling_data;
2 | pub use sampling_data::*;
3 |
4 | pub mod rate_state;
5 | pub use rate_state::*;
6 |
7 | pub mod sample_record;
8 | pub use sample_record::*;
9 |
--------------------------------------------------------------------------------
/programs/rate-twap/src/state/rate_state.rs:
--------------------------------------------------------------------------------
1 | use anchor_lang::prelude::*;
2 |
3 | use super::SamplingData;
4 |
5 | #[account]
6 | pub struct RateState {
7 | pub fair_value: [[u8; 16]; 10],
8 | pub refreshed_slot: u64,
9 | pub rate_state_source: Pubkey,
10 | pub sampling_data: SamplingData,
11 | }
12 |
13 | impl RateState {
14 | pub fn compute_twap(&mut self) -> Result<()> {
15 | let (twap_value, twap_refreshed_slot) = self.sampling_data.twap()?;
16 |
17 | self.fair_value = twap_value.map(|c| c.serialize());
18 | self.refreshed_slot = twap_refreshed_slot;
19 |
20 | Ok(())
21 | }
22 |
23 | pub fn len(sampling_size: usize) -> usize {
24 | 8 + // discriminator
25 | 10*16 + // pub fair_value: [[u8; 16]; 10],
26 | 8 + // pub refreshed_slot: u64,
27 | 32 + // pub rate_state_source: Pubkey,
28 | SamplingData::len(sampling_size) // pub sampling_data: SamplingData
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/programs/rate-twap/src/state/sample_record.rs:
--------------------------------------------------------------------------------
1 | use anchor_lang::prelude::*;
2 | use rust_decimal::Decimal;
3 | use std::fmt;
4 |
5 | #[derive(AnchorDeserialize, AnchorSerialize, Clone)]
6 | pub struct SampleRecord {
7 | value: [[u8; 16]; 10],
8 | slot: u64,
9 | }
10 |
11 | impl SampleRecord {
12 |
13 | pub const LEN: usize =
14 | 10 * 16 + // value: [[u8; 16]; 10],
15 | 8 // slot: u64,
16 | ;
17 |
18 | pub fn new(value: [Decimal; 10], slot: u64) -> SampleRecord {
19 | SampleRecord {
20 | value: value.map(|f| f.serialize()),
21 | slot,
22 | }
23 | }
24 |
25 | pub fn get_value(&self) -> [Decimal; 10] {
26 | self.value.map(Decimal::deserialize)
27 | }
28 |
29 | pub fn get_slot(&self) -> u64 {
30 | self.slot
31 | }
32 | }
33 |
34 | impl fmt::Debug for SampleRecord {
35 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 | f.debug_struct("SampleRecord")
37 | .field("value", &self.get_value())
38 | .field("slot", &self.get_slot())
39 | .finish()
40 | }
41 | }
42 |
43 | #[cfg(test)]
44 | mod tests {
45 | use rust_decimal_macros::dec;
46 |
47 | use super::*;
48 |
49 | #[test]
50 | fn test_getters() {
51 | let v = SampleRecord::new([dec!(10); 10], 10);
52 |
53 | assert_eq!(v.get_value(), [dec!(10); 10]);
54 | assert_eq!(v.get_slot(), 10);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/programs/rate-twap/src/state/sampling_data.rs:
--------------------------------------------------------------------------------
1 | use anchor_lang::prelude::*;
2 | use rust_decimal::Decimal;
3 | use std::fmt;
4 |
5 | use crate::errors::RateTwapErrorCode;
6 | use crate::state::sample_record::SampleRecord;
7 |
8 | #[derive(AnchorDeserialize, AnchorSerialize, Clone)]
9 | pub struct SamplingData {
10 | min_slot_delta: u64,
11 | max_samples_size: u32,
12 | samples: Vec,
13 | }
14 |
15 | impl fmt::Debug for SamplingData {
16 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17 | f.debug_struct("SamplingData")
18 | .field("min_slot_delta", &self.min_slot_delta)
19 | .field("max_samples_size", &self.max_samples_size)
20 | .field("samples len", &self.samples.len())
21 | .field("samples", &self.samples)
22 | .finish()
23 | }
24 | }
25 |
26 | impl SamplingData {
27 | pub fn new(min_slot_delta: u64, samples_size: u32) -> Result {
28 | require!(samples_size > 0, RateTwapErrorCode::InputError);
29 | Ok(SamplingData {
30 | min_slot_delta,
31 | max_samples_size: samples_size,
32 | samples: Vec::new(),
33 | })
34 | }
35 |
36 | /// will try to add a new sample
37 | /// this can fail if there's already another sample with a close slot as the new one
38 | /// the threshold used for checking is the self.min_slot_delta
39 | pub fn try_add(&mut self, value: [Decimal; 10], slot: u64) -> Result<()> {
40 | if !self.samples.is_empty() {
41 | // check if there's a too recent samples
42 | if (slot - self.samples[self.get_most_recent_sample_idx()].get_slot())
43 | < self.min_slot_delta
44 | {
45 | return err!(RateTwapErrorCode::AnotherTooRecentSample);
46 | }
47 |
48 | // check if max length is reached
49 | if self.samples.len() == self.max_samples_size.try_into().unwrap() {
50 | // remove oldest sample
51 | self.samples.remove(self.get_oldest_sample_idx());
52 | }
53 | }
54 |
55 | self.samples.push(SampleRecord::new(value, slot));
56 | Ok(())
57 | }
58 |
59 | /// Get the twap
60 | /// Average of all the values and the most recent slot
61 | pub fn twap(&self) -> Result<([Decimal; 10], u64)> {
62 | let avg = self.avg()?;
63 | let most_recent_slot = self.samples[self.get_most_recent_sample_idx()].get_slot();
64 |
65 | Ok((avg, most_recent_slot))
66 | }
67 |
68 | /// Get the average of all the samples value
69 | pub fn avg(&self) -> Result<[Decimal; 10]> {
70 | if self.samples.is_empty() {
71 | return err!(RateTwapErrorCode::EmptySamples);
72 | }
73 |
74 | let mut agg = [Decimal::ZERO; 10];
75 | for sample_record in &self.samples {
76 | for (idx, val) in sample_record.get_value().iter().enumerate() {
77 | agg[idx] = agg[idx]
78 | .checked_add(*val)
79 | .ok_or(RateTwapErrorCode::GenericError)?;
80 | }
81 | }
82 |
83 | Ok(agg.map(|c| c / Decimal::from(self.samples.len())))
84 | }
85 |
86 | /// Get the idx of the oldest sample
87 | fn get_oldest_sample_idx(&self) -> usize {
88 | let mut oldest_idx = 0;
89 |
90 | for (i, v) in self.samples.iter().enumerate() {
91 | if v.get_slot() < self.samples[oldest_idx].get_slot() {
92 | oldest_idx = i;
93 | }
94 | }
95 |
96 | oldest_idx
97 | }
98 |
99 | /// Get the idx of the most recent sample
100 | fn get_most_recent_sample_idx(&self) -> usize {
101 | let mut most_recent_idx = 0;
102 |
103 | for (i, v) in self.samples.iter().enumerate() {
104 | if v.get_slot() > self.samples[most_recent_idx].get_slot() {
105 | most_recent_idx = i;
106 | }
107 | }
108 |
109 | most_recent_idx
110 | }
111 |
112 | pub fn len(sampling_size: usize) -> usize {
113 | 8 + // min_slot_delta: u64,
114 | 4 + // max_samples_size: u32,
115 | 4 + (SampleRecord::LEN * sampling_size) // samples: Vec,
116 | }
117 | }
118 |
119 | #[cfg(test)]
120 | mod tests {
121 | use rust_decimal_macros::dec;
122 |
123 | use super::*;
124 |
125 | #[test]
126 | fn test_try_add() {
127 | let mut sampling = SamplingData::new(0, 4).unwrap();
128 |
129 | sampling.try_add([Decimal::ZERO; 10], 0).unwrap();
130 | sampling.try_add([Decimal::ZERO; 10], 1).unwrap();
131 | sampling.try_add([Decimal::ZERO; 10], 2).unwrap();
132 | sampling.try_add([Decimal::ZERO; 10], 3).unwrap();
133 | assert_eq!(sampling.twap().unwrap(), ([Decimal::ZERO; 10], 3));
134 |
135 | sampling.try_add([Decimal::ZERO; 10], 4).unwrap();
136 | assert_eq!(sampling.twap().unwrap(), ([Decimal::ZERO; 10], 4));
137 | }
138 |
139 | #[test]
140 | fn test_avg() {
141 | let mut sampling = SamplingData::new(0, 4).unwrap();
142 |
143 | sampling.try_add([Decimal::ZERO; 10], 0).unwrap();
144 | sampling.try_add([Decimal::ZERO; 10], 1).unwrap();
145 | sampling.try_add([Decimal::ZERO; 10], 2).unwrap();
146 | sampling.try_add([Decimal::ZERO; 10], 3).unwrap();
147 | assert_eq!(sampling.avg().unwrap(), [Decimal::ZERO; 10]);
148 |
149 | sampling.try_add([Decimal::ONE; 10], 4).unwrap();
150 | assert_eq!(sampling.avg().unwrap(), [dec!(0.25); 10]);
151 | }
152 |
153 | #[test]
154 | fn test_error_on_recent_slot() {
155 | let mut sampling = SamplingData::new(2, 4).unwrap();
156 |
157 | sampling.try_add([Decimal::ZERO; 10], 0).unwrap();
158 | sampling.try_add([Decimal::ZERO; 10], 2).unwrap();
159 | sampling.try_add([Decimal::ZERO; 10], 4).unwrap();
160 | assert!(sampling.try_add([Decimal::ZERO; 10], 5).is_err());
161 | }
162 |
163 | #[test]
164 | fn test_get_most_recent_sample_idx() {
165 | let mut sampling = SamplingData::new(2, 4).unwrap();
166 |
167 | sampling.try_add([Decimal::ZERO; 10], 0).unwrap();
168 | sampling.try_add([Decimal::ZERO; 10], 2).unwrap();
169 | sampling.try_add([Decimal::ZERO; 10], 4).unwrap();
170 | assert_eq!(sampling.get_most_recent_sample_idx(), 2);
171 | }
172 |
173 | #[test]
174 | fn test_get_oldest_sample_idx() {
175 | let mut sampling = SamplingData::new(2, 4).unwrap();
176 |
177 | sampling.try_add([Decimal::ZERO; 10], 0).unwrap();
178 | sampling.try_add([Decimal::ZERO; 10], 2).unwrap();
179 | sampling.try_add([Decimal::ZERO; 10], 4).unwrap();
180 | assert_eq!(sampling.get_oldest_sample_idx(), 0);
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/programs/redeem-logic-digital/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "redeem-logic-digital"
3 | version = "1.0.0"
4 | description = "Created with Anchor"
5 | edition = "2021"
6 |
7 | [lib]
8 | crate-type = ["cdylib", "lib"]
9 | name = "redeem_logic_digital"
10 |
11 | [features]
12 | no-entrypoint = []
13 | no-idl = []
14 | no-log-ix-name = []
15 | cpi = ["no-entrypoint"]
16 | default = []
17 |
18 | [dependencies]
19 | anchor-lang = "0.24.2"
20 | rust_decimal = { version="1.24", features=[ "maths", "borsh"] }
21 | rust_decimal_macros = "1.24"
22 | vyper-utils = { path = "../../libs/vyper-utils" }
23 | vyper-macros = { path = "../../libs/vyper-macros" }
24 | solana-security-txt = "1.0.1"
--------------------------------------------------------------------------------
/programs/redeem-logic-digital/Xargo.toml:
--------------------------------------------------------------------------------
1 | [target.bpfel-unknown-unknown.dependencies.std]
2 | features = []
3 |
--------------------------------------------------------------------------------
/programs/redeem-logic-digital/src/lib.rs:
--------------------------------------------------------------------------------
1 | // Vyper Redeem Logic: Digital Option Contract
2 | // Example: BTC/USD digital paying 1 USD if BTC/USD >= 20000 at expiry
3 | // Supports both digi-calls and digi-puts
4 | // Senior [0] is long the option, junior [1] is short
5 | // Senior amount is the option premium paid in any case, junior amount is the digi-size paid only if it expires ITM
6 | // For convention, call expires ITM if >=, while put expires ITM if <
7 | // Learn more here: https://vyperprotocol.notion.site/Contract-Payoff-Digital-39cab877a28a4a6fa349d9b816bd15a4
8 |
9 | use anchor_lang::prelude::*;
10 | use rust_decimal::prelude::*;
11 | use vyper_utils::redeem_logic_common::RedeemLogicErrors;
12 |
13 | #[cfg(not(feature = "no-entrypoint"))]
14 | solana_security_txt::security_txt! {
15 | name: "Redeem Logic Digital | Vyper Core",
16 | project_url: "https://vyperprotocol.io",
17 | contacts: "email:info@vyperprotocol.io,link:https://docs.vyperprotocol.io/",
18 | policy: "https://github.com/vyper-protocol/vyper-core/blob/master/SECURITY.md",
19 | preferred_languages: "en",
20 | source_code: "https://github.com/vyper-protocol/vyper-core/tree/main/programs/redeem-logic-digital"
21 | }
22 |
23 | declare_id!("5Dq9PjUJUG5dM9DzYFqKA4YZYeKJfGaM5Gy7NjpY3p5r");
24 |
25 | #[program]
26 | pub mod redeem_logic_digital {
27 | use super::*;
28 |
29 | pub fn initialize(ctx: Context, strike: f64, is_call: bool) -> Result<()> {
30 | let redeem_logic_config = &mut ctx.accounts.redeem_logic_config;
31 | redeem_logic_config.strike = Decimal::from_f64(strike)
32 | .ok_or(RedeemLogicErrors::MathError)?
33 | .serialize();
34 | redeem_logic_config.is_call = is_call;
35 |
36 | Ok(())
37 | }
38 |
39 | pub fn execute(
40 | ctx: Context,
41 | input_data: RedeemLogicExecuteInput,
42 | ) -> Result<()> {
43 | // input_data.is_valid()?;
44 | ctx.accounts.redeem_logic_config.dump();
45 |
46 | let result: RedeemLogicExecuteResult = execute_plugin(
47 | input_data.old_quantity,
48 | Decimal::deserialize(input_data.new_reserve_fair_value[0]),
49 | Decimal::deserialize(ctx.accounts.redeem_logic_config.strike),
50 | ctx.accounts.redeem_logic_config.is_call,
51 | )?;
52 |
53 | anchor_lang::solana_program::program::set_return_data(&result.try_to_vec()?);
54 |
55 | Ok(())
56 | }
57 | }
58 |
59 | #[derive(AnchorSerialize, AnchorDeserialize, Debug)]
60 | pub struct RedeemLogicExecuteInput {
61 | pub old_quantity: [u64; 2],
62 | pub old_reserve_fair_value: [[u8; 16]; 10],
63 | pub new_reserve_fair_value: [[u8; 16]; 10],
64 | }
65 |
66 | // impl RedeemLogicExecuteInput {
67 | // fn is_valid(&self) -> Result<()> {
68 | // Result::Ok(())
69 | // }
70 | // }
71 |
72 | #[derive(AnchorSerialize, AnchorDeserialize, Debug)]
73 | pub struct RedeemLogicExecuteResult {
74 | pub new_quantity: [u64; 2],
75 | pub fee_quantity: u64,
76 | }
77 |
78 | #[derive(Accounts)]
79 | pub struct InitializeContext<'info> {
80 | /// Tranche config account, where all the parameters are saved
81 | #[account(init, payer = payer, space = RedeemLogicConfig::LEN)]
82 | pub redeem_logic_config: Box>,
83 |
84 | /// Signer account
85 | #[account(mut)]
86 | pub payer: Signer<'info>,
87 | pub system_program: Program<'info, System>,
88 | }
89 |
90 | #[derive(Accounts)]
91 | pub struct ExecuteContext<'info> {
92 | #[account()]
93 | pub redeem_logic_config: Account<'info, RedeemLogicConfig>,
94 | }
95 |
96 | #[account]
97 | pub struct RedeemLogicConfig {
98 | /// true if call, false if put
99 | pub is_call: bool,
100 |
101 | pub strike: [u8; 16],
102 | }
103 |
104 | impl RedeemLogicConfig {
105 | pub const LEN: usize = 8 + // discriminator
106 | 1 + // pub is_call: bool,
107 | 16 // pub strike: [u8; 16],
108 | ;
109 |
110 | fn dump(&self) {
111 | msg!("redeem logic config:");
112 | msg!("+ is_call: {:?}", self.is_call);
113 | msg!("+ strike: {:?}", Decimal::deserialize(self.strike))
114 | }
115 | }
116 |
117 | fn execute_plugin(
118 | old_quantity: [u64; 2],
119 | new_spot: Decimal,
120 | strike: Decimal,
121 | is_call: bool,
122 | ) -> Result {
123 | let senior_new_quantity = {
124 | if (is_call && new_spot >= strike) || (!is_call && new_spot < strike) {
125 | old_quantity[1]
126 | } else {
127 | 0
128 | }
129 | };
130 |
131 | let junior_new_quantity = old_quantity.iter().sum::() - senior_new_quantity;
132 |
133 | Ok(RedeemLogicExecuteResult {
134 | new_quantity: [senior_new_quantity, junior_new_quantity],
135 | fee_quantity: 0,
136 | })
137 | }
138 |
139 | #[cfg(test)]
140 | mod tests {
141 | use super::*;
142 |
143 | #[test]
144 | fn test_call_itm() {
145 | let old_quantity = [100_000; 2];
146 | let new_spot = Decimal::ONE_HUNDRED;
147 | let strike = Decimal::ONE_HUNDRED;
148 | let is_call = true;
149 |
150 | let res = execute_plugin(old_quantity, new_spot, strike, is_call).unwrap();
151 |
152 | assert_eq!(res.new_quantity[0], 100_000);
153 | assert_eq!(res.new_quantity[1], 100_000);
154 | assert_eq!(res.fee_quantity, 0);
155 | assert_eq!(
156 | old_quantity.iter().sum::(),
157 | res.new_quantity.iter().sum::() + res.fee_quantity
158 | );
159 | }
160 |
161 | #[test]
162 | fn test_call_otm() {
163 | let old_quantity = [100_000; 2];
164 | let new_spot = Decimal::ONE;
165 | let strike = Decimal::ONE_HUNDRED;
166 | let is_call = true;
167 |
168 | let res = execute_plugin(old_quantity, new_spot, strike, is_call).unwrap();
169 |
170 | assert_eq!(res.new_quantity[0], 0);
171 | assert_eq!(res.new_quantity[1], 200_000);
172 | assert_eq!(res.fee_quantity, 0);
173 | assert_eq!(
174 | old_quantity.iter().sum::(),
175 | res.new_quantity.iter().sum::() + res.fee_quantity
176 | );
177 | }
178 |
179 | #[test]
180 | fn test_put_itm() {
181 | let old_quantity = [100_000; 2];
182 | let new_spot = Decimal::ONE;
183 | let strike = Decimal::ONE_HUNDRED;
184 | let is_call = false;
185 |
186 | let res = execute_plugin(old_quantity, new_spot, strike, is_call).unwrap();
187 |
188 | assert_eq!(res.new_quantity[0], 100_000);
189 | assert_eq!(res.new_quantity[1], 100_000);
190 | assert_eq!(res.fee_quantity, 0);
191 | assert_eq!(
192 | old_quantity.iter().sum::(),
193 | res.new_quantity.iter().sum::() + res.fee_quantity
194 | );
195 | }
196 |
197 | #[test]
198 | fn test_put_otm() {
199 | let old_quantity = [100_000; 2];
200 | let new_spot = Decimal::ONE_HUNDRED;
201 | let strike = Decimal::ONE_HUNDRED;
202 | let is_call = false;
203 |
204 | let res = execute_plugin(old_quantity, new_spot, strike, is_call).unwrap();
205 |
206 | assert_eq!(res.new_quantity[0], 0);
207 | assert_eq!(res.new_quantity[1], 200_000);
208 | assert_eq!(res.fee_quantity, 0);
209 | assert_eq!(
210 | old_quantity.iter().sum::(),
211 | res.new_quantity.iter().sum::() + res.fee_quantity
212 | );
213 | }
214 | }
215 |
--------------------------------------------------------------------------------
/programs/redeem-logic-farming/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "redeem-logic-farming"
3 | version = "2.0.0"
4 | description = "Created with Anchor"
5 | edition = "2021"
6 |
7 | [lib]
8 | crate-type = ["cdylib", "lib"]
9 | name = "redeem_logic_farming"
10 |
11 | [features]
12 | no-entrypoint = []
13 | no-idl = []
14 | no-log-ix-name = []
15 | cpi = ["no-entrypoint"]
16 | default = []
17 |
18 | [dependencies]
19 | anchor-lang = "0.24.2"
20 | rust_decimal = {version = "1.24", features = ["maths", "borsh"]}
21 | rust_decimal_macros = "1.24"
22 | vyper-utils = { path = "../../libs/vyper-utils" }
23 | vyper-macros = { path = "../../libs/vyper-macros" }
24 | solana-security-txt = "1.0.1"
--------------------------------------------------------------------------------
/programs/redeem-logic-farming/Xargo.toml:
--------------------------------------------------------------------------------
1 | [target.bpfel-unknown-unknown.dependencies.std]
2 | features = []
3 |
--------------------------------------------------------------------------------
/programs/redeem-logic-fila/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "redeem-logic-fila"
3 | version = "1.0.0"
4 | description = "Created with Anchor"
5 | edition = "2021"
6 |
7 | [lib]
8 | crate-type = ["cdylib", "lib"]
9 | name = "redeem_logic_fila"
10 |
11 | [features]
12 | no-entrypoint = []
13 | no-idl = []
14 | no-log-ix-name = []
15 | cpi = ["no-entrypoint"]
16 | default = []
17 |
18 | [dependencies]
19 | anchor-lang = "0.24.2"
20 | rust_decimal = { version = "1.24", features=["maths", "borsh"] }
21 | rust_decimal_macros = "1.24"
22 | vyper-utils = { path = "../../libs/vyper-utils" }
23 | vyper-macros = { path = "../../libs/vyper-macros" }
24 | solana-security-txt = "1.0.1"
--------------------------------------------------------------------------------
/programs/redeem-logic-fila/Xargo.toml:
--------------------------------------------------------------------------------
1 | [target.bpfel-unknown-unknown.dependencies.std]
2 | features = []
3 |
--------------------------------------------------------------------------------
/programs/redeem-logic-forward/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "redeem-logic-forward"
3 | version = "1.0.0"
4 | description = "Created with Anchor"
5 | edition = "2021"
6 |
7 | [lib]
8 | crate-type = ["cdylib", "lib"]
9 | name = "redeem_logic_forward"
10 |
11 | [features]
12 | no-entrypoint = []
13 | no-idl = []
14 | no-log-ix-name = []
15 | cpi = ["no-entrypoint"]
16 | default = []
17 |
18 | [dependencies]
19 | anchor-lang = "0.24.2"
20 | rust_decimal = { version="1.24", features=[ "maths", "borsh"] }
21 | rust_decimal_macros = "1.24"
22 | vyper-utils = { path = "../../libs/vyper-utils" }
23 | vyper-macros = { path = "../../libs/vyper-macros" }
24 | solana-security-txt = "1.0.1"
25 |
--------------------------------------------------------------------------------
/programs/redeem-logic-forward/Xargo.toml:
--------------------------------------------------------------------------------
1 | [target.bpfel-unknown-unknown.dependencies.std]
2 | features = []
3 |
--------------------------------------------------------------------------------
/programs/redeem-logic-lending-fee/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "redeem-logic-lending-fee"
3 | version = "1.0.0"
4 | description = "Created with Anchor"
5 | edition = "2021"
6 |
7 | [lib]
8 | crate-type = ["cdylib", "lib"]
9 | name = "redeem_logic_lending_fee"
10 |
11 | [features]
12 | no-entrypoint = []
13 | no-idl = []
14 | no-log-ix-name = []
15 | cpi = ["no-entrypoint"]
16 | default = []
17 |
18 | [dependencies]
19 | anchor-lang = "0.24.2"
20 | rust_decimal = { version = "1.24", features = ["borsh"] }
21 | rust_decimal_macros = "1.24"
22 | vyper-utils = { path = "../../libs/vyper-utils" }
23 | vyper-macros = { path = "../../libs/vyper-macros" }
24 | solana-security-txt = "1.0.1"
25 |
--------------------------------------------------------------------------------
/programs/redeem-logic-lending-fee/Xargo.toml:
--------------------------------------------------------------------------------
1 | [target.bpfel-unknown-unknown.dependencies.std]
2 | features = []
3 |
--------------------------------------------------------------------------------
/programs/redeem-logic-lending/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "redeem-logic-lending"
3 | version = "1.0.0"
4 | description = "Created with Anchor"
5 | edition = "2021"
6 | repository = "https://github.com/vyper-protocol/vyper-core/"
7 | homepage = "https://www.vyperprotocol.io/"
8 | authors = ["Vyper Labs ltd"]
9 |
10 | [lib]
11 | crate-type = ["cdylib", "lib"]
12 | name = "redeem_logic_lending"
13 |
14 | [features]
15 | no-entrypoint = []
16 | no-idl = []
17 | no-log-ix-name = []
18 | cpi = ["no-entrypoint"]
19 | default = []
20 |
21 | [dependencies]
22 | anchor-lang = "0.24.2"
23 | rust_decimal = { version = "1.24", features = ["borsh"] }
24 | rust_decimal_macros = "1.24"
25 | vyper-utils = { path = "../../libs/vyper-utils" }
26 | vyper-macros = { path = "../../libs/vyper-macros" }
27 | solana-security-txt = "1.0.1"
28 |
--------------------------------------------------------------------------------
/programs/redeem-logic-lending/Xargo.toml:
--------------------------------------------------------------------------------
1 | [target.bpfel-unknown-unknown.dependencies.std]
2 | features = []
3 |
--------------------------------------------------------------------------------
/programs/redeem-logic-settled-forward/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "redeem-logic-settled-forward"
3 | version = "1.0.0"
4 | description = "Created with Anchor"
5 | edition = "2021"
6 |
7 | [lib]
8 | crate-type = ["cdylib", "lib"]
9 | name = "redeem_logic_settled_forward"
10 |
11 | [features]
12 | no-entrypoint = []
13 | no-idl = []
14 | no-log-ix-name = []
15 | cpi = ["no-entrypoint"]
16 | default = []
17 |
18 | [dependencies]
19 | anchor-lang = "0.24.2"
20 | rust_decimal = { version="1.24", features=[ "maths", "borsh"] }
21 | rust_decimal_macros = "1.24"
22 | vyper-utils = { path = "../../libs/vyper-utils" }
23 | vyper-macros = { path = "../../libs/vyper-macros" }
24 | solana-security-txt = "1.0.1"
25 |
--------------------------------------------------------------------------------
/programs/redeem-logic-settled-forward/Xargo.toml:
--------------------------------------------------------------------------------
1 | [target.bpfel-unknown-unknown.dependencies.std]
2 | features = []
3 |
--------------------------------------------------------------------------------
/programs/redeem-logic-vanilla-option/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "redeem-logic-vanilla-option"
3 | version = "2.0.0"
4 | description = "Created with Anchor"
5 | edition = "2021"
6 |
7 | [lib]
8 | crate-type = ["cdylib", "lib"]
9 | name = "redeem_logic_vanilla_option"
10 |
11 | [features]
12 | no-entrypoint = []
13 | no-idl = []
14 | no-log-ix-name = []
15 | cpi = ["no-entrypoint"]
16 | default = []
17 |
18 | [dependencies]
19 | anchor-lang = "0.24.2"
20 | rust_decimal = { version="1.24", features=[ "maths", "borsh"] }
21 | rust_decimal_macros = "1.24"
22 | vyper-utils = { path = "../../libs/vyper-utils" }
23 | vyper-macros = { path = "../../libs/vyper-macros" }
24 | solana-security-txt = "1.0.1"
25 |
--------------------------------------------------------------------------------
/programs/redeem-logic-vanilla-option/Xargo.toml:
--------------------------------------------------------------------------------
1 | [target.bpfel-unknown-unknown.dependencies.std]
2 | features = []
3 |
--------------------------------------------------------------------------------
/programs/vyper-core/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "vyper-core"
3 | version = "0.1.0"
4 | edition = "2021"
5 | repository = "https://github.com/vyper-protocol/vyper-core/"
6 | homepage = "https://www.vyperprotocol.io/"
7 | authors = ["Vyper Labs ltd"]
8 | description = "Permissionless tranching on Solana"
9 |
10 | [lib]
11 | crate-type = ["cdylib", "lib"]
12 | name = "vyper_core"
13 |
14 | [features]
15 | no-entrypoint = []
16 | no-idl = []
17 | no-log-ix-name = []
18 | cpi = ["no-entrypoint"]
19 | default = ["debug"]
20 | debug = ["anchor-lang/anchor-debug"]
21 |
22 | [dependencies]
23 | anchor-lang = "0.24.2"
24 | anchor-spl = "0.24.2"
25 | vyper-utils = { path = "../../libs/vyper-utils" }
26 | vyper-macros = { path = "../../libs/vyper-macros" }
27 | bitflags = "1.3"
28 | boolinator = "2.4.0"
29 | solana-security-txt = "1.0.1"
30 | rust_decimal = { version="1.24", features=["borsh"] }
31 | rust_decimal_macros = "1.24"
--------------------------------------------------------------------------------
/programs/vyper-core/Xargo.toml:
--------------------------------------------------------------------------------
1 | [target.bpfel-unknown-unknown.dependencies.std]
2 | features = []
3 |
--------------------------------------------------------------------------------
/programs/vyper-core/src/errors.rs:
--------------------------------------------------------------------------------
1 | use anchor_lang::prelude::*;
2 |
3 | #[error_code]
4 | pub enum VyperErrorCode {
5 | #[msg("generic error")]
6 | GenericError,
7 |
8 | #[msg("invalid input")]
9 | InvalidInput,
10 |
11 | #[msg("failed to perform some math operation safely")]
12 | MathError,
13 |
14 | #[msg("Bits passed in do not result in valid halt flags")]
15 | InvalidTrancheHaltFlags,
16 |
17 | #[msg("Current operation is not available because is halted")]
18 | HaltError,
19 |
20 | #[msg("Bits passed in do not result in valid owner restricted instruction flags")]
21 | InvalidOwnerRestrictedIxFlags,
22 |
23 | #[msg("Current operation is available only for tranche config owner")]
24 | OwnerRestrictedIx,
25 |
26 | #[msg("Fair value is stale, refresh it")]
27 | StaleFairValue,
28 |
29 | #[msg("The redeem logic plugin didn't return anything, maybe we forgot to set solana_program::program::set_return_data()?")]
30 | RedeemLogicNoReturn,
31 |
32 | #[msg("cross-program invocation error calling a vyper plugin")]
33 | PluginCpiError,
34 |
35 | #[msg("bitmask value provided can't be converted")]
36 | InvalidBitmask,
37 |
38 | #[msg("current deposit exceeded cap")]
39 | DepositExceededCap,
40 | }
41 |
--------------------------------------------------------------------------------
/programs/vyper-core/src/instructions/collect_fee.rs:
--------------------------------------------------------------------------------
1 | use crate::state::TrancheConfig;
2 | use anchor_lang::prelude::*;
3 | use anchor_spl::token::{self, Token, TokenAccount, Transfer};
4 |
5 | #[derive(Accounts)]
6 | pub struct CollectFeeContext<'info> {
7 | /// Tranche config account, where all the parameters are saved
8 | #[account(mut,
9 | has_one = owner,
10 | has_one = tranche_authority,
11 | has_one = reserve)]
12 | pub tranche_config: Box>,
13 |
14 | /// CHECK:
15 | #[account(seeds = [tranche_config.key().as_ref(), b"authority".as_ref()], bump)]
16 | pub tranche_authority: AccountInfo<'info>,
17 |
18 | /// tranche reserve vault
19 | #[account(mut)]
20 | pub reserve: Box>,
21 |
22 | /// tranche reserve vault
23 | #[account(mut)]
24 | pub dest_reserve: Box>,
25 |
26 | pub owner: Signer<'info>,
27 |
28 | pub token_program: Program<'info, Token>,
29 | }
30 |
31 | impl<'info> CollectFeeContext<'info> {
32 | /// CpiContext for transferring reserve tokens from user to vault
33 | fn transfer_context(&self) -> CpiContext<'_, '_, '_, 'info, Transfer<'info>> {
34 | CpiContext::new(
35 | self.token_program.to_account_info(),
36 | Transfer {
37 | from: self.reserve.to_account_info(),
38 | to: self.dest_reserve.to_account_info(),
39 | authority: self.tranche_authority.to_account_info(),
40 | },
41 | )
42 | }
43 | }
44 |
45 | pub fn handler(ctx: Context) -> Result<()> {
46 | // TODO calculate tranching rounding and collect in fee the exceding part
47 |
48 | let fee_to_collect_quantity = ctx
49 | .accounts
50 | .tranche_config
51 | .tranche_data
52 | .fee_to_collect_quantity;
53 | msg!("collecting fee: {}", fee_to_collect_quantity);
54 | token::transfer(
55 | ctx.accounts
56 | .transfer_context()
57 | .with_signer(&[&ctx.accounts.tranche_config.authority_seeds()]),
58 | fee_to_collect_quantity,
59 | )?;
60 |
61 | ctx.accounts
62 | .tranche_config
63 | .tranche_data
64 | .fee_to_collect_quantity = 0;
65 |
66 | Ok(())
67 | }
68 |
--------------------------------------------------------------------------------
/programs/vyper-core/src/instructions/initialize.rs:
--------------------------------------------------------------------------------
1 | use crate::{
2 | errors::VyperErrorCode,
3 | state::{TrancheConfig, TrancheData},
4 | };
5 | use anchor_lang::prelude::*;
6 | use anchor_spl::token::{Mint, Token, TokenAccount};
7 |
8 | #[derive(Accounts)]
9 | #[instruction(input_data: InitializeInput)]
10 | pub struct InitializeContext<'info> {
11 | /// Signer account
12 | #[account(mut)]
13 | pub payer: Signer<'info>,
14 |
15 | /// CHECK: Owner of the tranche config
16 | #[account()]
17 | pub owner: AccountInfo<'info>,
18 |
19 | /// Tranche config account, where all the parameters are saved
20 | #[account(init, payer = payer, space = TrancheConfig::LEN)]
21 | pub tranche_config: Box>,
22 |
23 | /// CHECK:
24 | #[account(seeds = [tranche_config.key().as_ref(), b"authority".as_ref()], bump)]
25 | pub tranche_authority: AccountInfo<'info>,
26 |
27 | /// CHECK:
28 | #[account()]
29 | pub rate_program: AccountInfo<'info>,
30 |
31 | /// CHECK:
32 | #[account()]
33 | pub rate_program_state: AccountInfo<'info>,
34 |
35 | /// CHECK:
36 | #[account()]
37 | pub redeem_logic_program: AccountInfo<'info>,
38 |
39 | /// CHECK:
40 | #[account()]
41 | pub redeem_logic_program_state: AccountInfo<'info>,
42 |
43 | /// LP mint token to deposit
44 | #[account()]
45 | pub reserve_mint: Box>,
46 |
47 | /// Token account for vault reserve tokens
48 | #[account(init, payer = payer, seeds = [tranche_config.key().as_ref(), reserve_mint.key().as_ref()], bump, token::authority = tranche_authority, token::mint = reserve_mint)]
49 | pub reserve: Box>,
50 |
51 | /// Senior tranche mint
52 | #[account(init, payer = payer, mint::decimals = input_data.tranche_mint_decimals, mint::authority = tranche_authority)]
53 | pub senior_tranche_mint: Box>,
54 |
55 | /// Junior tranche mint
56 | #[account(init, payer = payer, mint::decimals = input_data.tranche_mint_decimals, mint::authority = tranche_authority)]
57 | pub junior_tranche_mint: Box>,
58 |
59 | pub system_program: Program<'info, System>,
60 | pub token_program: Program<'info, Token>,
61 | pub rent: Sysvar<'info, Rent>,
62 | }
63 |
64 | #[derive(AnchorDeserialize, AnchorSerialize, Clone, Copy, Debug)]
65 | pub struct InitializeInput {
66 | pub tranche_mint_decimals: u8,
67 | pub halt_flags: u16,
68 | pub owner_restricted_ixs: u16,
69 | }
70 |
71 | pub fn handler(ctx: Context, input_data: InitializeInput) -> Result<()> {
72 | let clock = Clock::get()?;
73 |
74 | // create tranche config account
75 |
76 | msg!("create tranche config");
77 | let tranche_config = &mut ctx.accounts.tranche_config;
78 |
79 | tranche_config.version = get_version_arr();
80 | tranche_config.owner = ctx.accounts.owner.key();
81 | tranche_config.tranche_data = TrancheData::new(clock.slot);
82 | tranche_config
83 | .tranche_data
84 | .set_halt_flags(input_data.halt_flags)?;
85 | tranche_config
86 | .tranche_data
87 | .set_owner_restricted_instructions(input_data.owner_restricted_ixs)?;
88 | tranche_config.tranche_authority = ctx.accounts.tranche_authority.key();
89 | tranche_config.authority_seed = tranche_config.key();
90 | tranche_config.authority_bump = [*ctx
91 | .bumps
92 | .get("tranche_authority")
93 | .ok_or(VyperErrorCode::GenericError)?];
94 | tranche_config.reserve_mint = ctx.accounts.reserve_mint.key();
95 | tranche_config.reserve = ctx.accounts.reserve.key();
96 | tranche_config.rate_program = ctx.accounts.rate_program.key();
97 | tranche_config.rate_program_state = ctx.accounts.rate_program_state.key();
98 | tranche_config.redeem_logic_program = ctx.accounts.redeem_logic_program.key();
99 | tranche_config.redeem_logic_program_state = ctx.accounts.redeem_logic_program_state.key();
100 | tranche_config.senior_tranche_mint = ctx.accounts.senior_tranche_mint.key();
101 | tranche_config.junior_tranche_mint = ctx.accounts.junior_tranche_mint.key();
102 | tranche_config.created_at = clock.unix_timestamp;
103 |
104 | Ok(())
105 | }
106 |
107 | fn get_version_arr() -> [u8; 3] {
108 | [
109 | env!("CARGO_PKG_VERSION_MAJOR")
110 | .parse::()
111 | .expect("failed to parse major version"),
112 | env!("CARGO_PKG_VERSION_MINOR")
113 | .parse::()
114 | .expect("failed to parse minor version"),
115 | env!("CARGO_PKG_VERSION_PATCH")
116 | .parse::()
117 | .expect("failed to parse patch version"),
118 | ]
119 | }
120 |
--------------------------------------------------------------------------------
/programs/vyper-core/src/instructions/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod collect_fee;
2 | pub mod deposit;
3 | pub mod initialize;
4 | pub mod redeem;
5 | pub mod refresh_tranche_fair_value;
6 | pub mod update_tranche_data;
7 |
8 | pub use collect_fee::*;
9 | pub use deposit::*;
10 | pub use initialize::*;
11 | pub use redeem::*;
12 | pub use refresh_tranche_fair_value::*;
13 | pub use update_tranche_data::*;
14 |
--------------------------------------------------------------------------------
/programs/vyper-core/src/instructions/update_tranche_data.rs:
--------------------------------------------------------------------------------
1 | use crate::{errors::VyperErrorCode, state::TrancheConfig};
2 | use anchor_lang::prelude::*;
3 |
4 | bitflags::bitflags! {
5 | struct UpdateTrancheConfigFlags: u16 {
6 | const HALT_FLAGS = 1 << 0;
7 | const OWNER_RESTRICTED_IXS = 1 << 1;
8 | const RESERVE_FAIR_VALUE_STALE_SLOT_THRESHOLD = 1 << 2;
9 | const TRANCHE_FAIR_VALUE_STALE_SLOT_THRESHOLD = 1 << 3;
10 | const DEPOSIT_CAP = 1 << 4;
11 | }
12 | }
13 |
14 | #[derive(Accounts)]
15 | pub struct UpdateTrancheDataContext<'info> {
16 | pub owner: Signer<'info>,
17 |
18 | /// Tranche config account, where all the parameters are saved
19 | #[account(mut, has_one = owner)]
20 | pub tranche_config: Box>,
21 | }
22 |
23 | #[derive(AnchorSerialize, AnchorDeserialize, Default, Clone)]
24 | pub struct UpdateTrancheDataInput {
25 | pub bitmask: u16,
26 | pub halt_flags: u16,
27 | pub owner_restricted_ixs: u16,
28 | pub reserve_fair_value_stale_slot_threshold: u64,
29 | pub tranche_fair_value_stale_slot_threshold: u64,
30 | pub deposit_cap: [Option; 2],
31 | }
32 |
33 | impl UpdateTrancheDataInput {
34 | fn get_update_tranche_bitmask(&self) -> Option {
35 | UpdateTrancheConfigFlags::from_bits(self.bitmask)
36 | }
37 | }
38 |
39 | pub fn handler(
40 | ctx: Context,
41 | input_data: UpdateTrancheDataInput,
42 | ) -> Result<()> {
43 | // update tranche config account
44 |
45 | let tranche_data = &mut ctx.accounts.tranche_config.tranche_data;
46 |
47 | let update_bitmask = input_data
48 | .get_update_tranche_bitmask()
49 | .ok_or(VyperErrorCode::InvalidBitmask)?;
50 |
51 | // halt flags
52 |
53 | if update_bitmask.contains(UpdateTrancheConfigFlags::HALT_FLAGS) {
54 | msg!("update tranche_data halt_flags");
55 |
56 | #[cfg(feature = "debug")]
57 | msg!("+ old value: {}", tranche_data.get_halt_flags()?.bits());
58 |
59 | tranche_data.set_halt_flags(input_data.halt_flags)?;
60 |
61 | #[cfg(feature = "debug")]
62 | msg!("+ new value: {}", tranche_data.get_halt_flags()?.bits());
63 | }
64 |
65 | // owner restricted ixs
66 |
67 | if update_bitmask.contains(UpdateTrancheConfigFlags::OWNER_RESTRICTED_IXS) {
68 | msg!("update tranche_data owner_restricted_ixs");
69 |
70 | #[cfg(feature = "debug")]
71 | msg!(
72 | "+ old value: {}",
73 | tranche_data.get_owner_restricted_ixs()?.bits()
74 | );
75 |
76 | tranche_data.set_owner_restricted_instructions(input_data.owner_restricted_ixs)?;
77 |
78 | #[cfg(feature = "debug")]
79 | msg!(
80 | "+ new value: {}",
81 | tranche_data.get_owner_restricted_ixs()?.bits()
82 | );
83 | }
84 |
85 | // reserve fair value stale slot th
86 |
87 | if update_bitmask.contains(UpdateTrancheConfigFlags::RESERVE_FAIR_VALUE_STALE_SLOT_THRESHOLD) {
88 | msg!("update tranche_data reserve_fair_value stale_slot_threashold");
89 |
90 | #[cfg(feature = "debug")]
91 | msg!(
92 | "+ old value: {}",
93 | tranche_data
94 | .reserve_fair_value
95 | .slot_tracking
96 | .stale_slot_threshold
97 | );
98 |
99 | tranche_data
100 | .reserve_fair_value
101 | .slot_tracking
102 | .stale_slot_threshold = input_data.reserve_fair_value_stale_slot_threshold;
103 |
104 | #[cfg(feature = "debug")]
105 | msg!(
106 | "+ new value: {}",
107 | tranche_data
108 | .reserve_fair_value
109 | .slot_tracking
110 | .stale_slot_threshold
111 | );
112 | }
113 |
114 | // tranche fair value stale slot th
115 |
116 | if update_bitmask.contains(UpdateTrancheConfigFlags::TRANCHE_FAIR_VALUE_STALE_SLOT_THRESHOLD) {
117 | msg!("update tranche_data tranche_fair_value stale_slot_threashold");
118 |
119 | #[cfg(feature = "debug")]
120 | msg!(
121 | "+ old value: {}",
122 | tranche_data
123 | .tranche_fair_value
124 | .slot_tracking
125 | .stale_slot_threshold
126 | );
127 |
128 | tranche_data
129 | .tranche_fair_value
130 | .slot_tracking
131 | .stale_slot_threshold = input_data.tranche_fair_value_stale_slot_threshold;
132 |
133 | #[cfg(feature = "debug")]
134 | msg!(
135 | "+ new value: {}",
136 | tranche_data
137 | .tranche_fair_value
138 | .slot_tracking
139 | .stale_slot_threshold
140 | );
141 | }
142 |
143 | // deposit cap
144 |
145 | if update_bitmask.contains(UpdateTrancheConfigFlags::DEPOSIT_CAP) {
146 | msg!("update tranche_data deposit_cap");
147 |
148 | #[cfg(feature = "debug")]
149 | msg!("+ old value: {:?}", tranche_data.deposit_cap);
150 |
151 | tranche_data.deposit_cap = input_data.deposit_cap;
152 |
153 | #[cfg(feature = "debug")]
154 | msg!("+ old value: {:?}", tranche_data.deposit_cap);
155 | }
156 |
157 | Ok(())
158 | }
159 |
--------------------------------------------------------------------------------
/programs/vyper-core/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub mod errors;
2 | pub mod instructions;
3 | pub mod state;
4 | pub mod utils;
5 |
6 | use anchor_lang::prelude::*;
7 | use instructions::*;
8 | use vyper_macros::*;
9 |
10 | #[cfg(not(feature = "no-entrypoint"))]
11 | solana_security_txt::security_txt! {
12 | name: "Vyper Core",
13 | project_url: "https://vyperprotocol.io",
14 | contacts: "email:info@vyperprotocol.io,link:https://docs.vyperprotocol.io/",
15 | policy: "https://github.com/vyper-protocol/vyper-core/blob/master/SECURITY.md",
16 | preferred_languages: "en",
17 | source_code: "https://github.com/vyper-protocol/vyper-core/tree/main/programs/vyper-core"
18 | }
19 |
20 | declare_id!("vyPErCcGJKQQBeeQ59gXcWrDyU4vBrq8qQfacwmsAsp");
21 |
22 | #[program]
23 | pub mod vyper_core {
24 | use super::*;
25 |
26 | #[log_wrap_ix()]
27 | pub fn initialize(ctx: Context, input_data: InitializeInput) -> Result<()> {
28 | instructions::initialize::handler(ctx, input_data)
29 | }
30 |
31 | #[log_wrap_ix()]
32 | pub fn update_tranche_data(
33 | ctx: Context,
34 | input_data: UpdateTrancheDataInput,
35 | ) -> Result<()> {
36 | instructions::update_tranche_data::handler(ctx, input_data)
37 | }
38 |
39 | #[log_wrap_ix()]
40 | pub fn refresh_tranche_fair_value(ctx: Context) -> Result<()> {
41 | instructions::refresh_tranche_fair_value::handler(ctx)
42 | }
43 |
44 | #[log_wrap_ix()]
45 | pub fn deposit(ctx: Context, input_data: DepositInput) -> Result<()> {
46 | instructions::deposit::handler(ctx, input_data)
47 | }
48 |
49 | #[log_wrap_ix()]
50 | pub fn redeem(ctx: Context, input_data: RedeemInput) -> Result<()> {
51 | instructions::redeem::handler(ctx, input_data)
52 | }
53 |
54 | #[log_wrap_ix()]
55 | pub fn collect_fee(ctx: Context) -> Result<()> {
56 | instructions::collect_fee::handler(ctx)
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/programs/vyper-core/src/state/last_update.rs:
--------------------------------------------------------------------------------
1 | use anchor_lang::prelude::*;
2 |
3 | use crate::errors::VyperErrorCode;
4 |
5 | // TODO check jet protocol assert_size macro: https://github.com/jet-lab/program-libraries/blob/main/proc-macros/src/lib.rs
6 | // #[assert_size(aligns, 16)]
7 | #[repr(C, align(8))]
8 | #[derive(AnchorDeserialize, AnchorSerialize, Clone, Copy, Debug, Default)]
9 | pub struct LastUpdate {
10 | slot: u64,
11 | _padding: [u8; 8],
12 | }
13 |
14 | impl LastUpdate {
15 | /// Create new last update
16 | pub fn new(slot: u64) -> Self {
17 | Self {
18 | slot,
19 | _padding: [0_u8; 8],
20 | }
21 | }
22 |
23 | /// Return slots elapsed since given slot
24 | pub fn slots_elapsed(&self, slot: u64) -> Result {
25 | slot.checked_sub(self.slot)
26 | .ok_or_else(|| VyperErrorCode::MathError.into())
27 | }
28 |
29 | /// Set last update slot
30 | pub fn get_slot(&self) -> u64 {
31 | self.slot
32 | }
33 |
34 | /// Set last update slot
35 | pub fn update_slot(&mut self, slot: u64) {
36 | self.slot = slot;
37 | }
38 |
39 | pub const LEN: usize = 8 + // slot: u64,
40 | 8; // _padding: [u8; 8],
41 | }
42 |
--------------------------------------------------------------------------------
/programs/vyper-core/src/state/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod tranche_config;
2 | pub use tranche_config::*;
3 |
4 | pub mod tranche_data;
5 | pub use tranche_data::*;
6 |
7 | pub mod tranche_halt_flags;
8 | pub use tranche_halt_flags::*;
9 |
10 | pub mod owner_restricted_ix_flags;
11 | pub use owner_restricted_ix_flags::*;
12 |
13 | pub mod tranche_fair_value;
14 | pub use tranche_fair_value::*;
15 |
16 | pub mod reserve_fair_value;
17 | pub use reserve_fair_value::*;
18 |
19 | pub mod slot_tracking;
20 | pub use slot_tracking::*;
21 |
22 | pub mod last_update;
23 | pub use last_update::*;
24 |
--------------------------------------------------------------------------------
/programs/vyper-core/src/state/owner_restricted_ix_flags.rs:
--------------------------------------------------------------------------------
1 | bitflags::bitflags! {
2 | pub struct OwnerRestrictedIxFlags: u16 {
3 | /// Owner restricted: Deposits
4 | const DEPOSITS = 1 << 0;
5 |
6 | /// Owner restricted: Refreshes
7 | const REFRESHES = 1 << 1;
8 |
9 | /// Owner restricted: Redeems
10 | const REDEEMS = 1 << 2;
11 |
12 | /// Disable all operations
13 | const ALL = Self::DEPOSITS.bits
14 | | Self::REFRESHES.bits
15 | | Self::REDEEMS.bits;
16 |
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/programs/vyper-core/src/state/reserve_fair_value.rs:
--------------------------------------------------------------------------------
1 | use anchor_lang::prelude::*;
2 |
3 | use super::SlotTracking;
4 |
5 | #[repr(C, align(8))]
6 | #[derive(AnchorDeserialize, AnchorSerialize, Clone, Copy, Debug, Default)]
7 | pub struct ReserveFairValue {
8 | /// reserve fair value expressed in Decimal
9 | pub value: [[u8; 16]; 10],
10 | pub slot_tracking: SlotTracking,
11 | }
12 |
13 | impl ReserveFairValue {
14 | pub const LEN: usize = 16*10 + // pub value: [[u8; 16]; 10],
15 | SlotTracking::LEN; // pub slot_tracking: SlotTracking
16 | }
17 |
--------------------------------------------------------------------------------
/programs/vyper-core/src/state/slot_tracking.rs:
--------------------------------------------------------------------------------
1 | use anchor_lang::prelude::*;
2 |
3 | use crate::errors::VyperErrorCode;
4 |
5 | use super::LastUpdate;
6 |
7 | /// Tracking of slot information
8 | #[repr(C, align(8))]
9 | #[derive(AnchorDeserialize, AnchorSerialize, Clone, Copy, Debug, Default)]
10 | pub struct SlotTracking {
11 | last_update: LastUpdate,
12 |
13 | /// threshold for defining a slot tracked value stale
14 | pub stale_slot_threshold: u64,
15 | }
16 |
17 | impl SlotTracking {
18 | pub fn new(slot: u64) -> Self {
19 | Self {
20 | last_update: LastUpdate::new(slot),
21 | stale_slot_threshold: 2,
22 | }
23 | }
24 |
25 | pub fn update(&mut self, slot: u64) {
26 | self.last_update.update_slot(slot);
27 | }
28 |
29 | pub fn slot_elapsed(&self, current_slot: u64) -> Result {
30 | current_slot
31 | .checked_sub(self.last_update.get_slot())
32 | .ok_or_else(|| VyperErrorCode::MathError.into())
33 | }
34 |
35 | pub fn is_stale(&self, current_slot: u64) -> Result {
36 | Ok(self.slot_elapsed(current_slot)? >= self.stale_slot_threshold)
37 | }
38 |
39 | pub fn get_last_update_slot(&self) -> u64 {
40 | self.last_update.get_slot()
41 | }
42 |
43 | pub const LEN: usize = LastUpdate::LEN + // last_update: LastUpdate,
44 | 8; // pub stale_slot_threshold: u64,
45 | }
46 |
--------------------------------------------------------------------------------
/programs/vyper-core/src/state/tranche_config.rs:
--------------------------------------------------------------------------------
1 | use anchor_lang::prelude::*;
2 |
3 | use super::TrancheData;
4 |
5 | #[repr(C, align(8))]
6 | #[account]
7 | pub struct TrancheConfig {
8 | pub reserve_mint: Pubkey,
9 | pub reserve: Pubkey,
10 |
11 | pub tranche_data: TrancheData,
12 |
13 | /// Senior tranche mint public key
14 | pub senior_tranche_mint: Pubkey,
15 |
16 | /// Junior tranche mint public key
17 | pub junior_tranche_mint: Pubkey,
18 |
19 | /// Tranche configuration authority
20 | pub tranche_authority: Pubkey,
21 |
22 | pub authority_seed: Pubkey,
23 |
24 | pub authority_bump: [u8; 1],
25 |
26 | /// Account which is allowed to call restricted instructions
27 | pub owner: Pubkey,
28 |
29 | pub rate_program: Pubkey,
30 | pub rate_program_state: Pubkey,
31 |
32 | pub redeem_logic_program: Pubkey,
33 | pub redeem_logic_program_state: Pubkey,
34 |
35 | /// Program version when initialized: [major, minor, patch]
36 | pub version: [u8; 3],
37 |
38 | /// Creation date
39 | pub created_at: i64,
40 |
41 | /// Reserved space for future upgrades
42 | _reserved: [u8; 256],
43 | }
44 |
45 | impl TrancheConfig {
46 | pub fn authority_seeds(&self) -> [&[u8]; 3] {
47 | [
48 | self.authority_seed.as_ref(),
49 | b"authority".as_ref(),
50 | &self.authority_bump,
51 | ]
52 | }
53 |
54 | pub const LEN: usize = 8 + // discriminator
55 | 32 + // pub reserve_mint: Pubkey,
56 | 32 + // pub reserve: Pubkey,
57 | TrancheData::LEN + // pub tranche_data: TrancheData,
58 | 32 + // pub senior_tranche_mint: Pubkey,
59 | 32 + // pub junior_tranche_mint: Pubkey,
60 | 32 + // pub tranche_authority: Pubkey,
61 | 32 + // pub authority_seed: Pubkey,
62 | 1 + // pub authority_bump: [u8; 1],
63 | 32 + // pub owner: Pubkey,
64 | 32 + // pub rate_program: Pubkey,
65 | 32 + // pub rate_program_state: Pubkey,
66 | 32 + // pub redeem_logic_program: Pubkey,
67 | 32 + // pub redeem_logic_program_state: Pubkey,
68 | 3 + // pub version: [u8; 3],
69 | 8 + // pub created_at: i64;
70 | 256; // _reserved: [u8; 256],
71 | }
72 |
--------------------------------------------------------------------------------
/programs/vyper-core/src/state/tranche_data.rs:
--------------------------------------------------------------------------------
1 | use anchor_lang::prelude::*;
2 | use rust_decimal_macros::dec;
3 |
4 | use crate::errors::VyperErrorCode;
5 |
6 | use super::{
7 | OwnerRestrictedIxFlags, ReserveFairValue, SlotTracking, TrancheFairValue, TrancheHaltFlags,
8 | };
9 |
10 | #[repr(C, align(8))]
11 | #[derive(AnchorDeserialize, AnchorSerialize, Clone, Copy, Debug)]
12 | pub struct TrancheData {
13 | /// Current deposited quantities, for senior and junior cUSDC
14 | pub deposited_quantity: [u64; 2],
15 |
16 | ///
17 | pub fee_to_collect_quantity: u64,
18 |
19 | /// pe cUSDC / USDC
20 | pub reserve_fair_value: ReserveFairValue,
21 |
22 | /// pe [ sTranche / cUSDC ; jTranche / cUSDC ]
23 | pub tranche_fair_value: TrancheFairValue,
24 |
25 | /// halt flags
26 | halt_flags: u16,
27 |
28 | /// flags for owner-only instructions
29 | owner_restricted_ix: u16,
30 |
31 | /// deposit cap for senior side and junior side, if the value is None the cap is disabled
32 | pub deposit_cap: [Option; 2],
33 | }
34 |
35 | impl TrancheData {
36 | pub fn new(slot: u64) -> Self {
37 | Self {
38 | deposited_quantity: [0; 2],
39 | deposit_cap: [None; 2],
40 | reserve_fair_value: ReserveFairValue {
41 | value: [dec!(1).serialize(); 10],
42 | slot_tracking: SlotTracking::new(slot),
43 | },
44 | tranche_fair_value: TrancheFairValue {
45 | value: [dec!(1).serialize(); 2],
46 | slot_tracking: SlotTracking::new(slot),
47 | },
48 | halt_flags: 0,
49 | owner_restricted_ix: 0,
50 | fee_to_collect_quantity: 0,
51 | }
52 | }
53 |
54 | pub fn get_halt_flags(&self) -> Result {
55 | TrancheHaltFlags::from_bits(self.halt_flags)
56 | .ok_or_else(|| VyperErrorCode::InvalidTrancheHaltFlags.into())
57 | }
58 |
59 | pub fn set_halt_flags(&mut self, bits: u16) -> Result<()> {
60 | TrancheHaltFlags::from_bits(bits).ok_or(VyperErrorCode::InvalidTrancheHaltFlags)?;
61 | self.halt_flags = bits;
62 | Ok(())
63 | }
64 |
65 | pub fn get_owner_restricted_ixs(&self) -> Result {
66 | OwnerRestrictedIxFlags::from_bits(self.owner_restricted_ix)
67 | .ok_or_else(|| VyperErrorCode::InvalidOwnerRestrictedIxFlags.into())
68 | }
69 |
70 | pub fn set_owner_restricted_instructions(&mut self, bits: u16) -> Result<()> {
71 | OwnerRestrictedIxFlags::from_bits(bits)
72 | .ok_or(VyperErrorCode::InvalidOwnerRestrictedIxFlags)?;
73 | self.owner_restricted_ix = bits;
74 | Ok(())
75 | }
76 |
77 | pub const LEN: usize = 2*8 + // pub deposited_quantity: [u64; 2],
78 | 8 + // pub fee_to_collect_quantity: u64,
79 | 2 * (1 + 8) + // pub deposit_cap: [Option; 2],
80 | ReserveFairValue::LEN + // pub reserve_fair_value: ReserveFairValue,
81 | TrancheFairValue::LEN + // pub tranche_fair_value: TrancheFairValue,
82 | 2 + // halt_flags: u16,
83 | 2 + // owner_restricted_ix: u16,
84 | 64; // padding
85 | }
86 |
--------------------------------------------------------------------------------
/programs/vyper-core/src/state/tranche_fair_value.rs:
--------------------------------------------------------------------------------
1 | use super::SlotTracking;
2 | use anchor_lang::prelude::*;
3 |
4 | #[repr(C, align(8))]
5 | #[derive(AnchorDeserialize, AnchorSerialize, Clone, Copy, Debug, Default)]
6 | pub struct TrancheFairValue {
7 | /// tranches [senior, junior] fair values expressed in Decimal
8 | pub value: [[u8; 16]; 2],
9 | pub slot_tracking: SlotTracking,
10 | }
11 |
12 | impl TrancheFairValue {
13 | pub const LEN: usize = 16*2 + // pub value: [[u8; 16];2],
14 | SlotTracking::LEN; // pub slot_tracking: SlotTracking
15 | }
16 |
--------------------------------------------------------------------------------
/programs/vyper-core/src/state/tranche_halt_flags.rs:
--------------------------------------------------------------------------------
1 | bitflags::bitflags! {
2 | pub struct TrancheHaltFlags: u16 {
3 | /// Disable deposits
4 | const HALT_DEPOSITS = 1 << 0;
5 |
6 | /// Disable refreshes
7 | const HALT_REFRESHES = 1 << 1;
8 |
9 | /// Disable redeems
10 | const HALT_REDEEMS = 1 << 2;
11 |
12 | /// Disable all operations
13 | const HALT_ALL = Self::HALT_DEPOSITS.bits
14 | | Self::HALT_REFRESHES.bits
15 | | Self::HALT_REDEEMS.bits;
16 |
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/programs/vyper-core/src/utils.rs:
--------------------------------------------------------------------------------
1 | use anchor_lang::prelude::*;
2 |
3 | pub trait Input {
4 | fn is_valid(&self) -> Result<()>;
5 | }
6 |
--------------------------------------------------------------------------------
/rustfmt.toml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vyper-protocol/vyper-core/2fa059ea7f9f106a7240f70c767f436034b8de0f/rustfmt.toml
--------------------------------------------------------------------------------
/scripts/create_git_tag.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | PROGRAM_NAME=$1
4 | CLUSTER=$2
5 | VERSION=$3
6 |
7 | echo git tag -a ${PROGRAM_NAME}_${CLUSTER}_v${VERSION} -m \"${PROGRAM_NAME} v${VERSION} deployment on ${CLUSTER}\"
8 | echo git push origin --tags
--------------------------------------------------------------------------------
/scripts/deploy_devnet.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # vyper core
4 | # anchor deploy -p vyper-core --provider.cluster d --provider.wallet ~/Dev/VyperWallets/vyper-program-authority/authority.json
5 | # anchor idl init --provider.cluster m --provider.wallet ~/Dev/VyperWallets/vyper-program-authority/authority.json -f ./target/idl/vyper_core.json vyPErCcGJKQQBeeQ59gXcWrDyU4vBrq8qQfacwmsAsp
6 |
7 | # rate mock plugin
8 | # anchor deploy -p rate-mock --provider.cluster d --provider.wallet ~/Dev/VyperWallets/vyper-program-authority/authority.json
9 |
10 | # rate switchboard
11 | # anchor deploy -p rate-switchboard --provider.cluster d --provider.wallet ~/Dev/VyperWallets/vyper-program-authority/authority.json
12 | anchor idl init --provider.cluster m --provider.wallet ~/Dev/VyperWallets/vyper-program-authority/authority.json -f ./target/idl/rate_switchboard.json 2hGXiH1oEQwjCXRx8bNdHTi49ScZp7Mj2bxcjxtULKe1
13 |
14 | # rate pyth
15 | # anchor deploy -p rate-pyth --provider.cluster d --provider.wallet ~/Dev/VyperWallets/vyper-program-authority/authority.json
16 | anchor idl init --provider.cluster d --provider.wallet ~/Dev/VyperWallets/vyper-program-authority/authority.json -f ./target/idl/rate_pyth.json å
17 |
18 | # rate poolv2
19 | # anchor deploy -p rate-poolv2 --provider.cluster d --provider.wallet ~/Dev/VyperWallets/vyper-program-authority/authority.json
20 | anchor idl init --provider.cluster m --provider.wallet ~/Dev/VyperWallets/vyper-program-authority/authority.json -f ./target/idl/rate_poolv2.json 5Vm2YZK3SeGbXbtQpKVByP9EvYy78ahnjFXKkf9B3yzW
21 |
22 | # redeem logic fee plugin
23 | # anchor deploy -p redeem-logic-lending-fee --provider.cluster d --provider.wallet ~/Dev/VyperWallets/vyper-program-authority/authority.json
24 |
25 | # redeem logic farming
26 | # anchor deploy -p redeem-logic-farming --provider.cluster d --provider.wallet ~/Dev/VyperWallets/vyper-program-authority/authority.json
27 | anchor idl init --provider.cluster m --provider.wallet ~/Dev/VyperWallets/vyper-program-authority/authority.json -f ./target/idl/redeem_logic_farming.json Fd87TGcYmWs1Gfa7XXZycJwt9kXjRs8axMtxCWtCmowN
28 | anchor idl upgrade --provider.cluster m --provider.wallet ~/Dev/VyperWallets/vyper-program-authority/authority.json -f ./target/idl/redeem_logic_farming.json Fd87TGcYmWs1Gfa7XXZycJwt9kXjRs8axMtxCWtCmowN
29 |
30 | # redeem logic vanilla option
31 | anchor deploy -p redeem-logic-vanilla-option --provider.cluster m --provider.wallet ~/Dev/VyperWallets/vyper-program-authority/authority.json
32 | anchor idl init --provider.cluster m --provider.wallet ~/Dev/VyperWallets/vyper-program-authority/authority.json -f ./target/idl/redeem_logic_vanilla_option.json 8fSeRtFseNrjdf8quE2YELhuzLkHV7WEGRPA9Jz8xEVe
33 | anchor idl upgrade --provider.cluster m --provider.wallet ~/Dev/VyperWallets/vyper-program-authority/authority.json -f ./target/idl/redeem_logic_vanilla_option.json 8fSeRtFseNrjdf8quE2YELhuzLkHV7WEGRPA9Jz8xEVe
34 |
35 | # redeem logic forward
36 | # anchor deploy -p redeem-logic-forward --provider.cluster d --provider.wallet ~/Dev/VyperWallets/vyper-program-authority/authority.json
37 |
38 | # redeem logic settled-forward
39 | anchor deploy -p redeem-logic-settled-forward --provider.cluster d --provider.wallet ~/Dev/VyperWallets/vyper-program-authority/authority.json
40 | anchor idl init --provider.cluster m --provider.wallet ~/Dev/VyperWallets/vyper-program-authority/authority.json -f ./target/idl/redeem_logic_settled_forward.json 6vBg1GMtKj7EYDLWWt6tkHoDWLAAksNPbKWiXMic99qU
41 | anchor idl upgrade --provider.cluster d --provider.wallet ~/Dev/VyperWallets/vyper-program-authority/authority.json -f ./target/idl/redeem_logic_settled_forward.json 6vBg1GMtKj7EYDLWWt6tkHoDWLAAksNPbKWiXMic99qU
42 |
43 | # redeem logic digital
44 | anchor deploy -p redeem-logic-digital --provider.cluster d --provider.wallet ~/Dev/VyperWallets/vyper-program-authority/authority.json
45 | anchor idl init --provider.cluster d --provider.wallet ~/Dev/VyperWallets/vyper-program-authority/authority.json -f ./target/idl/redeem_logic_digital.json 5Dq9PjUJUG5dM9DzYFqKA4YZYeKJfGaM5Gy7NjpY3p5r
46 | anchor idl upgrade --provider.cluster d --provider.wallet ~/Dev/VyperWallets/vyper-program-authority/authority.json -f ./target/idl/redeem_logic_digital.json 5Dq9PjUJUG5dM9DzYFqKA4YZYeKJfGaM5Gy7NjpY3p5r
47 |
48 | # # # # # # # # # # # # # # # # # #
49 | # RECOVERY
50 | # # # # # # # # # # # # # # # # # #
51 |
52 | # solana-keygen recover -o ./ephemeral-kp.json prompt://
53 | # solana program deploy --buffer ./ephemeral-kp.json --upgrade-authority ~/Dev/VyperWallets/vyper-program-authority/authority.json -k ~/Dev/VyperWallets/vyper-program-authority/authority.json ./target/deploy/redeem_logic_farming.so
54 |
--------------------------------------------------------------------------------
/scripts/rate_mock/create.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { Program, Wallet } from "@project-serum/anchor";
3 | import { Connection, Keypair, PublicKey } from "@solana/web3.js";
4 | import { RateMock, IDL } from "../../target/types/rate_mock";
5 |
6 | const PLUGIN_PROGRAM_ID = new PublicKey("FB7HErqohbgaVV21BRiiMTuiBpeUYT8Yw7Z6EdEL7FAG");
7 |
8 | const main = async () => {
9 | const connection = new Connection("https://api.devnet.solana.com");
10 |
11 | const wallet = Wallet.local();
12 | const provider = new anchor.AnchorProvider(connection, wallet, {
13 | commitment: "confirmed",
14 | });
15 |
16 | const program = new Program(IDL, PLUGIN_PROGRAM_ID, provider);
17 |
18 | const rateData = anchor.web3.Keypair.generate();
19 | const tx = await program.methods
20 | .initialize()
21 | .accounts({
22 | signer: provider.wallet.publicKey,
23 | authority: new PublicKey("6zoqN77QehDFPanib6WfBRcYnh31QSBdbL64Aj9Eq2fM"),
24 | rateData: rateData.publicKey,
25 | })
26 | .signers([rateData])
27 | .rpc();
28 |
29 | console.log("tx: " + tx);
30 | console.log("rate plugin state: " + rateData.publicKey);
31 | };
32 |
33 | main();
34 |
--------------------------------------------------------------------------------
/scripts/rate_mock/read_state.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { Program, Wallet } from "@project-serum/anchor";
3 | import { Connection, Keypair, PublicKey } from "@solana/web3.js";
4 | import { RateMock, IDL } from "../../target/types/rate_mock";
5 |
6 | const PLUGIN_PROGRAM_ID = new PublicKey("FB7HErqohbgaVV21BRiiMTuiBpeUYT8Yw7Z6EdEL7FAG");
7 | const PLUGIN_STATE = new PublicKey("2MV14QPzUh1WVgMXY7nYuDzurHiHsx8qkhp7vPrV1shL");
8 |
9 | const main = async () => {
10 | const connection = new Connection("https://api.devnet.solana.com");
11 |
12 | const wallet = Wallet.local();
13 | const provider = new anchor.AnchorProvider(connection, wallet, {
14 | commitment: "confirmed",
15 | });
16 | const program = new Program(IDL, PLUGIN_PROGRAM_ID, provider);
17 | const account = await program.account.rateState.fetch(PLUGIN_STATE);
18 |
19 | console.log("account: ", account);
20 | };
21 |
22 | main();
23 |
--------------------------------------------------------------------------------
/scripts/rate_mock/set_fair_value.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { Program, Wallet } from "@project-serum/anchor";
3 | import { Connection, Keypair, PublicKey } from "@solana/web3.js";
4 | import { RateMock, IDL } from "../../target/types/rate_mock";
5 |
6 | // ANCHOR_WALLET=~/Dev/VyperWallets/devnet-plugins-authority/authority.json ts-node -T ./scripts/rate_mock/set_fair_value.ts
7 |
8 | const PLUGIN_PROGRAM_ID = new PublicKey("FB7HErqohbgaVV21BRiiMTuiBpeUYT8Yw7Z6EdEL7FAG");
9 | const PLUGIN_STATE = new PublicKey("2MV14QPzUh1WVgMXY7nYuDzurHiHsx8qkhp7vPrV1shL");
10 |
11 | const main = async () => {
12 | const connection = new Connection("https://api.devnet.solana.com");
13 |
14 | const wallet = Wallet.local();
15 | const provider = new anchor.AnchorProvider(connection, wallet, {
16 | commitment: "confirmed",
17 | });
18 |
19 | const program = new Program(IDL, PLUGIN_PROGRAM_ID, provider);
20 |
21 | const tx = await program.methods
22 | .setFairValue(5000)
23 | .accounts({
24 | authority: wallet.publicKey,
25 | rateData: PLUGIN_STATE,
26 | })
27 | .rpc();
28 |
29 | console.log("tx: " + tx);
30 | };
31 |
32 | main();
33 |
--------------------------------------------------------------------------------
/scripts/rate_poolv2/create.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { Program, Wallet } from "@project-serum/anchor";
3 | import { Connection, Keypair, PublicKey } from "@solana/web3.js";
4 | import { IDL } from "../../target/types/rate_poolv2";
5 |
6 | const PLUGIN_PROGRAM_ID = new PublicKey("5Vm2YZK3SeGbXbtQpKVByP9EvYy78ahnjFXKkf9B3yzW");
7 |
8 | const ORCA_POOL_ID = new PublicKey("JU8kmKzDHF9sXWsnoznaFDFezLsE5uomX2JkRMbmsQP");
9 |
10 | const QUOTE_USDC_TA = new PublicKey("75HgnSvXbWKZBpZHveX68ZzAhDqMzNDS29X6BGLtxMo1");
11 | const QUOTE_USDC_MINT = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
12 | const BASE_SOL_TA = new PublicKey("ANP74VNsHwSrq9uUSjiSNyNWvf6ZPrKTmE4gHoNd13Lg");
13 | const BASE_SOL_MINT = new PublicKey("So11111111111111111111111111111111111111112");
14 |
15 | const LP_MINT = new PublicKey("APDFRM3HMr8CAGXwKHiu2f5ePSpaiEJhaURwhsRrUUt9");
16 |
17 | const main = async () => {
18 | const provider = anchor.AnchorProvider.env();
19 |
20 | const program = new Program(IDL, PLUGIN_PROGRAM_ID, provider);
21 |
22 | const rateData = anchor.web3.Keypair.generate();
23 | const tx = await program.methods
24 | .initialize()
25 | .accounts({
26 | signer: provider.wallet.publicKey,
27 | rateData: rateData.publicKey,
28 | pool: ORCA_POOL_ID,
29 | baseMint: BASE_SOL_MINT,
30 | baseTokenAccount: BASE_SOL_TA,
31 | quoteMint: QUOTE_USDC_MINT,
32 | quoteTokenAccount: QUOTE_USDC_TA,
33 | lpMint: LP_MINT,
34 | })
35 | .signers([rateData])
36 | .rpc();
37 |
38 | console.log("tx: " + tx);
39 | console.log("rate plugin state: " + rateData.publicKey);
40 | };
41 |
42 | main();
43 |
--------------------------------------------------------------------------------
/scripts/rate_poolv2/read_state.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { Program, Wallet } from "@project-serum/anchor";
3 | import { Connection, Keypair, PublicKey } from "@solana/web3.js";
4 | import { RustDecimalWrapper } from "@vyper-protocol/rust-decimal-wrapper";
5 | import { IDL } from "../../target/types/rate_poolv2";
6 |
7 | const PLUGIN_PROGRAM_ID = new PublicKey("5Vm2YZK3SeGbXbtQpKVByP9EvYy78ahnjFXKkf9B3yzW");
8 | const PLUGIN_STATE = new PublicKey("Ft6sFaRyjSDHUtkhwDjn5Sz1Pke9Y466knSYNf5ac7yG");
9 |
10 | const main = async () => {
11 | const provider = anchor.AnchorProvider.env();
12 |
13 | const program = new Program(IDL, PLUGIN_PROGRAM_ID, provider);
14 | const account = await program.account.rateState.fetch(PLUGIN_STATE);
15 |
16 | console.log("account: ", account);
17 | };
18 |
19 | main();
20 |
--------------------------------------------------------------------------------
/scripts/rate_poolv2/refresh.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { Program, Wallet } from "@project-serum/anchor";
3 | import { Connection, Keypair, PublicKey } from "@solana/web3.js";
4 | import { IDL } from "../../target/types/rate_poolv2";
5 |
6 | const PLUGIN_PROGRAM_ID = new PublicKey("5Vm2YZK3SeGbXbtQpKVByP9EvYy78ahnjFXKkf9B3yzW");
7 | const PLUGIN_STATE = new PublicKey("Ft6sFaRyjSDHUtkhwDjn5Sz1Pke9Y466knSYNf5ac7yG");
8 |
9 | const main = async () => {
10 | const provider = anchor.AnchorProvider.env();
11 |
12 | const program = new Program(IDL, PLUGIN_PROGRAM_ID, provider);
13 | const account = await program.account.rateState.fetch(PLUGIN_STATE);
14 |
15 | const sig = await program.methods
16 | .refresh()
17 | .accounts({
18 | rateData: PLUGIN_STATE,
19 | baseMint: account.baseMint,
20 | baseTokenAccount: account.baseTokenAccount,
21 | quoteMint: account.quoteMint,
22 | quoteTokenAccount: account.quoteTokenAccount,
23 | lpMint: account.lpMint,
24 | })
25 | .rpc();
26 | console.log("sig: ", sig);
27 | };
28 |
29 | main();
30 |
--------------------------------------------------------------------------------
/scripts/rate_switchboard/create.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { Program, Wallet } from "@project-serum/anchor";
3 | import { Connection, Keypair, PublicKey } from "@solana/web3.js";
4 | import { IDL } from "../../target/types/rate_switchboard";
5 |
6 | const PLUGIN_PROGRAM_ID = new PublicKey("2hGXiH1oEQwjCXRx8bNdHTi49ScZp7Mj2bxcjxtULKe1");
7 |
8 | const LP_ORCA_SOL_USDC_SWITCHBOARD_AGGREGATOR = new PublicKey("3By5v1am74SMQUkM9va8iHgNLVEjGVMRmMSt9nDiuczZ");
9 | const SOL_USD_SWITCHBOARD_AGGREGATOR = new PublicKey("GvDMxPzN1sCj7L26YDK2HnMRXEQmQ2aemov8YBtPS7vR");
10 | const SWITCHBOARD_AGGREGATORS = [LP_ORCA_SOL_USDC_SWITCHBOARD_AGGREGATOR, SOL_USD_SWITCHBOARD_AGGREGATOR];
11 |
12 | const main = async () => {
13 | const connection = new Connection("https://api.devnet.solana.com");
14 |
15 | const wallet = Wallet.local();
16 | const provider = new anchor.AnchorProvider(connection, wallet, {
17 | commitment: "confirmed",
18 | });
19 |
20 | const program = new Program(IDL, PLUGIN_PROGRAM_ID, provider);
21 |
22 | const rateData = anchor.web3.Keypair.generate();
23 | const tx = await program.methods
24 | .initialize()
25 | .accounts({
26 | signer: provider.wallet.publicKey,
27 | rateData: rateData.publicKey,
28 | })
29 | .remainingAccounts(SWITCHBOARD_AGGREGATORS.map((c) => ({ pubkey: c, isSigner: false, isWritable: false })))
30 | .signers([rateData])
31 | .rpc();
32 |
33 | console.log("tx: " + tx);
34 | console.log("rate plugin state: " + rateData.publicKey);
35 | };
36 |
37 | main();
38 |
--------------------------------------------------------------------------------
/scripts/rate_switchboard/read_aggregator.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { Program, Wallet } from "@project-serum/anchor";
3 | import { Connection, Keypair, PublicKey } from "@solana/web3.js";
4 | import { AggregatorAccount, loadSwitchboardProgram } from "@switchboard-xyz/switchboard-v2";
5 | import { RustDecimalWrapper } from "@vyper-protocol/rust-decimal-wrapper";
6 | import { IDL } from "../../target/types/rate_switchboard";
7 |
8 | import * as fs from "fs";
9 |
10 | const AGGREGATOR = new PublicKey("GvDMxPzN1sCj7L26YDK2HnMRXEQmQ2aemov8YBtPS7vR");
11 |
12 | const main = async () => {
13 | const connection = new Connection("https://api.devnet.solana.com");
14 |
15 | const program = await loadSwitchboardProgram("devnet", connection);
16 |
17 | const aggregatorAccount = new AggregatorAccount({
18 | program,
19 | publicKey: AGGREGATOR,
20 | });
21 |
22 | const history = await aggregatorAccount.loadHistory();
23 |
24 | history.forEach((c) =>
25 | fs.appendFileSync("./out.csv", `${new Date(c.timestamp.toNumber() * 1000)};${c.value.toNumber()}\n`)
26 | );
27 | };
28 |
29 | main();
30 |
--------------------------------------------------------------------------------
/scripts/rate_switchboard/read_state.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { Program, Wallet } from "@project-serum/anchor";
3 | import { Connection, Keypair, PublicKey } from "@solana/web3.js";
4 | import { RustDecimalWrapper } from "@vyper-protocol/rust-decimal-wrapper";
5 | import { IDL } from "../../target/types/rate_switchboard";
6 |
7 | const PLUGIN_PROGRAM_ID = new PublicKey("2hGXiH1oEQwjCXRx8bNdHTi49ScZp7Mj2bxcjxtULKe1");
8 | const PLUGIN_STATE = new PublicKey("5iz3MJ8cnRcXmzBDgNmb65HPwfeiw4djUfEaLqKuBA41");
9 |
10 | const main = async () => {
11 | const connection = new Connection("https://api.devnet.solana.com");
12 |
13 | const wallet = Wallet.local();
14 | const provider = new anchor.AnchorProvider(connection, wallet, {
15 | commitment: "confirmed",
16 | });
17 | const program = new Program(IDL, PLUGIN_PROGRAM_ID, provider);
18 | const account = await program.account.rateState.fetch(PLUGIN_STATE);
19 |
20 | console.log("account: ", account);
21 | account.fairValue.forEach((c, i) => {
22 | console.log(`fairValue #${i}: ` + new RustDecimalWrapper(new Uint8Array(c)).toNumber());
23 | });
24 | console.log("refreshedSlot: " + account.refreshedSlot.toNumber());
25 | console.log(
26 | "aggregators: " +
27 | (account.switchboardAggregators as (null | PublicKey)[]).filter((c) => c != null).map((c) => c.toBase58())
28 | );
29 | };
30 |
31 | main();
32 |
--------------------------------------------------------------------------------
/scripts/rate_switchboard/refresh.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { Program, Wallet } from "@project-serum/anchor";
3 | import { Connection, Keypair, PublicKey } from "@solana/web3.js";
4 | import { IDL } from "../../target/types/rate_switchboard";
5 |
6 | const PLUGIN_PROGRAM_ID = new PublicKey("2hGXiH1oEQwjCXRx8bNdHTi49ScZp7Mj2bxcjxtULKe1");
7 | const PLUGIN_STATE = new PublicKey("5iz3MJ8cnRcXmzBDgNmb65HPwfeiw4djUfEaLqKuBA41");
8 |
9 | const main = async () => {
10 | const connection = new Connection("https://api.devnet.solana.com");
11 |
12 | const wallet = Wallet.local();
13 | const provider = new anchor.AnchorProvider(connection, wallet, {
14 | commitment: "confirmed",
15 | });
16 | const program = new Program(IDL, PLUGIN_PROGRAM_ID, provider);
17 | const account = await program.account.rateState.fetch(PLUGIN_STATE);
18 |
19 | const sig = await program.methods
20 | .refresh()
21 | .accounts({
22 | rateData: PLUGIN_STATE,
23 | })
24 | .remainingAccounts(
25 | (account.switchboardAggregators as (null | PublicKey)[])
26 | .filter((c) => c != null)
27 | .map((c) => ({ pubkey: c, isSigner: false, isWritable: false }))
28 | )
29 | .rpc();
30 | console.log("sig: ", sig);
31 | };
32 |
33 | main();
34 |
--------------------------------------------------------------------------------
/scripts/readme.md:
--------------------------------------------------------------------------------
1 | # Vyper scripts
2 |
3 | 1. read the script you want to run
4 | 2. understand it
5 | 3. launch with:
6 | - `ANCHOR_PROVIDER_URL=https://api.mainnet-beta.solana.com ANCHOR_WALLET=~/.config/solana/id.json ts-node -T ./scripts/rate_mock/create.ts`
7 | - `ANCHOR_PROVIDER_URL=https://api.mainnet-beta.solana.com ANCHOR_WALLET=~/Dev/VyperWallets/vyper-program-authority/authority.json ts-node -T ./scripts/rate_mock/create.ts`
8 |
--------------------------------------------------------------------------------
/scripts/redeem_logic_farming/create.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { Program, Wallet } from "@project-serum/anchor";
3 | import { Connection, Keypair, PublicKey } from "@solana/web3.js";
4 | import { IDL } from "../../target/types/redeem_logic_farming";
5 |
6 | const PLUGIN_PROGRAM_ID = new PublicKey("Fd87TGcYmWs1Gfa7XXZycJwt9kXjRs8axMtxCWtCmowN");
7 |
8 | const main = async () => {
9 | const provider = anchor.AnchorProvider.env();
10 |
11 | const program = new Program(IDL, PLUGIN_PROGRAM_ID, provider);
12 |
13 | const stateAccount = anchor.web3.Keypair.generate();
14 | const interestSplit = 0.5;
15 | const capLow = 0.8;
16 | const capHigh = 1.2;
17 |
18 | const tx = await program.methods
19 | .initialize(interestSplit, capLow, capHigh)
20 | .accounts({
21 | redeemLogicConfig: stateAccount.publicKey,
22 | owner: new PublicKey("6zoqN77QehDFPanib6WfBRcYnh31QSBdbL64Aj9Eq2fM"),
23 | payer: provider.wallet.publicKey,
24 | })
25 | .signers([stateAccount])
26 | .rpc();
27 |
28 | console.log("tx: " + tx);
29 | console.log("redeem logic farming plugin state: " + stateAccount.publicKey);
30 | };
31 |
32 | main();
33 |
--------------------------------------------------------------------------------
/scripts/redeem_logic_farming/read_state.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { Program, Wallet } from "@project-serum/anchor";
3 | import { Connection, PublicKey } from "@solana/web3.js";
4 | import { IDL } from "../../target/types/redeem_logic_farming";
5 |
6 | const PLUGIN_PROGRAM_ID = new PublicKey("Fd87TGcYmWs1Gfa7XXZycJwt9kXjRs8axMtxCWtCmowN");
7 | const PLUGIN_STATE = new PublicKey("AjEifDpSYF57BGfJSnZ9qb9k3WNNknY1CJ8KeRoCwwU");
8 |
9 | const main = async () => {
10 | const connection = new Connection("https://api.devnet.solana.com");
11 |
12 | const wallet = Wallet.local();
13 | const provider = new anchor.AnchorProvider(connection, wallet, {
14 | commitment: "confirmed",
15 | });
16 |
17 | const program = new Program(IDL, PLUGIN_PROGRAM_ID, provider);
18 | const account = await program.account.redeemLogicConfig.fetch(PLUGIN_STATE);
19 |
20 | console.log("account: ", account);
21 | };
22 |
23 | main();
24 |
--------------------------------------------------------------------------------
/scripts/redeem_logic_farming/update.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { Program, Wallet } from "@project-serum/anchor";
3 | import { Connection, Keypair, PublicKey } from "@solana/web3.js";
4 | import { IDL } from "../../target/types/redeem_logic_farming";
5 |
6 | const PLUGIN_PROGRAM_ID = new PublicKey("Fd87TGcYmWs1Gfa7XXZycJwt9kXjRs8axMtxCWtCmowN");
7 | const PLUGIN_STATE = new PublicKey("E5X5QbyUUNyPzJyobyRpmfEpP17mevGj9Nu8jKMsXhMb");
8 |
9 | const main = async () => {
10 | const connection = new Connection("https://api.devnet.solana.com");
11 |
12 | const wallet = Wallet.local();
13 | const provider = new anchor.AnchorProvider(connection, wallet, {
14 | commitment: "confirmed",
15 | });
16 |
17 | const program = new Program(IDL, PLUGIN_PROGRAM_ID, provider);
18 |
19 | const interestSplit = 0;
20 | const capLow = 0.3;
21 | const capHigh = 0.3;
22 |
23 | const tx = await program.methods
24 | .update(interestSplit, capLow, capHigh)
25 | .accounts({
26 | redeemLogicConfig: PLUGIN_STATE,
27 | owner: provider.wallet.publicKey,
28 | })
29 | .rpc();
30 |
31 | console.log("tx: " + tx);
32 | };
33 |
34 | main();
35 |
--------------------------------------------------------------------------------
/scripts/redeem_logic_lending_fee/create.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { Program, Wallet } from "@project-serum/anchor";
3 | import { Connection, Keypair, PublicKey } from "@solana/web3.js";
4 | import { RedeemLogicLendingFee, IDL } from "../../target/types/redeem_logic_lending_fee";
5 |
6 | const PLUGIN_PROGRAM_ID = new PublicKey("3mq416it8YJsd5DKNuWeoCCAH8GYJfpuefHSNkSP6LyS");
7 |
8 | const main = async () => {
9 | const connection = new Connection("https://api.devnet.solana.com");
10 |
11 | const wallet = Wallet.local();
12 | const provider = new anchor.AnchorProvider(connection, wallet, {
13 | commitment: "confirmed",
14 | });
15 |
16 | const program = new Program(IDL, PLUGIN_PROGRAM_ID, provider);
17 |
18 | const stateAccount = anchor.web3.Keypair.generate();
19 | const interestSplit = 5000;
20 | const mgmtFeeBps = 100; // 1%
21 | const perfFeeBps = 10_000; // 100%
22 |
23 | const tx = await program.methods
24 | .initialize(interestSplit, mgmtFeeBps, perfFeeBps)
25 | .accounts({
26 | redeemLogicConfig: stateAccount.publicKey,
27 | owner: new PublicKey("6zoqN77QehDFPanib6WfBRcYnh31QSBdbL64Aj9Eq2fM"),
28 | payer: provider.wallet.publicKey,
29 | })
30 | .signers([stateAccount])
31 | .rpc();
32 |
33 | console.log("tx: " + tx);
34 | console.log("redeem logic lending plugin state: " + stateAccount.publicKey);
35 | };
36 |
37 | main();
38 |
--------------------------------------------------------------------------------
/scripts/redeem_logic_lending_fee/read_state.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { Program, Wallet } from "@project-serum/anchor";
3 | import { Connection, Keypair, PublicKey } from "@solana/web3.js";
4 | import { RedeemLogicLendingFee, IDL } from "../../target/types/redeem_logic_lending_fee";
5 |
6 | const PLUGIN_PROGRAM_ID = new PublicKey("3mq416it8YJsd5DKNuWeoCCAH8GYJfpuefHSNkSP6LyS");
7 | const PLUGIN_STATE = new PublicKey("ES8pE5xKpyKxwSryJ1sPqWh4agfAhbQThFntLfi9JXez");
8 |
9 | const main = async () => {
10 | const connection = new Connection("https://api.devnet.solana.com");
11 |
12 | const wallet = Wallet.local();
13 | const provider = new anchor.AnchorProvider(connection, wallet, {
14 | commitment: "confirmed",
15 | });
16 |
17 | const program = new Program(IDL, PLUGIN_PROGRAM_ID, provider);
18 | const account = await program.account.redeemLogicConfig.fetch(PLUGIN_STATE);
19 |
20 | console.log("account: ", account);
21 | };
22 |
23 | main();
24 |
--------------------------------------------------------------------------------
/scripts/redeem_logic_vanilla_option/create.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { Program, Wallet } from "@project-serum/anchor";
3 | import { Connection, Keypair, PublicKey } from "@solana/web3.js";
4 | import { IDL } from "../../target/types/redeem_logic_farming";
5 |
6 | const PLUGIN_PROGRAM_ID = new PublicKey("8fSeRtFseNrjdf8quE2YELhuzLkHV7WEGRPA9Jz8xEVe");
7 |
8 | const main = async () => {
9 | const connection = new Connection("https://api.devnet.solana.com");
10 |
11 | const wallet = Wallet.local();
12 | const provider = new anchor.AnchorProvider(connection, wallet, {
13 | commitment: "confirmed",
14 | });
15 |
16 | const program = new Program(IDL, PLUGIN_PROGRAM_ID, provider);
17 |
18 | const stateAccount = anchor.web3.Keypair.generate();
19 | const interestSplit = 5000;
20 |
21 | const tx = await program.methods
22 | .initialize(interestSplit)
23 | .accounts({
24 | redeemLogicConfig: stateAccount.publicKey,
25 | owner: new PublicKey("6zoqN77QehDFPanib6WfBRcYnh31QSBdbL64Aj9Eq2fM"),
26 | payer: provider.wallet.publicKey,
27 | })
28 | .signers([stateAccount])
29 | .rpc();
30 |
31 | console.log("tx: " + tx);
32 | console.log("redeem logic farming plugin state: " + stateAccount.publicKey);
33 | };
34 |
35 | main();
36 |
--------------------------------------------------------------------------------
/scripts/redeem_logic_vanilla_option/read_state.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { Program, Wallet } from "@project-serum/anchor";
3 | import { Connection, PublicKey } from "@solana/web3.js";
4 | import { IDL } from "../../target/types/redeem_logic_farming";
5 |
6 | const PLUGIN_PROGRAM_ID = new PublicKey("8fSeRtFseNrjdf8quE2YELhuzLkHV7WEGRPA9Jz8xEVe");
7 | const PLUGIN_STATE = new PublicKey("xx");
8 |
9 | const main = async () => {
10 | const connection = new Connection("https://api.devnet.solana.com");
11 |
12 | const wallet = Wallet.local();
13 | const provider = new anchor.AnchorProvider(connection, wallet, {
14 | commitment: "confirmed",
15 | });
16 |
17 | const program = new Program(IDL, PLUGIN_PROGRAM_ID, provider);
18 | const account = await program.account.redeemLogicConfig.fetch(PLUGIN_STATE);
19 |
20 | console.log("account: ", account);
21 | };
22 |
23 | main();
24 |
--------------------------------------------------------------------------------
/sdk/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | es2021: true,
5 | node: true,
6 | },
7 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
8 | parser: '@typescript-eslint/parser',
9 | parserOptions: {
10 | ecmaVersion: 'latest',
11 | sourceType: 'module',
12 | },
13 | plugins: ['@typescript-eslint'],
14 | rules: {},
15 | };
--------------------------------------------------------------------------------
/sdk/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | lib
3 | yarn.lock
4 | tsconfig.tsbuildinfo
5 | wallet.json
6 | .env
--------------------------------------------------------------------------------
/sdk/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sdk",
3 | "version": "1.0.0",
4 | "license": "MIT",
5 | "main": "./lib/index.js",
6 | "typings": "./lib/index.d.ts",
7 | "files": [
8 | "package.json",
9 | "lib"
10 | ],
11 | "directories": {
12 | "lib": "lib"
13 | },
14 | "scripts": {
15 | "build": "tsc",
16 | "watch": "tsc -w",
17 | "start": "ts-node src/index.ts",
18 | "lint": "eslint -c .eslintrc.js src/**/*.ts",
19 | "prettify": "prettier -w ./src",
20 | "prettify-check": "prettier -c ./src",
21 | "precommit": "yarn prettify && yarn lint",
22 | "test": "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
23 | },
24 | "devDependencies": {
25 | "@types/mocha": "^9.1.1",
26 | "chai": "^4.3.6",
27 | "mocha": "^10.0.0",
28 | "ts-mocha": "^10.0.0",
29 | "ts-node": "^10.8.1",
30 | "typescript": "^4.7.3"
31 | },
32 | "dependencies": {
33 | "@project-serum/anchor": "^0.25.0-beta.1",
34 | "@project-serum/common": "^0.0.1-beta.3",
35 | "@solana/spl-token": "^0.2.0",
36 | "dotenv": "^16.0.1"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/sdk/src/HaltFlags.ts:
--------------------------------------------------------------------------------
1 | export enum HaltFlags {
2 | NONE = 0,
3 |
4 | // Disable deposits
5 | HALT_DEPOSITS = 1 << 0,
6 |
7 | // Disable refreshes
8 | HALT_REFRESHES = 1 << 1,
9 |
10 | // Disable redeems
11 | HALT_REDEEMS = 1 << 2,
12 |
13 | // Disable all operations
14 | HALT_ALL = HALT_DEPOSITS | HALT_REFRESHES | HALT_REDEEMS
15 | }
--------------------------------------------------------------------------------
/sdk/src/LastUpdate.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from '@project-serum/anchor'
2 | export class LastUpdate {
3 | slot: number;
4 | padding: number[];
5 |
6 | constructor(slot: anchor.BN, padding: number[]) {
7 | this.slot = slot.toNumber();
8 | this.padding = padding;
9 | }
10 | }
--------------------------------------------------------------------------------
/sdk/src/OwnerRestrictedIxFlags.ts:
--------------------------------------------------------------------------------
1 | export enum OwnerRestrictedIxFlags {
2 | NONE = 0,
3 |
4 | DEPOSITS = 1 << 0,
5 |
6 | // Owner restricted: Refreshes
7 | REFRESHES = 1 << 1,
8 |
9 | // Owner restricted: Redeems
10 | REDEEMS = 1 << 2,
11 |
12 | // Disable all operations
13 | ALL = DEPOSITS | REFRESHES | REDEEMS
14 | }
--------------------------------------------------------------------------------
/sdk/src/ReserveFairValue.ts:
--------------------------------------------------------------------------------
1 | import { SlotTracking } from "./SlotTracking";
2 |
3 | export class ReserveFairValue {
4 | value: number;
5 | slotTracking: SlotTracking;
6 |
7 | constructor(value: number, slotTracking: SlotTracking) {
8 | this.value = value;
9 | this.slotTracking = slotTracking
10 | }
11 | }
--------------------------------------------------------------------------------
/sdk/src/SlotTracking.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from '@project-serum/anchor';
2 | import { LastUpdate } from "./LastUpdate";
3 |
4 | export class SlotTracking {
5 | lastUpdate: LastUpdate;
6 | staleSlotThreshold: number;
7 |
8 | constructor(lastUpdate: LastUpdate, staleSlotThreshold: anchor.BN) {
9 | this.lastUpdate = lastUpdate
10 | this.staleSlotThreshold = staleSlotThreshold.toNumber();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/sdk/src/TrancheConfig.ts:
--------------------------------------------------------------------------------
1 | import { PublicKey } from "@solana/web3.js";
2 | import { TrancheData } from "./TrancheData";
3 |
4 | export class TrancheConfig {
5 | reserveMint: PublicKey;
6 | reserve: PublicKey;
7 | trancheData: TrancheData;
8 | seniorTrancheMint: PublicKey;
9 | juniorTrancheMint: PublicKey;
10 | trancheAuthority: PublicKey;
11 | authoritySeed: PublicKey;
12 | authorityBump: number[];
13 | owner: PublicKey;
14 | rateProgram: PublicKey;
15 | rateProgramState: PublicKey;
16 | redeemLogicProgram: PublicKey;
17 | redeemLogicProgramState: PublicKey;
18 | version: number[];
19 | createdAt: number;
20 |
21 | constructor(
22 | reserveMint: PublicKey,
23 | reserve: PublicKey,
24 | trancheData: TrancheData,
25 | seniorTrancheMint: PublicKey,
26 | juniorTrancheMint: PublicKey,
27 | trancheAuthority: PublicKey,
28 | authoritySeed: PublicKey,
29 | authorityBump: number[],
30 | owner: PublicKey,
31 | rateProgram: PublicKey,
32 | rateProgramState: PublicKey,
33 | redeemLogicProgram: PublicKey,
34 | redeemLogicProgramState: PublicKey,
35 | version: number[],
36 | createdAt: number,
37 | ) {
38 | this.reserveMint = reserveMint;
39 | this.reserve = reserve;
40 | this.trancheData = trancheData;
41 | this.seniorTrancheMint = seniorTrancheMint;
42 | this.juniorTrancheMint = juniorTrancheMint;
43 | this.trancheAuthority = trancheAuthority;
44 | this.authoritySeed = authoritySeed;
45 | this.authorityBump = authorityBump;
46 | this.owner = owner;
47 | this.rateProgram = rateProgram;
48 | this.rateProgramState = rateProgramState;
49 | this.redeemLogicProgram = redeemLogicProgram;
50 | this.redeemLogicProgramState = redeemLogicProgramState;
51 | this.version = version;
52 | this.createdAt = createdAt;
53 | }
54 |
55 | }
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/sdk/src/TrancheData.ts:
--------------------------------------------------------------------------------
1 | import { ReserveFairValue } from "./ReserveFairValue";
2 | import { TrancheFairValue } from "./TrancheFairValue";
3 | import { HaltFlags } from "./HaltFlags";
4 | import { OwnerRestrictedIxFlags } from "./OwnerRestrictedIxFlags";
5 |
6 | export class TrancheData {
7 | depositedQuantity: number[];
8 | feeToCollectQuantity: number;
9 | reserveFairValue: ReserveFairValue;
10 | trancheFairValue: TrancheFairValue;
11 | haltFlags: HaltFlags;
12 | ownerRestrictedIx: OwnerRestrictedIxFlags;
13 |
14 | constructor(
15 | depositedQuantity: number[],
16 | feeToCollectQuantity: number,
17 | reserveFairValue: ReserveFairValue,
18 | trancheFairValue: TrancheFairValue,
19 | ownerRestrictedIx: OwnerRestrictedIxFlags,
20 | haltFlags: HaltFlags,
21 | ) {
22 |
23 | this.depositedQuantity = depositedQuantity;
24 | this.feeToCollectQuantity = feeToCollectQuantity;
25 | this.reserveFairValue = reserveFairValue;
26 | this.trancheFairValue = trancheFairValue;
27 | this.haltFlags = haltFlags;
28 | this.ownerRestrictedIx = ownerRestrictedIx;
29 | }
30 | }
--------------------------------------------------------------------------------
/sdk/src/TrancheFairValue.ts:
--------------------------------------------------------------------------------
1 | import { SlotTracking } from "./SlotTracking";
2 |
3 | export class TrancheFairValue {
4 | value: number[];
5 | slotTracking: SlotTracking;
6 |
7 | constructor(value: number[], slotTracking: SlotTracking) {
8 | this.value = value;
9 | this.slotTracking = slotTracking;
10 | }
11 | }
--------------------------------------------------------------------------------
/sdk/src/TrancheInitData.ts:
--------------------------------------------------------------------------------
1 | export type InitializationData = {
2 | trancheMintDecimals: number;
3 | haltFlags: number;
4 | ownerRestrictedIxs: number;
5 | };
--------------------------------------------------------------------------------
/sdk/src/UpdateTrancheConfigFlags.ts:
--------------------------------------------------------------------------------
1 | export enum UpdateTrancheConfigFlags {
2 | HALT_FLAGS = 1 << 0,
3 | OWNER_RESTRICTED_IXS = 1 << 1,
4 | RESERVE_FAIR_VALUE_STALE_SLOT_THRESHOLD = 1 << 2,
5 | TRANCHE_FAIR_VALUE_STALE_SLOT_THRESHOLD = 1 << 3,
6 | };
--------------------------------------------------------------------------------
/sdk/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Vyper';
2 |
3 |
--------------------------------------------------------------------------------
/sdk/src/plugins/ratePlugin/IRatePlugin.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { PublicKey } from "@solana/web3.js";
3 | import { RateMock } from "../../../../../target/types/rate_mock";
4 | import { RateState } from "./rateMock/RateState";
5 |
6 | export interface IRatePlugin {
7 | program: anchor.Program;
8 | provider: anchor.AnchorProvider;
9 | rateStateId: PublicKey;
10 | getProgramId(): PublicKey;
11 | getRatePluginState(rateStateId?: PublicKey): Promise;
12 | initialize(): Promise;
13 | getRefreshIX(): Promise
14 | }
--------------------------------------------------------------------------------
/sdk/src/plugins/ratePlugin/rateMock/Rate.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { PublicKey } from "@solana/web3.js";
3 | import { RateMock } from "../../../../../target/types/rate_mock";
4 | import idlRateMock from "../../../../../target/idl/rate_mock.json";
5 | import { RateState } from "./RateState";
6 | import { IRatePlugin } from "../IRatePlugin";
7 |
8 | export class RatePlugin implements IRatePlugin {
9 |
10 | program: anchor.Program;
11 | provider: anchor.AnchorProvider;
12 | rateStateId: PublicKey;
13 |
14 | getProgramId(): PublicKey {
15 | return this.program.programId;
16 | }
17 |
18 | static create(provider: anchor.AnchorProvider, ratePluginId: PublicKey): RatePlugin {
19 | const client = new RatePlugin();
20 | const program = new anchor.Program(idlRateMock as any, ratePluginId, provider) as anchor.Program;
21 | client.program = program;
22 | client.provider = provider;
23 | return client;
24 | }
25 |
26 | async getRatePluginState(rateStateId?: PublicKey) {
27 |
28 | if (!rateStateId) {
29 | rateStateId = this.rateStateId;
30 | }
31 | const ratePluginState = await this.program.account.rateState.fetch(rateStateId);
32 | const rateState = new RateState(
33 | ratePluginState.fairValue,
34 | ratePluginState.refreshedSlot.toNumber(),
35 | )
36 | return rateState;
37 | }
38 |
39 | async setFairValue(fairValue: number) {
40 | await this.program.methods
41 | .setFairValue(fairValue)
42 | .accounts({
43 | rateData: this.rateStateId,
44 | signer: this.provider.wallet.publicKey,
45 | })
46 | .rpc();
47 | }
48 |
49 | async getSetFairValueIX(fairValue: number): Promise {
50 | return await this.program.methods
51 | .setFairValue(fairValue)
52 | .accounts({
53 | rateData: this.rateStateId,
54 | signer: this.provider.wallet.publicKey,
55 | })
56 | .instruction();
57 | }
58 |
59 | async getRefreshIX(): Promise {
60 | return await this.program.methods
61 | .refresh()
62 | .accounts({
63 | rateData: this.rateStateId,
64 | signer: this.provider.wallet.publicKey,
65 | })
66 | .instruction();
67 | }
68 |
69 | async initialize() {
70 | const rateState = anchor.web3.Keypair.generate();
71 | await this.program.methods
72 | .initialize()
73 | .accounts({
74 | rateData: rateState.publicKey,
75 | authority: this.provider.wallet.publicKey,
76 | signer: this.provider.wallet.publicKey,
77 | })
78 | .signers([rateState])
79 | .rpc();
80 | this.rateStateId = rateState.publicKey;
81 | }
82 |
83 |
84 | }
--------------------------------------------------------------------------------
/sdk/src/plugins/ratePlugin/rateMock/RateState.ts:
--------------------------------------------------------------------------------
1 | export class RateState {
2 | fairValue: number[];
3 | refreshedSlot: number;
4 |
5 | constructor(fairValue: number[], refreshedSlot: number) {
6 | this.fairValue = fairValue;
7 | this.refreshedSlot = refreshedSlot;
8 | }
9 | }
--------------------------------------------------------------------------------
/sdk/src/plugins/redeemLogicPlugin/IReedeemLogicPlugin.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { PublicKey } from "@solana/web3.js";
3 | import { RedeemLogicLending } from "../../../../target/types/redeem_logic_lending";
4 | import { RedeemLogicVanillaOption } from "../../../../target/types/redeem_logic_vanilla_option";
5 | import { RedeemLogicLendingFee } from "../../../../target/types/redeem_logic_lending_fee";
6 | import { RedeemLogicFarming } from "../../../../target/types/redeem_logic_farming";
7 | import { RedeemLogicLendingState } from "./redeemLogicLending/RedeemLogicLendingState";
8 | import { RedeemLogicVanillaOptionState } from "./redeemLogicVanillaOption/RedeemLogicVanillaOptionState";
9 | import { RedeemLogicLendingFeeState } from "./redeemLogicLendingFee/RedeemLogicLendingFeeState";
10 | import { RedeemLogicFarmingState } from "./redeemLogicFarming/RedeemLogicFarmingState";
11 |
12 | export interface IRedeemLogicPlugin {
13 | program: anchor.Program | anchor.Program | anchor.Program | anchor.Program,
14 | provider: anchor.Provider;
15 | redeemLogicStateId: PublicKey;
16 |
17 | getProgramId(): PublicKey;
18 | getRedeemLogicState(redeemLogicStateId?: PublicKey): Promise;
19 | initialize(...args): Promise;
20 | }
21 |
--------------------------------------------------------------------------------
/sdk/src/plugins/redeemLogicPlugin/redeemLogicFarming/RedeemLogicFarming.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { PublicKey } from "@solana/web3.js";
3 | import { RedeemLogicFarming } from "../../../../../target/types/redeem_logic_farming";
4 | import idlRedeemLogicFarming from "../../../../../target/idl/redeem_logic_farming.json";
5 | import { RedeemLogicFarmingState } from "./RedeemLogicFarmingState";
6 | import { IRedeemLogicPlugin } from "../IReedeemLogicPlugin";
7 |
8 | export class RedeemLogicFarmingPlugin implements IRedeemLogicPlugin {
9 |
10 | program: anchor.Program;
11 | provider: anchor.AnchorProvider;
12 | redeemLogicStateId: PublicKey;
13 |
14 | getProgramId(): PublicKey {
15 | return this.program.programId;
16 | }
17 |
18 | static create(provider: anchor.AnchorProvider, redeemLogicStateId: PublicKey): RedeemLogicFarmingPlugin {
19 | const client = new RedeemLogicFarmingPlugin();
20 | const program = new anchor.Program(idlRedeemLogicFarming as any, redeemLogicStateId, provider) as anchor.Program;
21 | client.program = program;
22 | client.provider = provider;
23 | return client;
24 | }
25 |
26 | async getRedeemLogicState(redeemLogicStateId?: PublicKey) {
27 |
28 | if (!redeemLogicStateId) {
29 | redeemLogicStateId = this.redeemLogicStateId;
30 | }
31 |
32 | const redeemLogicLendingState = await this.program.account.redeemLogicConfig.fetch(redeemLogicStateId);
33 | const redeemLogicState = new RedeemLogicFarmingState(
34 | redeemLogicLendingState.interestSplit,
35 | redeemLogicLendingState.owner
36 | )
37 | return redeemLogicState;
38 | }
39 |
40 | async initialize(interestSplit: number) {
41 | const redeemLogicState = anchor.web3.Keypair.generate();
42 | await this.program.methods
43 | .initialize(interestSplit)
44 | .accounts({
45 | redeemLogicConfig: redeemLogicState.publicKey,
46 | owner: this.provider.wallet.publicKey,
47 | payer: this.provider.wallet.publicKey,
48 | })
49 | .signers([redeemLogicState])
50 | .rpc();
51 | this.redeemLogicStateId = redeemLogicState.publicKey;
52 | }
53 |
54 | }
--------------------------------------------------------------------------------
/sdk/src/plugins/redeemLogicPlugin/redeemLogicFarming/RedeemLogicFarmingState.ts:
--------------------------------------------------------------------------------
1 | import { PublicKey } from "@solana/web3.js";
2 |
3 | export class RedeemLogicFarmingState {
4 | interestSplit: number;
5 | owner: PublicKey
6 |
7 | constructor(interestSplit: number, owner: PublicKey) {
8 | this.interestSplit = interestSplit;
9 | this.owner = owner;
10 | }
11 | }
--------------------------------------------------------------------------------
/sdk/src/plugins/redeemLogicPlugin/redeemLogicLending/RedeemLogicLending.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { PublicKey } from "@solana/web3.js";
3 | import { RedeemLogicLending } from "../../../../../target/types/redeem_logic_lending";
4 | import idlRedeemLogicLending from "../../../../../target/idl/redeem_logic_lending.json";
5 | import { RedeemLogicLendingState } from "./RedeemLogicLendingState";
6 | import { IRedeemLogicPlugin } from "../IReedeemLogicPlugin";
7 |
8 | export class RedeemLogicLendingPlugin implements IRedeemLogicPlugin {
9 |
10 | program: anchor.Program;
11 | provider: anchor.AnchorProvider;
12 | redeemLogicStateId: PublicKey;
13 |
14 | getProgramId(): PublicKey {
15 | return this.program.programId;
16 | }
17 |
18 | static create(provider: anchor.AnchorProvider, redeemLogicStateId: PublicKey): RedeemLogicLendingPlugin {
19 | const client = new RedeemLogicLendingPlugin();
20 | const program = new anchor.Program(idlRedeemLogicLending as any, redeemLogicStateId, provider) as anchor.Program;
21 | client.program = program;
22 | client.provider = provider;
23 | return client;
24 | }
25 |
26 | async getRedeemLogicState(redeemLogicStateId?: PublicKey) {
27 |
28 | if (!redeemLogicStateId) {
29 | redeemLogicStateId = this.redeemLogicStateId;
30 | }
31 |
32 | const redeemLogicLendingState = await this.program.account.redeemLogicConfig.fetch(redeemLogicStateId);
33 | const redeemLogicState = new RedeemLogicLendingState(
34 | redeemLogicLendingState.interestSplit,
35 | redeemLogicLendingState.fixedFeePerTranche.toNumber(),
36 | redeemLogicLendingState.owner
37 | )
38 | return redeemLogicState;
39 | }
40 |
41 | async initialize(interestSplit: number, fixedFeePerTranche: number = 0) {
42 | const redeemLogicState = anchor.web3.Keypair.generate();
43 | await this.program.methods
44 | .initialize(interestSplit, new anchor.BN(fixedFeePerTranche))
45 | .accounts({
46 | redeemLogicConfig: redeemLogicState.publicKey,
47 | owner: this.provider.wallet.publicKey,
48 | payer: this.provider.wallet.publicKey,
49 | })
50 | .signers([redeemLogicState])
51 | .rpc();
52 | this.redeemLogicStateId = redeemLogicState.publicKey;
53 | }
54 |
55 | }
--------------------------------------------------------------------------------
/sdk/src/plugins/redeemLogicPlugin/redeemLogicLending/RedeemLogicLendingState.ts:
--------------------------------------------------------------------------------
1 | import { PublicKey } from "@solana/web3.js";
2 |
3 | export class RedeemLogicLendingState {
4 | interestSplit: number;
5 | fixedFeePerTranche: number;
6 | owner: PublicKey
7 |
8 | constructor(interestSplit: number, fixedFeePerTranche: number, owner: PublicKey) {
9 | this.interestSplit = interestSplit;
10 | this.fixedFeePerTranche = fixedFeePerTranche;
11 | this.owner = owner;
12 | }
13 | }
--------------------------------------------------------------------------------
/sdk/src/plugins/redeemLogicPlugin/redeemLogicLendingFee/RedeemLogicLendingFee.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { PublicKey } from "@solana/web3.js";
3 | import { RedeemLogicLendingFee } from "../../../../../target/types/redeem_logic_lending_fee";
4 | import idlRedeemLogicLendingFee from "../../../../../target/idl/redeem_logic_lending_fee.json";
5 | import { RedeemLogicLendingFeeState } from "./RedeemLogicLendingFeeState";
6 | import { IRedeemLogicPlugin } from "../IReedeemLogicPlugin";
7 |
8 | export class RedeemLogicLendingFeePlugin implements IRedeemLogicPlugin {
9 |
10 | program: anchor.Program;
11 | provider: anchor.AnchorProvider;
12 | redeemLogicStateId: PublicKey;
13 |
14 | getProgramId(): PublicKey {
15 | return this.program.programId;
16 | }
17 |
18 | static create(provider: anchor.AnchorProvider, redeemLogicStateId: PublicKey): RedeemLogicLendingFeePlugin {
19 | const client = new RedeemLogicLendingFeePlugin();
20 | const program = new anchor.Program(idlRedeemLogicLendingFee as any, redeemLogicStateId, provider) as anchor.Program;
21 | client.program = program;
22 | client.provider = provider;
23 | return client;
24 | }
25 |
26 | async getRedeemLogicState(redeemLogicStateId?: PublicKey) {
27 |
28 | if (!redeemLogicStateId) {
29 | redeemLogicStateId = this.redeemLogicStateId;
30 | }
31 |
32 | const redeemLogicLendingState = await this.program.account.redeemLogicConfig.fetch(redeemLogicStateId);
33 | const redeemLogicState = new RedeemLogicLendingFeeState(
34 | redeemLogicLendingState.interestSplit,
35 | redeemLogicLendingState.mgmtFee,
36 | redeemLogicLendingState.perfFee,
37 | redeemLogicLendingState.owner
38 | )
39 | return redeemLogicState;
40 | }
41 |
42 | async initialize(interestSplit: number, mgmtFee: number, perfFee: number) {
43 | const redeemLogicState = anchor.web3.Keypair.generate();
44 | await this.program.methods
45 | .initialize(interestSplit,mgmtFee,perfFee)
46 | .accounts({
47 | redeemLogicConfig: redeemLogicState.publicKey,
48 | owner: this.provider.wallet.publicKey,
49 | payer: this.provider.wallet.publicKey,
50 | })
51 | .signers([redeemLogicState])
52 | .rpc();
53 | this.redeemLogicStateId = redeemLogicState.publicKey;
54 | }
55 |
56 | }
--------------------------------------------------------------------------------
/sdk/src/plugins/redeemLogicPlugin/redeemLogicLendingFee/RedeemLogicLendingFeeState.ts:
--------------------------------------------------------------------------------
1 | import { PublicKey } from "@solana/web3.js";
2 |
3 | export class RedeemLogicLendingFeeState {
4 | interestSplit: number;
5 | mgmtFee: number;
6 | perfFee: number;
7 | owner: PublicKey;
8 |
9 |
10 | constructor(interestSplit: number, mgmtFee: number,perfFee: number, owner: PublicKey) {
11 | this.interestSplit = interestSplit;
12 | this.mgmtFee = mgmtFee;
13 | this.perfFee = perfFee;
14 | this.owner = owner;
15 | }
16 | }
--------------------------------------------------------------------------------
/sdk/src/plugins/redeemLogicPlugin/redeemLogicVanillaOption/RedeemLogicVanillaOption.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { PublicKey } from "@solana/web3.js";
3 | import { RedeemLogicVanillaOption } from "../../../../../target/types/redeem_logic_vanilla_option";
4 | import idlRedeemLogicVanilla from "../../../../../target/idl/redeem_logic_vanilla_option.json";
5 | import { RedeemLogicVanillaOptionState } from "./RedeemLogicVanillaOptionState";
6 | import { IRedeemLogicPlugin } from "../IReedeemLogicPlugin";
7 |
8 | export class RedeemLogicVanillaOptionPlugin implements IRedeemLogicPlugin {
9 |
10 | program: anchor.Program;
11 | provider: anchor.AnchorProvider;
12 | redeemLogicStateId: PublicKey;
13 |
14 | getProgramId(): PublicKey {
15 | return this.program.programId;
16 | }
17 |
18 | static create(provider: anchor.AnchorProvider, redeemLogicStateId: PublicKey): RedeemLogicVanillaOptionPlugin {
19 | const client = new RedeemLogicVanillaOptionPlugin();
20 | const program = new anchor.Program(idlRedeemLogicVanilla as any, redeemLogicStateId, provider) as anchor.Program;
21 | client.program = program;
22 | client.provider = provider;
23 | return client;
24 | }
25 |
26 |
27 | async getRedeemLogicState(redeemLogicStateId?: PublicKey) {
28 |
29 | if (!redeemLogicStateId) {
30 | redeemLogicStateId = this.redeemLogicStateId;
31 | }
32 |
33 | const redeemVanillaState = await this.program.account.redeemLogicConfig.fetch(redeemLogicStateId);
34 | const redeemLogicState = new RedeemLogicVanillaOptionState(
35 | redeemVanillaState.isCall,
36 | redeemVanillaState.isLinear,
37 | redeemVanillaState.strike,
38 | redeemVanillaState.owner
39 | )
40 | return redeemLogicState;
41 | }
42 |
43 | async initialize(isCall: boolean, isLinear: boolean, strike: number) {
44 | const redeemVanillaState = anchor.web3.Keypair.generate();
45 | await this.program.methods
46 | .initialize(strike,isCall,isLinear)
47 | .accounts({
48 | redeemLogicConfig: redeemVanillaState.publicKey,
49 | owner: this.provider.wallet.publicKey,
50 | payer: this.provider.wallet.publicKey,
51 | })
52 | .signers([redeemVanillaState])
53 | .rpc();
54 | this.redeemLogicStateId = redeemVanillaState.publicKey;
55 | }
56 |
57 | }
--------------------------------------------------------------------------------
/sdk/src/plugins/redeemLogicPlugin/redeemLogicVanillaOption/RedeemLogicVanillaOptionState.ts:
--------------------------------------------------------------------------------
1 | import { PublicKey } from "@solana/web3.js";
2 |
3 | export class RedeemLogicVanillaOptionState {
4 |
5 | isCall: boolean
6 | isLinear: boolean
7 | strike: number
8 | owner: PublicKey
9 |
10 | constructor(isCall: boolean, isLinear: boolean,strike: number, owner: PublicKey) {
11 | this.isCall = isCall
12 | this.isLinear = isLinear
13 | this.strike = strike
14 | this.owner = owner
15 | }
16 | }
--------------------------------------------------------------------------------
/sdk/tests/RateMockPlugin.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { PublicKey, } from "@solana/web3.js";
3 | import * as dotenv from 'dotenv';
4 | import { assert, expect} from "chai";
5 | import { RatePlugin } from '../src/plugins/ratePlugin/rateMock/Rate';
6 |
7 | dotenv.config();
8 |
9 | const rateMockPluginId = new PublicKey('FB7HErqohbgaVV21BRiiMTuiBpeUYT8Yw7Z6EdEL7FAG');
10 |
11 | describe('Rate Mock Plugin', () => {
12 |
13 | const provider = anchor.AnchorProvider.env();
14 | const rateMockPlugin = RatePlugin.create(provider, rateMockPluginId);
15 |
16 | it("initialize", async () => {
17 | await rateMockPlugin.initialize();
18 | const rateState = await rateMockPlugin.getRatePluginState();
19 | expect(rateState.fairValue).to.eql(Array(10).fill(0));
20 | });
21 |
22 | it('fetch existing rate mock state', async () => {
23 | await rateMockPlugin.initialize();
24 | const rateState = await rateMockPlugin.getRatePluginState();
25 |
26 | assert.ok(rateState.fairValue != undefined);
27 | assert.ok(rateState.refreshedSlot != undefined);
28 | })
29 |
30 | it('set mock rate', async () => {
31 | await rateMockPlugin.initialize();
32 | let rateState = await rateMockPlugin.getRatePluginState();
33 |
34 | // with direct rpc call
35 | await rateMockPlugin.setFairValue(1500);
36 | rateState = await rateMockPlugin.getRatePluginState();
37 | expect(rateState.fairValue[0]).to.eq(1500);
38 |
39 | // with setFairValueIX
40 | const tx = new anchor.web3.Transaction();
41 | const instruction = await rateMockPlugin.getSetFairValueIX(2500);
42 | tx.add(instruction);
43 | await provider.sendAndConfirm(tx);
44 |
45 | rateState = await rateMockPlugin.getRatePluginState();
46 | expect(rateState.fairValue[0]).to.eq(2500);
47 | })
48 |
49 | });
--------------------------------------------------------------------------------
/sdk/tests/RedeemLendingPlugin.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from '@project-serum/anchor'
2 | import { AnchorProvider } from '@project-serum/anchor';
3 | import { PublicKey, } from "@solana/web3.js";
4 | import * as dotenv from 'dotenv';
5 | import { expect } from "chai";
6 | import { RedeemLogicLendingPlugin } from '../src/plugins/redeemLogicPlugin/redeemLogicLending/RedeemLogicLending';
7 |
8 | dotenv.config();
9 |
10 | const redeemLogicLendingPluginId = new PublicKey('Gc2ZKNuCpdNKhAzEGS2G9rBSiz4z8MULuC3M3t8EqdWA');
11 |
12 | describe('Redeem Logic Lending Plugin', () => {
13 |
14 | const provider = AnchorProvider.env();
15 | const redeemLogicLendingPlugin = RedeemLogicLendingPlugin.create(provider,redeemLogicLendingPluginId);
16 |
17 | it('initialize', async () => {
18 | const interestSplit = 5000;
19 | await redeemLogicLendingPlugin.initialize(interestSplit);
20 | const redeemState = await redeemLogicLendingPlugin.getRedeemLogicState();
21 |
22 | expect(redeemState.interestSplit).to.eql(interestSplit);
23 | expect(redeemState.owner.toBase58()).to.eq(provider.wallet.publicKey.toBase58());
24 | })
25 |
26 |
27 | it('fetch existing redeem logic configuration', async () => {
28 | const interestSplit = 5000;
29 | await redeemLogicLendingPlugin.initialize(interestSplit);
30 | const redeemState = await redeemLogicLendingPlugin.getRedeemLogicState();
31 |
32 | expect(redeemState.interestSplit != undefined);
33 | expect(redeemState.fixedFeePerTranche != undefined);
34 | expect(redeemState.owner.toBase58()).to.eq(provider.wallet.publicKey.toBase58())
35 | })
36 |
37 |
38 |
39 | });
--------------------------------------------------------------------------------
/sdk/tests/RedeemLogicFarmingPlugin.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from '@project-serum/anchor'
2 | import { AnchorProvider } from '@project-serum/anchor';
3 | import { PublicKey, } from "@solana/web3.js";
4 | import * as dotenv from 'dotenv';
5 | import { expect } from "chai";
6 | import { RedeemLogicFarmingPlugin } from '../src/plugins/redeemLogicPlugin/redeemLogicFarming/RedeemLogicFarming';
7 |
8 | dotenv.config();
9 |
10 | const redeemLogicFarmingPluginId = new PublicKey('Fd87TGcYmWs1Gfa7XXZycJwt9kXjRs8axMtxCWtCmowN');
11 |
12 | describe('Redeem Logic Farming Plugin', () => {
13 |
14 | const provider = AnchorProvider.env();
15 | const redeemLogicFarmingPlugin = RedeemLogicFarmingPlugin.create(provider,redeemLogicFarmingPluginId);
16 |
17 | it('initialize and fetch existing redeem logic configuration', async () => {
18 | const interestSplit = 5000;
19 |
20 | await redeemLogicFarmingPlugin.initialize(interestSplit);
21 | const redeemState = await redeemLogicFarmingPlugin.getRedeemLogicState();
22 |
23 | expect(redeemState.interestSplit).to.eql(interestSplit);
24 | expect(redeemState.owner.toBase58()).to.eq(provider.wallet.publicKey.toBase58());
25 | })
26 | });
--------------------------------------------------------------------------------
/sdk/tests/RedeemLogicLendingFee.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from '@project-serum/anchor'
2 | import { AnchorProvider } from '@project-serum/anchor';
3 | import { PublicKey, } from "@solana/web3.js";
4 | import * as dotenv from 'dotenv';
5 | import { expect } from "chai";
6 | import { RedeemLogicLendingFeePlugin } from '../src/plugins/redeemLogicPlugin/redeemLogicLendingFee/RedeemLogicLendingFee';
7 |
8 | dotenv.config();
9 |
10 | const redeemLogicLendingPluginId = new PublicKey('3mq416it8YJsd5DKNuWeoCCAH8GYJfpuefHSNkSP6LyS');
11 |
12 | describe('Redeem Logic Lending Fee Plugin', () => {
13 |
14 | const provider = AnchorProvider.env();
15 | const redeemLogicLendingPlugin = RedeemLogicLendingFeePlugin.create(provider,redeemLogicLendingPluginId);
16 |
17 | it('initialize and fetch existing redeem logic configuration', async () => {
18 | const interestSplit = 5000;
19 | const mgmtFee = 5;
20 | const perfFee = 5;
21 | await redeemLogicLendingPlugin.initialize(interestSplit,mgmtFee,perfFee);
22 | const redeemState = await redeemLogicLendingPlugin.getRedeemLogicState();
23 |
24 | expect(redeemState.interestSplit).to.eql(interestSplit);
25 | expect(redeemState.mgmtFee).to.eql(mgmtFee);
26 | expect(redeemState.perfFee).to.eql(perfFee);
27 | expect(redeemState.owner.toBase58()).to.eq(provider.wallet.publicKey.toBase58());
28 | })
29 | });
--------------------------------------------------------------------------------
/sdk/tests/RedeemVanillaOptionPlugin.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from '@project-serum/anchor'
2 | import { AnchorProvider } from '@project-serum/anchor';
3 | import { PublicKey, } from "@solana/web3.js";
4 | import * as dotenv from 'dotenv';
5 | import { expect } from "chai";
6 | import { RedeemLogicVanillaOptionPlugin } from '../src/plugins/redeemLogicPlugin/redeemLogicVanillaOption/RedeemLogicVanillaOption';
7 |
8 | dotenv.config();
9 |
10 | const redeemLogicVanillaPluginId = new PublicKey('8fSeRtFseNrjdf8quE2YELhuzLkHV7WEGRPA9Jz8xEVe');
11 |
12 | describe('Redeem Logic Vanilla Option Plugin', () => {
13 |
14 | const provider = AnchorProvider.env();
15 | const redeemLogicVanillaPlugin = RedeemLogicVanillaOptionPlugin.create(provider,redeemLogicVanillaPluginId);
16 |
17 | it('initialize and fetch the redeem logic vanilla configuration', async () => {
18 | const isCall = true;
19 | const isLinear = true;
20 | const strike = 2;
21 |
22 | await redeemLogicVanillaPlugin.initialize(isCall,isLinear,strike);
23 | const redeemState = await redeemLogicVanillaPlugin.getRedeemLogicState();
24 |
25 | expect(redeemState.isCall).to.eql(isCall);
26 | expect(redeemState.isLinear).to.eql(isLinear);
27 | expect(redeemState.strike).to.eql(strike);
28 | expect(redeemState.owner.toBase58()).to.eq(provider.wallet.publicKey.toBase58());
29 |
30 | })
31 | });
--------------------------------------------------------------------------------
/sdk/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": [
4 | "es2015"
5 | ],
6 | "module": "commonjs",
7 | "target": "es6",
8 | "outDir": "lib",
9 | "rootDir": "src",
10 | "esModuleInterop": true,
11 | "declaration": true,
12 | "sourceMap": true,
13 | "resolveJsonModule": true,
14 | "skipLibCheck": true
15 | },
16 | "include": [
17 | "./src/**/*.ts",
18 | "./src/**/*.json",
19 | ],
20 | }
--------------------------------------------------------------------------------
/tests/rate-mock.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { Program } from "@project-serum/anchor";
3 | import { assert, expect } from "chai";
4 | import { RateMock } from "../target/types/rate_mock";
5 | import { bn } from "./utils";
6 |
7 | describe("rate_mock", () => {
8 | const provider = anchor.AnchorProvider.env();
9 |
10 | // Configure the client to use the local cluster.
11 | anchor.setProvider(provider);
12 | const programRateMock = anchor.workspace.RateMock as Program;
13 |
14 | it("initialize", async () => {
15 | const rateData = anchor.web3.Keypair.generate();
16 |
17 | await programRateMock.methods
18 | .initialize()
19 | .accounts({
20 | signer: provider.wallet.publicKey,
21 | authority: provider.wallet.publicKey,
22 | rateData: rateData.publicKey,
23 | })
24 | .signers([rateData])
25 | .rpc();
26 |
27 | expect((await programRateMock.account.rateState.fetch(rateData.publicKey)).authority.toBase58()).to.be.eql(
28 | provider.wallet.publicKey.toBase58()
29 | );
30 | expect((await programRateMock.account.rateState.fetch(rateData.publicKey)).refreshedSlot.toNumber()).to.be.gte(
31 | 0
32 | );
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/tests/rate-poolv2.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { Program } from "@project-serum/anchor";
3 | import { createMintToInstruction } from "@solana/spl-token";
4 | import { Keypair, Transaction } from "@solana/web3.js";
5 | import { RustDecimalWrapper } from "@vyper-protocol/rust-decimal-wrapper";
6 | import { expect } from "chai";
7 | import { RatePoolv2 } from "../target/types/rate_poolv2";
8 | import { createMint, createTokenAccount } from "./utils";
9 |
10 | describe("rate_poolv2", () => {
11 | const provider = anchor.AnchorProvider.env();
12 |
13 | // Configure the client to use the local cluster.
14 | anchor.setProvider(provider);
15 | const program = anchor.workspace.RatePoolv2 as Program;
16 |
17 | it("initialize", async () => {
18 | const baseMint = 9;
19 | const quoteDecimals = 6;
20 | const lpDecimals = 6;
21 | const baseAmount = 74_792 * 10 ** baseMint;
22 | const quoteAmount = 2_394_354 * 10 ** quoteDecimals;
23 | const lpAmount = 4_142_365 * 10 ** lpDecimals;
24 | const poolConfig = await createPoolConfig(
25 | provider,
26 | baseAmount,
27 | quoteAmount,
28 | lpAmount,
29 | baseMint,
30 | quoteDecimals,
31 | lpDecimals
32 | );
33 |
34 | const rateData = anchor.web3.Keypair.generate();
35 | const sig = await program.methods
36 | .initialize()
37 | .accounts({
38 | signer: provider.wallet.publicKey,
39 | rateData: rateData.publicKey,
40 | pool: poolConfig.poolId.publicKey,
41 | lpMint: poolConfig.mints.lpMint,
42 | baseMint: poolConfig.mints.baseMint,
43 | quoteMint: poolConfig.mints.quoteMint,
44 | baseTokenAccount: poolConfig.ata.baseATA,
45 | quoteTokenAccount: poolConfig.ata.quoteATA,
46 | })
47 | .signers([rateData])
48 | .rpc();
49 | console.log("init sig: " + sig);
50 |
51 | const rateDataAccount = await program.account.rateState.fetch(rateData.publicKey);
52 | expect(rateDataAccount.baseTokenAccount).to.be.eql(poolConfig.ata.baseATA);
53 | expect(rateDataAccount.quoteTokenAccount).to.be.eql(poolConfig.ata.quoteATA);
54 | expect(rateDataAccount.lpMint).to.be.eql(poolConfig.mints.lpMint);
55 | expect(rateDataAccount.refreshedSlot.toNumber()).to.be.gt(0);
56 | expect(new RustDecimalWrapper(new Uint8Array(rateDataAccount.fairValue[0])).toNumber()).to.be.closeTo(
57 | 1.1560323631,
58 | 0.000000001
59 | );
60 | expect(new RustDecimalWrapper(new Uint8Array(rateDataAccount.fairValue[1])).toNumber()).to.be.closeTo(
61 | 32.013504118,
62 | 0.00000001
63 | );
64 | });
65 |
66 | it("refresh", async () => {
67 | const baseMint = 9;
68 | const quoteDecimals = 6;
69 | const lpDecimals = 6;
70 | const baseAmount = 10 * 10 ** baseMint;
71 | const quoteAmount = 10 * 10 ** quoteDecimals;
72 | const lpAmount = 100 * 10 ** lpDecimals;
73 | const poolConfig = await createPoolConfig(
74 | provider,
75 | baseAmount,
76 | quoteAmount,
77 | lpAmount,
78 | baseMint,
79 | quoteDecimals,
80 | lpDecimals
81 | );
82 |
83 | const rateData = anchor.web3.Keypair.generate();
84 | await program.methods
85 | .initialize()
86 | .accounts({
87 | signer: provider.wallet.publicKey,
88 | pool: poolConfig.poolId.publicKey,
89 | rateData: rateData.publicKey,
90 | lpMint: poolConfig.mints.lpMint,
91 | baseMint: poolConfig.mints.baseMint,
92 | quoteMint: poolConfig.mints.quoteMint,
93 | baseTokenAccount: poolConfig.ata.baseATA,
94 | quoteTokenAccount: poolConfig.ata.quoteATA,
95 | })
96 | .signers([rateData])
97 | .rpc();
98 |
99 | let rateDataAccount = await program.account.rateState.fetch(rateData.publicKey);
100 | const initialLpPrice = new RustDecimalWrapper(new Uint8Array(rateDataAccount.fairValue[0])).toNumber();
101 | // mint some new lp tokens
102 | const tx = new Transaction();
103 | tx.add(
104 | createMintToInstruction(
105 | poolConfig.mints.lpMint,
106 | poolConfig.ata.lpATA,
107 | poolConfig.poolId.publicKey,
108 | lpAmount,
109 | [poolConfig.poolId]
110 | )
111 | );
112 | await provider.sendAndConfirm(tx, [poolConfig.poolId]);
113 |
114 | // refresh prices
115 | const refreshSig = await program.methods
116 | .refresh()
117 | .accounts({
118 | rateData: rateData.publicKey,
119 | lpMint: poolConfig.mints.lpMint,
120 | baseMint: poolConfig.mints.baseMint,
121 | quoteMint: poolConfig.mints.quoteMint,
122 | baseTokenAccount: poolConfig.ata.baseATA,
123 | quoteTokenAccount: poolConfig.ata.quoteATA,
124 | })
125 | .rpc();
126 | console.log("refreshSig: ", refreshSig);
127 |
128 | rateDataAccount = await program.account.rateState.fetch(rateData.publicKey);
129 |
130 | const finalLpPrice = new RustDecimalWrapper(new Uint8Array(rateDataAccount.fairValue[0])).toNumber();
131 | expect(finalLpPrice).to.be.eq(initialLpPrice / 2);
132 | });
133 | });
134 |
135 | async function createPoolConfig(
136 | provider: anchor.AnchorProvider,
137 | baseAmount: number,
138 | quoteAmount: number,
139 | lpAmount: number,
140 | baseDecimals: number,
141 | quoteDecimals: number,
142 | lpDecimals: number
143 | ) {
144 | const mintAuthority = anchor.web3.Keypair.generate();
145 | const poolId = anchor.web3.Keypair.generate();
146 |
147 | const baseMint = await createMint(provider, baseDecimals, mintAuthority.publicKey);
148 | const quoteMint = await createMint(provider, quoteDecimals, mintAuthority.publicKey);
149 | const lpMint = await createMint(provider, lpDecimals, poolId.publicKey);
150 |
151 | const baseATA = await createTokenAccount(provider, baseMint, poolId.publicKey);
152 | const quoteATA = await createTokenAccount(provider, quoteMint, poolId.publicKey);
153 | const lpATA = await createTokenAccount(provider, lpMint, Keypair.generate().publicKey);
154 |
155 | // mint some tokens
156 | const tx = new Transaction();
157 | tx.add(createMintToInstruction(baseMint, baseATA, mintAuthority.publicKey, baseAmount, [mintAuthority]));
158 | tx.add(createMintToInstruction(quoteMint, quoteATA, mintAuthority.publicKey, quoteAmount, [mintAuthority]));
159 | tx.add(createMintToInstruction(lpMint, lpATA, poolId.publicKey, lpAmount, [poolId]));
160 | await provider.sendAndConfirm(tx, [mintAuthority, poolId]);
161 |
162 | return {
163 | poolId,
164 | mints: {
165 | baseMint,
166 | quoteMint,
167 | lpMint,
168 | },
169 | ata: {
170 | baseATA,
171 | quoteATA,
172 | lpATA,
173 | },
174 | };
175 | }
176 |
--------------------------------------------------------------------------------
/tests/rate-pyth.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { Program } from "@project-serum/anchor";
3 | import { PublicKey, SYSVAR_EPOCH_SCHEDULE_PUBKEY } from "@solana/web3.js";
4 | import { RustDecimalWrapper } from "@vyper-protocol/rust-decimal-wrapper";
5 | import { assert, expect } from "chai";
6 | import { RatePyth } from "../target/types/rate_pyth";
7 | import { RateSwitchboard } from "../target/types/rate_switchboard";
8 | import { bn } from "./utils";
9 |
10 | const BTC_USD_PYTH_FEED = new PublicKey("GVXRSBjFk6e6J3NbVPXohDJetcTjaeeuykUpbQF8UoMU");
11 | const SOL_USD_PYTH_FEED = new PublicKey("H6ARHf6YXhGYeQfUzQNGk6rDNnLBQKrenN712K4AQJEG");
12 |
13 | const PYTH_ORACLES = [BTC_USD_PYTH_FEED, SOL_USD_PYTH_FEED];
14 |
15 | describe("rate_pyth", () => {
16 | const provider = anchor.AnchorProvider.env();
17 |
18 | // Configure the client to use the local cluster.
19 | anchor.setProvider(provider);
20 | const program = anchor.workspace.RatePyth as Program;
21 |
22 | it("initialize", async () => {
23 | const rateData = anchor.web3.Keypair.generate();
24 |
25 | await program.methods
26 | .initialize()
27 | .accounts({
28 | signer: provider.wallet.publicKey,
29 | rateData: rateData.publicKey,
30 | })
31 | .remainingAccounts(PYTH_ORACLES.map((c) => ({ pubkey: c, isSigner: false, isWritable: false })))
32 | .signers([rateData])
33 | .rpc();
34 |
35 | const rateDataAccount = await program.account.rateState.fetch(rateData.publicKey);
36 |
37 | for (let i = 0; i < 10; i++) {
38 | if (i < PYTH_ORACLES.length) {
39 | expect(rateDataAccount.pythOracles[i].toBase58()).to.eql(PYTH_ORACLES[i].toBase58());
40 | expect(new RustDecimalWrapper(new Uint8Array(rateDataAccount.fairValue[i])).toNumber()).to.be.not.eq(0);
41 | } else {
42 | expect(rateDataAccount.pythOracles[i]).to.be.null;
43 | expect(new RustDecimalWrapper(new Uint8Array(rateDataAccount.fairValue[i])).toNumber()).to.be.eq(0);
44 | }
45 | }
46 |
47 | expect(rateDataAccount.refreshedSlot.toNumber()).to.be.gt(0);
48 | });
49 |
50 | it("refresh", async () => {
51 | const rateData = anchor.web3.Keypair.generate();
52 |
53 | await program.methods
54 | .initialize()
55 | .accounts({
56 | signer: provider.wallet.publicKey,
57 | rateData: rateData.publicKey,
58 | })
59 | .remainingAccounts(PYTH_ORACLES.map((c) => ({ pubkey: c, isSigner: false, isWritable: false })))
60 | .signers([rateData])
61 | .rpc();
62 |
63 | const oldSlot = (await program.account.rateState.fetch(rateData.publicKey)).refreshedSlot.toNumber();
64 |
65 | await program.methods
66 | .refresh()
67 | .accounts({
68 | rateData: rateData.publicKey,
69 | })
70 | .remainingAccounts(PYTH_ORACLES.map((c) => ({ pubkey: c, isSigner: false, isWritable: false })))
71 | .rpc();
72 |
73 | expect((await program.account.rateState.fetchNullable(rateData.publicKey)).refreshedSlot.toNumber()).to.be.gt(
74 | oldSlot
75 | );
76 | });
77 |
78 | it("cannot change aggregators order", async () => {
79 | const rateData = anchor.web3.Keypair.generate();
80 |
81 | await program.methods
82 | .initialize()
83 | .accounts({
84 | signer: provider.wallet.publicKey,
85 | rateData: rateData.publicKey,
86 | })
87 | .remainingAccounts(PYTH_ORACLES.map((c) => ({ pubkey: c, isSigner: false, isWritable: false })))
88 | .signers([rateData])
89 | .rpc();
90 |
91 | try {
92 | await program.methods
93 | .refresh()
94 | .accounts({
95 | rateData: rateData.publicKey,
96 | })
97 | .remainingAccounts(
98 | PYTH_ORACLES.reverse().map((c) => ({ pubkey: c, isSigner: false, isWritable: false }))
99 | )
100 | .rpc();
101 | expect(true).to.be.eq(false);
102 | } catch (err) {
103 | expect(err.error.errorCode.code).to.be.eql("RequireKeysEqViolated");
104 | }
105 | });
106 |
107 | it("cannot provide less aggregators", async () => {
108 | const rateData = anchor.web3.Keypair.generate();
109 |
110 | await program.methods
111 | .initialize()
112 | .accounts({
113 | signer: provider.wallet.publicKey,
114 | rateData: rateData.publicKey,
115 | })
116 | .remainingAccounts(PYTH_ORACLES.map((c) => ({ pubkey: c, isSigner: false, isWritable: false })))
117 | .signers([rateData])
118 | .rpc();
119 |
120 | try {
121 | await program.methods
122 | .refresh()
123 | .accounts({
124 | rateData: rateData.publicKey,
125 | })
126 | .remainingAccounts(
127 | [PYTH_ORACLES[0]].map((c) => ({
128 | pubkey: c,
129 | isSigner: false,
130 | isWritable: false,
131 | }))
132 | )
133 | .rpc();
134 | expect(true).to.be.eq(false);
135 | } catch (err) {
136 | expect(err.error.errorCode.code).to.be.eql("InvalidAggregatorsNumber");
137 | }
138 | });
139 | });
140 |
--------------------------------------------------------------------------------
/tests/rate-switchboard.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { Program } from "@project-serum/anchor";
3 | import { PublicKey } from "@solana/web3.js";
4 | import { assert, expect } from "chai";
5 | import { RateSwitchboard } from "../target/types/rate_switchboard";
6 | import { bn } from "./utils";
7 |
8 | const BTC_USD_SWITCHBOARD_AGGREGATOR = new PublicKey("8SXvChNYFhRq4EZuZvnhjrB3jJRQCv4k3P4W6hesH3Ee");
9 | const USDC_USD_SWITCHBOARD_AGGREGATOR = new PublicKey("BjUgj6YCnFBZ49wF54ddBVA9qu8TeqkFtkbqmZcee8uW");
10 | const XTZ_USD_SWITCHBOARD_AGGREGATOR = new PublicKey("F11LACseaLXuRaPSvnD6w15vSPHtx73YaGZv9293rQQm");
11 | const SWITCHBOARD_AGGREGATORS = [
12 | BTC_USD_SWITCHBOARD_AGGREGATOR,
13 | USDC_USD_SWITCHBOARD_AGGREGATOR,
14 | XTZ_USD_SWITCHBOARD_AGGREGATOR,
15 | ];
16 |
17 | describe("rate_switchboard", async () => {
18 | const provider = anchor.AnchorProvider.env();
19 |
20 | // Configure the client to use the local cluster.
21 | anchor.setProvider(provider);
22 | const program = anchor.workspace.RateSwitchboard as Program;
23 |
24 | it("initialize", async () => {
25 | const rateData = anchor.web3.Keypair.generate();
26 |
27 | await program.methods
28 | .initialize()
29 | .accounts({
30 | signer: provider.wallet.publicKey,
31 | rateData: rateData.publicKey,
32 | })
33 | .remainingAccounts(SWITCHBOARD_AGGREGATORS.map((c) => ({ pubkey: c, isSigner: false, isWritable: false })))
34 | .signers([rateData])
35 | .rpc();
36 |
37 | const rateDataAccount = await program.account.rateState.fetchNullable(rateData.publicKey);
38 |
39 | for (let i = 0; i < 10; i++) {
40 | if (i < SWITCHBOARD_AGGREGATORS.length) {
41 | expect(rateDataAccount.switchboardAggregators[i].toBase58()).to.eql(
42 | SWITCHBOARD_AGGREGATORS[i].toBase58()
43 | );
44 | // expect(rateDataAccount.fairValue[i]).to.be.not.eq(0);
45 | } else {
46 | expect(rateDataAccount.switchboardAggregators[i]).to.be.null;
47 | // expect(rateDataAccount.fairValue[i]).to.be.eq(0);
48 | }
49 | }
50 |
51 | expect(rateDataAccount.refreshedSlot.toNumber()).to.be.gt(0);
52 | });
53 |
54 | it("refresh", async () => {
55 | const rateData = anchor.web3.Keypair.generate();
56 |
57 | await program.methods
58 | .initialize()
59 | .accounts({
60 | signer: provider.wallet.publicKey,
61 | rateData: rateData.publicKey,
62 | })
63 | .remainingAccounts(SWITCHBOARD_AGGREGATORS.map((c) => ({ pubkey: c, isSigner: false, isWritable: false })))
64 | .signers([rateData])
65 | .rpc();
66 | await program.methods
67 | .refresh()
68 | .accounts({
69 | rateData: rateData.publicKey,
70 | })
71 | .remainingAccounts(SWITCHBOARD_AGGREGATORS.map((c) => ({ pubkey: c, isSigner: false, isWritable: false })))
72 | .rpc();
73 |
74 | expect((await program.account.rateState.fetchNullable(rateData.publicKey)).refreshedSlot.toNumber()).to.be.gt(
75 | 0
76 | );
77 | });
78 |
79 | it("cannot change aggregators order", async () => {
80 | const rateData = anchor.web3.Keypair.generate();
81 |
82 | await program.methods
83 | .initialize()
84 | .accounts({
85 | signer: provider.wallet.publicKey,
86 | rateData: rateData.publicKey,
87 | })
88 | .remainingAccounts(SWITCHBOARD_AGGREGATORS.map((c) => ({ pubkey: c, isSigner: false, isWritable: false })))
89 | .signers([rateData])
90 | .rpc();
91 |
92 | try {
93 | await program.methods
94 | .refresh()
95 | .accounts({
96 | rateData: rateData.publicKey,
97 | })
98 | .remainingAccounts(
99 | SWITCHBOARD_AGGREGATORS.reverse().map((c) => ({ pubkey: c, isSigner: false, isWritable: false }))
100 | )
101 | .rpc();
102 | expect(true).to.be.eq(false);
103 | } catch (err) {
104 | expect(err.error.errorCode.code).to.be.eql("RequireKeysEqViolated");
105 | }
106 | });
107 |
108 | it("cannot provide less aggregators", async () => {
109 | const rateData = anchor.web3.Keypair.generate();
110 |
111 | await program.methods
112 | .initialize()
113 | .accounts({
114 | signer: provider.wallet.publicKey,
115 | rateData: rateData.publicKey,
116 | })
117 | .remainingAccounts(SWITCHBOARD_AGGREGATORS.map((c) => ({ pubkey: c, isSigner: false, isWritable: false })))
118 | .signers([rateData])
119 | .rpc();
120 |
121 | try {
122 | await program.methods
123 | .refresh()
124 | .accounts({
125 | rateData: rateData.publicKey,
126 | })
127 | .remainingAccounts(
128 | [SWITCHBOARD_AGGREGATORS[0], SWITCHBOARD_AGGREGATORS[1]].map((c) => ({
129 | pubkey: c,
130 | isSigner: false,
131 | isWritable: false,
132 | }))
133 | )
134 | .rpc();
135 | expect(true).to.be.eq(false);
136 | } catch (err) {
137 | expect(err.error.errorCode.code).to.be.eql("InvalidAggregatorsNumber");
138 | }
139 | });
140 | });
141 |
--------------------------------------------------------------------------------
/tests/rate-twap.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { Program } from "@project-serum/anchor";
3 | import { RustDecimalWrapper } from "@vyper-protocol/rust-decimal-wrapper";
4 | import { assert, expect } from "chai";
5 | import { RateMock } from "../target/types/rate_mock";
6 | import { RateTwap } from "../target/types/rate_twap";
7 | import { bn } from "./utils";
8 |
9 | describe("rate_twap", () => {
10 | const provider = anchor.AnchorProvider.env();
11 |
12 | // Configure the client to use the local cluster.
13 | anchor.setProvider(provider);
14 | const programRateMock = anchor.workspace.RateMock as Program;
15 | const programRateTwap = anchor.workspace.RateTwap as Program;
16 |
17 | it("initialize", async () => {
18 | const twapMinSlotDelta = 0;
19 | const twapSamplingSize = 10;
20 |
21 | const rateMockData = anchor.web3.Keypair.generate();
22 | await programRateMock.methods
23 | .initialize()
24 | .accounts({
25 | signer: provider.wallet.publicKey,
26 | authority: provider.wallet.publicKey,
27 | rateData: rateMockData.publicKey,
28 | })
29 | .signers([rateMockData])
30 | .rpc();
31 |
32 | const rateTwapData = anchor.web3.Keypair.generate();
33 | await programRateTwap.methods
34 | .initialize({
35 | minSlotDelta: bn(twapMinSlotDelta),
36 | samplingSize: twapSamplingSize,
37 | })
38 | .accounts({
39 | rateState: rateTwapData.publicKey,
40 | rateStateSource: rateMockData.publicKey,
41 | payer: provider.wallet.publicKey,
42 | })
43 | .signers([rateTwapData])
44 | .rpc();
45 |
46 | const twapAccountInfo = await programRateTwap.account.rateState.fetch(rateTwapData.publicKey);
47 | expect(twapAccountInfo.rateStateSource).to.be.eql(rateMockData.publicKey);
48 | });
49 |
50 | it("twap", async () => {
51 | const twapMinSlotDelta = 0;
52 | const twapSamplingSize = 10;
53 |
54 | const rateMockData = anchor.web3.Keypair.generate();
55 | await programRateMock.methods
56 | .initialize()
57 | .accounts({
58 | signer: provider.wallet.publicKey,
59 | authority: provider.wallet.publicKey,
60 | rateData: rateMockData.publicKey,
61 | })
62 | .signers([rateMockData])
63 | .rpc();
64 |
65 | const rateTwapData = anchor.web3.Keypair.generate();
66 | await programRateTwap.methods
67 | .initialize({
68 | minSlotDelta: bn(twapMinSlotDelta),
69 | samplingSize: twapSamplingSize,
70 | })
71 | .accounts({
72 | rateState: rateTwapData.publicKey,
73 | rateStateSource: rateMockData.publicKey,
74 | })
75 | .signers([rateTwapData])
76 | .rpc();
77 |
78 | for (let i = 0; i < twapSamplingSize; i++) {
79 | await programRateMock.methods
80 | .setFairValue(5)
81 | .accounts({
82 | authority: provider.wallet.publicKey,
83 | rateData: rateMockData.publicKey,
84 | })
85 | .rpc();
86 |
87 | await programRateTwap.methods
88 | .refresh()
89 | .accounts({
90 | rateState: rateTwapData.publicKey,
91 | rateStateSource: rateMockData.publicKey,
92 | })
93 | .rpc();
94 | }
95 |
96 | for (let i = 0; i < twapSamplingSize; i++) {
97 | await programRateMock.methods
98 | .setFairValue(50)
99 | .accounts({
100 | authority: provider.wallet.publicKey,
101 | rateData: rateMockData.publicKey,
102 | })
103 | .rpc();
104 |
105 | await programRateTwap.methods
106 | .refresh()
107 | .accounts({
108 | rateState: rateTwapData.publicKey,
109 | rateStateSource: rateMockData.publicKey,
110 | })
111 | .rpc();
112 | }
113 |
114 | const twapAccountInfo = await programRateTwap.account.rateState.fetch(rateTwapData.publicKey);
115 |
116 | expect(
117 | //@ts-ignore
118 | twapAccountInfo.fairValue.map((c) => new RustDecimalWrapper(new Uint8Array(c)).toNumber())
119 | ).to.be.eql([50, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
120 | });
121 |
122 | it("fails on refresh burst", async () => {
123 | const twapMinSlotDelta = 10;
124 | const twapSamplingSize = 10;
125 |
126 | const rateMockData = anchor.web3.Keypair.generate();
127 | await programRateMock.methods
128 | .initialize()
129 | .accounts({
130 | signer: provider.wallet.publicKey,
131 | authority: provider.wallet.publicKey,
132 | rateData: rateMockData.publicKey,
133 | })
134 | .signers([rateMockData])
135 | .rpc();
136 |
137 | const rateTwapData = anchor.web3.Keypair.generate();
138 | await programRateTwap.methods
139 | .initialize({
140 | minSlotDelta: bn(twapMinSlotDelta),
141 | samplingSize: twapSamplingSize,
142 | })
143 | .accounts({
144 | rateState: rateTwapData.publicKey,
145 | rateStateSource: rateMockData.publicKey,
146 | })
147 | .signers([rateTwapData])
148 | .rpc();
149 |
150 | try {
151 | for (let i = 0; i < twapSamplingSize; i++) {
152 | await programRateTwap.methods
153 | .refresh()
154 | .accounts({
155 | rateState: rateTwapData.publicKey,
156 | rateStateSource: rateMockData.publicKey,
157 | })
158 | .rpc();
159 | }
160 | } catch (err) {
161 | expect(err.error.errorCode.code).to.be.eql("AnotherTooRecentSample");
162 | }
163 | });
164 | });
165 |
--------------------------------------------------------------------------------
/tests/redeem-logic-lending.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { Program } from "@project-serum/anchor";
3 | import { assert, expect } from "chai";
4 | import { RedeemLogicLending } from "../target/types/redeem_logic_lending";
5 | import { bn } from "./utils";
6 |
7 | describe("redeem_logic_lending", () => {
8 | const provider = anchor.AnchorProvider.env();
9 |
10 | // Configure the client to use the local cluster.
11 | anchor.setProvider(provider);
12 | const program = anchor.workspace.RedeemLogicLending as Program;
13 |
14 | it("initialize", async () => {
15 | const redeemLogicConfig = anchor.web3.Keypair.generate();
16 |
17 | const interestSplit = 0.5;
18 | await program.methods
19 | .initialize(interestSplit, bn(0))
20 | .accounts({
21 | redeemLogicConfig: redeemLogicConfig.publicKey,
22 | owner: provider.wallet.publicKey,
23 | payer: provider.wallet.publicKey,
24 | })
25 | .signers([redeemLogicConfig])
26 | .rpc();
27 |
28 | const redeemLogicAccount = await program.account.redeemLogicConfig.fetch(redeemLogicConfig.publicKey);
29 | // expect(redeemLogicAccount.interestSplit).to.eql(interestSplit);
30 | expect(redeemLogicAccount.owner.toBase58()).to.eq(provider.wallet.publicKey.toBase58());
31 | });
32 |
33 | // it("update", async () => {
34 | // const redeemLogicConfig = anchor.web3.Keypair.generate();
35 |
36 | // const originalInterestSplit = 5000;
37 | // const newInterestSplit = 6000;
38 | // await program.methods
39 | // .initialize(originalInterestSplit, bn(0))
40 | // .accounts({
41 | // redeemLogicConfig: redeemLogicConfig.publicKey,
42 | // owner: provider.wallet.publicKey,
43 | // payer: provider.wallet.publicKey,
44 | // })
45 | // .signers([redeemLogicConfig])
46 | // .rpc();
47 | // await program.methods
48 | // .update(newInterestSplit, bn(0))
49 | // .accounts({
50 | // redeemLogicConfig: redeemLogicConfig.publicKey,
51 | // owner: provider.wallet.publicKey,
52 | // })
53 | // .rpc();
54 | // const redeemLogicAccount = await program.account.redeemLogicConfig.fetch(redeemLogicConfig.publicKey);
55 | // expect(redeemLogicAccount.interestSplit).to.eql(newInterestSplit);
56 | // });
57 |
58 | it("reject non owner update", async () => {
59 | const redeemLogicConfig = anchor.web3.Keypair.generate();
60 |
61 | const originalInterestSplit = 0.5;
62 | const newInterestSplit = 0.6;
63 |
64 | await program.methods
65 | .initialize(originalInterestSplit, bn(0))
66 | .accounts({
67 | redeemLogicConfig: redeemLogicConfig.publicKey,
68 | owner: provider.wallet.publicKey,
69 | payer: provider.wallet.publicKey,
70 | })
71 | .signers([redeemLogicConfig])
72 | .rpc();
73 |
74 | try {
75 | await program.methods
76 | .update(newInterestSplit, bn(0))
77 | .accounts({
78 | redeemLogicConfig: redeemLogicConfig.publicKey,
79 | owner: anchor.web3.Keypair.generate().publicKey,
80 | })
81 | .rpc();
82 | expect(false).to.be.true;
83 | } catch (err) {
84 | assert(true);
85 | }
86 | });
87 |
88 | // it("execute", async () => {
89 | // const redeemLogicConfig = anchor.web3.Keypair.generate();
90 | // const interestSplit = 2_000;
91 | // await program.methods
92 | // .initialize(interestSplit, bn(0))
93 | // .accounts({
94 | // redeemLogicConfig: redeemLogicConfig.publicKey,
95 | // owner: provider.wallet.publicKey,
96 | // payer: provider.wallet.publicKey,
97 | // })
98 | // .signers([redeemLogicConfig])
99 | // .rpc();
100 |
101 | // const oldQuantity = [bn(100_000), bn(100_000)];
102 | // const oldReserveFV = 6_000;
103 | // const newReserveFV = 7_500;
104 | // const tx = await program.methods
105 | // .execute({
106 | // oldQuantity: oldQuantity,
107 | // oldReserveFairValueBps: [oldReserveFV, 0, 0, 0, 0, 0, 0, 0, 0, 0],
108 | // newReserveFairValueBps: [newReserveFV, 0, 0, 0, 0, 0, 0, 0, 0, 0],
109 | // })
110 | // .accounts({
111 | // redeemLogicConfig: redeemLogicConfig.publicKey,
112 | // })
113 | // .rpc();
114 |
115 | // // TODO find a way to read the solana return value from the client
116 | // // expect((viewResult.newQuantity as anchor.BN[]).map((c) => c.toNumber())).to.be.eql([100_000, 100_000]);
117 | // // expect((viewResult.feeQuantity as anchor.BN).toNumber()).to.be.eql(0);
118 | // });
119 | });
120 |
--------------------------------------------------------------------------------
/tests/sdk/Vyper.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { PublicKey } from "@solana/web3.js";
3 | import { VyperCore } from "../../target/types/vyper_core";
4 |
5 | export type InitializationData = {
6 | trancheMintDecimals: number;
7 | haltFlags: number;
8 | ownerRestrictedIxs: number;
9 | };
10 |
11 | export class Vyper {
12 | program: anchor.Program;
13 | provider: anchor.AnchorProvider;
14 |
15 | juniorTrancheMint: PublicKey;
16 | seniorTrancheMint: PublicKey;
17 | trancheConfig: PublicKey;
18 | trancheAuthority: PublicKey;
19 | reserveMint: PublicKey;
20 | reserve: PublicKey;
21 | ratePlugin: PublicKey;
22 | ratePluginState: PublicKey;
23 | redeemLogicPlugin: PublicKey;
24 | redeemLogicPluginState: PublicKey;
25 |
26 | static create(program: anchor.Program, provider: anchor.AnchorProvider): Vyper {
27 | const client = new Vyper();
28 | client.program = program;
29 | client.provider = provider;
30 | return client;
31 | }
32 |
33 | async initialize(
34 | initData: InitializationData,
35 | reserveMint: PublicKey,
36 | ratePlugin: PublicKey,
37 | ratePluginState: PublicKey,
38 | redeemLogicPlugin: PublicKey,
39 | redeemLogicPluginState: PublicKey,
40 | owner?: PublicKey
41 | ) {
42 | const juniorTrancheMint = anchor.web3.Keypair.generate();
43 | const seniorTrancheMint = anchor.web3.Keypair.generate();
44 | const trancheConfig = anchor.web3.Keypair.generate();
45 | const [trancheAuthority] = await anchor.web3.PublicKey.findProgramAddress(
46 | [trancheConfig.publicKey.toBuffer(), anchor.utils.bytes.utf8.encode("authority")],
47 | this.program.programId
48 | );
49 | const [reserve] = await anchor.web3.PublicKey.findProgramAddress(
50 | [trancheConfig.publicKey.toBuffer(), reserveMint.toBuffer()],
51 | this.program.programId
52 | );
53 |
54 | await this.program.methods
55 | .initialize(initData)
56 | .accounts({
57 | payer: this.provider.wallet.publicKey,
58 | owner: owner ?? this.provider.wallet.publicKey,
59 | trancheConfig: trancheConfig.publicKey,
60 | trancheAuthority,
61 | rateProgram: ratePlugin,
62 | rateProgramState: ratePluginState,
63 | redeemLogicProgram: redeemLogicPlugin,
64 | redeemLogicProgramState: redeemLogicPluginState,
65 | reserveMint,
66 | reserve,
67 | juniorTrancheMint: juniorTrancheMint.publicKey,
68 | seniorTrancheMint: seniorTrancheMint.publicKey,
69 | })
70 | .signers([juniorTrancheMint, seniorTrancheMint, trancheConfig])
71 | .rpc();
72 |
73 | this.seniorTrancheMint = seniorTrancheMint.publicKey;
74 | this.juniorTrancheMint = juniorTrancheMint.publicKey;
75 | this.trancheConfig = trancheConfig.publicKey;
76 | this.trancheAuthority = trancheAuthority;
77 | this.reserveMint = reserveMint;
78 | this.reserve = reserve;
79 | this.ratePlugin = ratePlugin;
80 | this.ratePluginState = ratePluginState;
81 | this.redeemLogicPlugin = redeemLogicPlugin;
82 | this.redeemLogicPluginState = redeemLogicPluginState;
83 | }
84 |
85 | async getRefreshTrancheFairValueIX(): Promise {
86 | return await this.program.methods
87 | .refreshTrancheFairValue()
88 | .accounts({
89 | signer: this.provider.wallet.publicKey,
90 | trancheConfig: this.trancheConfig,
91 | seniorTrancheMint: this.seniorTrancheMint,
92 | juniorTrancheMint: this.juniorTrancheMint,
93 | rateProgramState: this.ratePluginState,
94 | redeemLogicProgram: this.redeemLogicPlugin,
95 | redeemLogicProgramState: this.redeemLogicPluginState,
96 | })
97 | .instruction();
98 | }
99 |
100 | async refreshTrancheFairValue() {
101 | await this.program.methods
102 | .refreshTrancheFairValue()
103 | .accounts({
104 | signer: this.provider.wallet.publicKey,
105 | trancheConfig: this.trancheConfig,
106 | seniorTrancheMint: this.seniorTrancheMint,
107 | juniorTrancheMint: this.juniorTrancheMint,
108 | rateProgramState: this.ratePluginState,
109 | redeemLogicProgram: this.redeemLogicPlugin,
110 | redeemLogicProgramState: this.redeemLogicPluginState,
111 | })
112 | .rpc();
113 | }
114 |
115 | async getDepositIx(
116 | seniorDepositAmount: number,
117 | juniorDepositAmount: number,
118 | userReserveToken: PublicKey,
119 | userSeniorTrancheTokenAccount: PublicKey,
120 | userJuniorTrancheTokenAccount: PublicKey
121 | ): Promise {
122 | return await this.program.methods
123 | .deposit({
124 | reserveQuantity: [new anchor.BN(seniorDepositAmount), new anchor.BN(juniorDepositAmount)],
125 | })
126 | .accounts({
127 | signer: this.provider.wallet.publicKey,
128 | trancheConfig: this.trancheConfig,
129 | trancheAuthority: this.trancheAuthority,
130 | reserve: this.reserve,
131 | userReserveToken,
132 | seniorTrancheMint: this.seniorTrancheMint,
133 | juniorTrancheMint: this.juniorTrancheMint,
134 | seniorTrancheDest: userSeniorTrancheTokenAccount,
135 | juniorTrancheDest: userJuniorTrancheTokenAccount,
136 | })
137 | .instruction();
138 | }
139 |
140 | async getRedeemIx(
141 | seniorDepositAmount: number,
142 | juniorDepositAmount: number,
143 | userReserveToken: anchor.web3.PublicKey,
144 | seniorTrancheTokenAccount: anchor.web3.PublicKey,
145 | juniorTrancheTokenAccount: anchor.web3.PublicKey
146 | ): Promise {
147 | return await this.program.methods
148 | .redeem({
149 | trancheQuantity: [new anchor.BN(seniorDepositAmount), new anchor.BN(juniorDepositAmount)],
150 | })
151 | .accounts({
152 | signer: this.provider.wallet.publicKey,
153 | trancheConfig: this.trancheConfig,
154 | trancheAuthority: this.trancheAuthority,
155 | reserve: this.reserve,
156 | userReserveToken,
157 | seniorTrancheMint: this.seniorTrancheMint,
158 | juniorTrancheMint: this.juniorTrancheMint,
159 | seniorTrancheSource: seniorTrancheTokenAccount,
160 | juniorTrancheSource: juniorTrancheTokenAccount,
161 | })
162 | .instruction();
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/tests/sdk/plugins/rates/RateMockPlugin.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { PublicKey } from "@solana/web3.js";
3 | import { RateMock } from "../../../../target/types/rate_mock";
4 |
5 | export class RateMockPlugin {
6 | program: anchor.Program;
7 | provider: anchor.AnchorProvider;
8 | state: PublicKey;
9 |
10 | get programID(): PublicKey {
11 | return this.program.programId;
12 | }
13 |
14 | static create(program: anchor.Program, provider: anchor.AnchorProvider): RateMockPlugin {
15 | const client = new RateMockPlugin();
16 | client.program = program;
17 | client.provider = provider;
18 | return client;
19 | }
20 |
21 | async initialize() {
22 | const rateState = anchor.web3.Keypair.generate();
23 | await this.program.methods
24 | .initialize()
25 | .accounts({
26 | rateData: rateState.publicKey,
27 | authority: this.provider.wallet.publicKey,
28 | signer: this.provider.wallet.publicKey,
29 | })
30 | .signers([rateState])
31 | .rpc();
32 | this.state = rateState.publicKey;
33 | }
34 |
35 | async setFairValue(fairValue: number) {
36 | await this.program.methods
37 | .setFairValue(fairValue)
38 | .accounts({
39 | rateData: this.state,
40 | authority: this.provider.wallet.publicKey,
41 | })
42 | .rpc();
43 | }
44 |
45 | async getSetFairValueIX(fairValue: number): Promise {
46 | return await this.program.methods
47 | .setFairValue(fairValue)
48 | .accounts({
49 | rateData: this.state,
50 | authority: this.provider.wallet.publicKey,
51 | })
52 | .instruction();
53 | }
54 |
55 | async getRefreshIX(): Promise {
56 | return await this.program.methods
57 | .refresh()
58 | .accounts({
59 | rateData: this.state,
60 | authority: this.provider.wallet.publicKey,
61 | })
62 | .instruction();
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/tests/sdk/plugins/redeemLogic/RedeemLogicLendingPlugin.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { PublicKey } from "@solana/web3.js";
3 | import { RedeemLogicLending } from "../../../../target/types/redeem_logic_lending";
4 |
5 | export class RedeemLogicLendingPlugin {
6 | program: anchor.Program;
7 | provider: anchor.AnchorProvider;
8 | state: PublicKey;
9 |
10 | get programID(): PublicKey {
11 | return this.program.programId;
12 | }
13 |
14 | static create(
15 | program: anchor.Program,
16 | provider: anchor.AnchorProvider
17 | ): RedeemLogicLendingPlugin {
18 | const client = new RedeemLogicLendingPlugin();
19 | client.program = program;
20 | client.provider = provider;
21 | return client;
22 | }
23 |
24 | async initialize(interestSplit: number, fixedFeePerTranche: number = 0) {
25 | const redeemLogicProgramState = anchor.web3.Keypair.generate();
26 | await this.program.methods
27 | .initialize(interestSplit, new anchor.BN(fixedFeePerTranche))
28 | .accounts({
29 | redeemLogicConfig: redeemLogicProgramState.publicKey,
30 | owner: this.provider.wallet.publicKey,
31 | payer: this.provider.wallet.publicKey,
32 | })
33 | .signers([redeemLogicProgramState])
34 | .rpc();
35 | this.state = redeemLogicProgramState.publicKey;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/tests/sdk/plugins/redeemLogic/RedeemLogicVanillaOptionPlugin.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import { PublicKey } from "@solana/web3.js";
3 | import { RedeemLogicVanillaOption } from "../../../../target/types/redeem_logic_vanilla_option";
4 |
5 | export class RedeemLogicVanillaOptionPlugin {
6 | program: anchor.Program;
7 | provider: anchor.AnchorProvider;
8 | state: PublicKey;
9 |
10 | get programID(): PublicKey {
11 | return this.program.programId;
12 | }
13 |
14 | static create(
15 | program: anchor.Program,
16 | provider: anchor.AnchorProvider
17 | ): RedeemLogicVanillaOptionPlugin {
18 | const client = new RedeemLogicVanillaOptionPlugin();
19 | client.program = program;
20 | client.provider = provider;
21 | return client;
22 | }
23 |
24 | async initialize(strike: number, isCall: boolean, isLinear: boolean) {
25 | const redeemLogicProgramState = anchor.web3.Keypair.generate();
26 | await this.program.methods
27 | .initialize(strike, isCall, isLinear)
28 | .accounts({
29 | redeemLogicConfig: redeemLogicProgramState.publicKey,
30 | owner: this.provider.wallet.publicKey,
31 | payer: this.provider.wallet.publicKey,
32 | })
33 | .signers([redeemLogicProgramState])
34 | .rpc();
35 | this.state = redeemLogicProgramState.publicKey;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/tests/utils.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@project-serum/anchor";
2 | import {
3 | createAccount,
4 | createAssociatedTokenAccount,
5 | createAssociatedTokenAccountInstruction,
6 | createInitializeAccountInstruction,
7 | createInitializeMintInstruction,
8 | createMintToInstruction,
9 | getAccount,
10 | getAssociatedTokenAddress,
11 | getMint,
12 | TOKEN_PROGRAM_ID,
13 | } from "@solana/spl-token";
14 |
15 | export async function createTokenAccount(
16 | provider: anchor.AnchorProvider,
17 | mint: anchor.web3.PublicKey,
18 | owner: anchor.web3.PublicKey
19 | ) {
20 | const tx = new anchor.web3.Transaction();
21 |
22 | const aToken = await getAssociatedTokenAddress(mint, owner);
23 | tx.add(createAssociatedTokenAccountInstruction(provider.wallet.publicKey, aToken, owner, mint));
24 | const signature = await provider.sendAndConfirm(tx);
25 | // console.log("createTokenAccount signature: ", signature);
26 |
27 | return aToken;
28 | }
29 |
30 | export async function createMintAndVault(provider: anchor.AnchorProvider, amount: number, decimals: number = 6) {
31 | const mint = anchor.web3.Keypair.generate();
32 | const authority = anchor.web3.Keypair.generate();
33 |
34 | const createMintIx = await createMintInstructions(provider, mint.publicKey, decimals, authority.publicKey);
35 | const aToken = await getAssociatedTokenAddress(mint.publicKey, provider.wallet.publicKey);
36 |
37 | const aTokenCreationIx = createAssociatedTokenAccountInstruction(
38 | provider.wallet.publicKey,
39 | aToken,
40 | provider.wallet.publicKey,
41 | mint.publicKey
42 | );
43 | const mintToIx = createMintToInstruction(mint.publicKey, aToken, authority.publicKey, amount);
44 |
45 | const tx = new anchor.web3.Transaction();
46 | tx.add(...createMintIx);
47 | tx.add(aTokenCreationIx);
48 | tx.add(mintToIx);
49 |
50 | const signature = await provider.sendAndConfirm(tx, [mint, authority]);
51 |
52 | return [mint.publicKey, aToken];
53 | }
54 |
55 | export async function createMint(
56 | provider: anchor.AnchorProvider,
57 | decimals: number = 6,
58 | authority?: anchor.web3.PublicKey
59 | ) {
60 | if (authority === undefined) {
61 | authority = anchor.web3.Keypair.generate().publicKey;
62 | }
63 | const mint = anchor.web3.Keypair.generate();
64 | const instructions = await createMintInstructions(provider, mint.publicKey, decimals, authority);
65 |
66 | const tx = new anchor.web3.Transaction();
67 | tx.add(...instructions);
68 |
69 | await provider.sendAndConfirm(tx, [mint]);
70 |
71 | return mint.publicKey;
72 | }
73 |
74 | export async function createMintInstructions(
75 | provider: anchor.AnchorProvider,
76 | mint: anchor.web3.PublicKey,
77 | decimals: number,
78 | authority: anchor.web3.PublicKey
79 | ) {
80 | return [
81 | anchor.web3.SystemProgram.createAccount({
82 | fromPubkey: provider.wallet.publicKey,
83 | newAccountPubkey: mint,
84 | space: 82,
85 | lamports: await provider.connection.getMinimumBalanceForRentExemption(82),
86 | programId: TOKEN_PROGRAM_ID,
87 | }),
88 | createInitializeMintInstruction(mint, decimals, authority, null),
89 | ];
90 | }
91 |
92 | export function bn(v: number): anchor.BN {
93 | return new anchor.BN(v);
94 | }
95 |
96 | export function getInitializeData(trancheMintDecimals: number) {
97 | return {
98 | trancheMintDecimals,
99 | };
100 | }
101 |
102 | export async function getTokenAccountAmount(
103 | provider: anchor.AnchorProvider,
104 | tokenAccount: anchor.web3.PublicKey
105 | ): Promise {
106 | return Number((await getAccount(provider.connection, tokenAccount, undefined, TOKEN_PROGRAM_ID)).amount);
107 | }
108 |
109 | export const UPDATE_TRANCHE_CONFIG_FLAGS = {
110 | HALT_FLAGS: 1 << 0,
111 | OWNER_RESTRICTED_IXS: 1 << 1,
112 | RESERVE_FAIR_VALUE_STALE_SLOT_THRESHOLD: 1 << 2,
113 | TRANCHE_FAIR_VALUE_STALE_SLOT_THRESHOLD: 1 << 3,
114 | DEPOSIT_CAP: 1 << 4,
115 | };
116 |
117 | export const TRANCHE_HALT_FLAGS = {
118 | NONE: 0,
119 | HALT_DEPOSITS: 1 << 0,
120 | HALT_REFRESHES: 1 << 1,
121 | HALT_REDEEMS: 1 << 2,
122 | };
123 |
124 | export const TRANCHE_HALT_FLAGS_HALT_ALL =
125 | TRANCHE_HALT_FLAGS.HALT_DEPOSITS | TRANCHE_HALT_FLAGS.HALT_REFRESHES | TRANCHE_HALT_FLAGS.HALT_REDEEMS;
126 |
127 | export const OWNER_RESTRICTED_IX_FLAGS = {
128 | NONE: 0,
129 | DEPOSITS: 1 << 0,
130 | REFRESHES: 1 << 1,
131 | REDEEMS: 1 << 2,
132 | };
133 |
134 | export const OWNER_RESTRICTED_IX_FLAGS_ALL =
135 | OWNER_RESTRICTED_IX_FLAGS.DEPOSITS | OWNER_RESTRICTED_IX_FLAGS.REDEEMS | OWNER_RESTRICTED_IX_FLAGS.REFRESHES;
136 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "types": ["node", "mocha", "chai"],
4 | "typeRoots": ["./node_modules/@types"],
5 | "lib": ["es2015"],
6 | "module": "commonjs",
7 | "target": "es6",
8 | "esModuleInterop": true
9 | }
10 | }
11 |
--------------------------------------------------------------------------------