├── .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 | Vyper Protocol 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 | --------------------------------------------------------------------------------