Skip to content
Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
eb2948f
feat: use feature flags to add "no yield" option
Oighty May 28, 2025
362b554
chore: rename program
Oighty May 28, 2025
38e8c1f
fix: state serialization
Oighty May 29, 2025
fe25de0
chore: makefile and test fixes
Oighty May 29, 2025
43454c3
chore: remove unused imports and cleanup compile warnings
Oighty May 29, 2025
5f79a5f
chore: update CI
Oighty May 29, 2025
b238990
chore: remove unnecessary idl-build flag
Oighty May 29, 2025
855e3d1
fix: compiler warning on no-yield variant
Oighty May 29, 2025
ac44e44
chore: comment clean-up
Oighty May 29, 2025
0ab5828
fix: no yield multiplier return value
Oighty May 29, 2025
0207359
test: no yield ext tests
Oighty May 29, 2025
7118905
chore: move test runner to makefile
Oighty May 29, 2025
b106d64
chore: add default feature to play nicer with anchor plugin
Oighty May 29, 2025
dd56955
test: new test harness and initial test conversion to new format
Oighty May 30, 2025
6d67f3d
feat: check that ext mint has freeze authority on initialization
Oighty May 30, 2025
585872b
refactor: cfg_if imports
Oighty May 30, 2025
678114c
fix: remove unused signer from sync
Oighty Jun 2, 2025
763d7af
test: finish refactored admin tests
Oighty Jun 2, 2025
2f1f58b
test: wrap/unwrap test refactor
Oighty Jun 2, 2025
75eb1ca
test: refactor sync unit tests
Oighty Jun 2, 2025
a4948d7
test: remove old test files and update makefile
Oighty Jun 2, 2025
1478743
test: add test case for ext mint not having freeze authority
Oighty Jun 2, 2025
d9e5c96
test: refactor multiplier calculation and add tests
Oighty Jun 3, 2025
5094a71
tests: update multiplier checks to account for floating point precisi…
Oighty Jun 3, 2025
3965c93
chore: clarify comment on test
Oighty Jun 4, 2025
de68b3b
chore: update solana-m dep to `develop` branch
Oighty Jun 9, 2025
8d3ba81
feat: use last claim index for scaledui syncs instead of m global
Oighty Jun 9, 2025
62ebb68
test: update existing tests for index change
Oighty Jun 9, 2025
2ff3f0c
test: add'l ext tests with solvency checks
Oighty Jun 10, 2025
43a4827
chore: update README
Oighty Jun 10, 2025
e6e0f45
Update README.md
Oighty Jun 10, 2025
f71a84e
chore: kurtis review clean-up
Oighty Jun 10, 2025
89ab8f6
fix
SC4RECOIN Jun 11, 2025
edc0759
test: fix tests to accompany fix
Oighty Jun 11, 2025
5487dd7
Merge pull request #15 from m0-foundation/kurtis/unwrap-exploit
Oighty Jun 11, 2025
abaf31c
PROTO-179: swap router (#12)
SC4RECOIN Jun 11, 2025
8a98536
fix: make "no-yield" the default option to fix CPI build
Oighty Jun 11, 2025
1de0096
chore: rename program_authority -> wrap_authority
Oighty Jun 11, 2025
07ee511
test: fix existing tests with tokenAuthority/wrapAuthority split
Oighty Jun 11, 2025
d6faa3d
test: wrap/unwrap with wrap authority tests
Oighty Jun 11, 2025
6e8c7bb
feat: dynamically sized wrap auth list (#17)
Oighty Jun 12, 2025
4a94237
Allow wrap authority co-signing on swaps (#16)
SC4RECOIN Jun 12, 2025
b7754ad
test: fix tests after merge
Oighty Jun 12, 2025
34ec02c
chore: readme updates
Oighty Jun 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions .github/setup/action.yaml
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
name: setup
description: 'Installing tooling and dependencies for running tests'
description: "Installing tooling and dependencies for running tests"
inputs:
node-version:
description: 'Node.js version'
description: "Node.js version"
required: false
default: '22'
default: "22"
solana-version:
description: 'Solana version'
description: "Solana version"
required: false
default: '2.1.0'
default: "2.1.0"
anchor-version:
description: 'Anchor CLI version'
description: "Anchor CLI version"
required: false
default: '0.31.1'
default: "0.31.1"
runs:
using: 'composite'
using: "composite"
steps:
- name: Setup Node
uses: actions/setup-node@v4
Expand Down Expand Up @@ -61,5 +61,5 @@ runs:
shell: bash

- name: Build programs
run: anchor build
shell: bash
run: make build-programs
shell: bash
8 changes: 4 additions & 4 deletions .github/workflows/anchor.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ concurrency:
cancel-in-progress: true

jobs:
scaled-ui-ext-tests:
runs-on: macos-latest
program-tests:
runs-on: macos-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
Expand All @@ -25,5 +25,5 @@ jobs:
uses: ./.github/setup

- name: Run tests
run: yarn run jest --preset ts-jest --verbose tests/unit/scaled_ui_ext.test.ts
shell: bash
run: yarn test
shell: bash
3 changes: 2 additions & 1 deletion Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ seeds = false
skip-lint = false

[programs.localnet]
scaled_ui_ext = "3C865D264L4NkAm78zfnDzQJJvXuU3fMjRUvRxyPi5da"
m_ext = "3C865D264L4NkAm78zfnDzQJJvXuU3fMjRUvRxyPi5da"
ext_swap = "MSwapi3WhNKMUGm9YrxGhypgUEt7wYQH3ZgG32XoWzH"

[registry]
url = "https://api.apr.dev"
Expand Down
38 changes: 24 additions & 14 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ codegen-units = 1
anchor-lang = "0.31.1"
anchor-spl = "0.31.1"
spl-token-2022 = { version = "7.0.0", features = ["no-entrypoint"] }
solana-program = "=2.1.0"
solana-security-txt = "1.1.1"
cfg-if = "1.0"
earn = { git = "https://github.com/m0-foundation/solana-m", branch = "solana-2.1.0", features = ["no-entrypoint"] }
earn = { git = "https://github.com/m0-foundation/solana-m", branch = "develop", features = ["no-entrypoint"] }

34 changes: 34 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
build-programs:
anchor build -p ext_swap
anchor build -p m_ext -- --features scaled-ui --no-default-features
@mv target/deploy/m_ext.so target/deploy/scaled_ui.so
@mv target/idl/m_ext.json target/idl/scaled_ui.json
@mv target/types/m_ext.ts target/types/scaled_ui.ts
anchor build -p m_ext -- --features no-yield --no-default-features
@cp target/deploy/m_ext.so target/deploy/no_yield.so
@cp target/idl/m_ext.json target/idl/no_yield.json
@cp target/types/m_ext.ts target/types/no_yield.ts

test-programs:
@yarn run jest --preset ts-jest --verbose tests/unit/**.test.ts
@cargo test

define update-program-id
@sed -i '' 's/declare_id!("[^"]*")/declare_id!("$(1)")/' programs/m_ext/src/lib.rs
endef

build-test-programs:
$(call update-program-id,3joDhmLtHLrSBGfeAe1xQiv3gjikes3x8S4N3o6Ld8zB)
anchor build -p m_ext
@mv target/deploy/m_ext.so tests/programs/ext_a.so
$(call update-program-id,HSMnbWEkB7sEQAGSzBPeACNUCXC9FgNeeESLnHtKfoy3)
anchor build -p m_ext
@mv target/deploy/m_ext.so tests/programs/ext_b.so
$(call update-program-id,81gYpXqg8ZT9gdkFSe35eqiitqBWqVfYwDwVfXuk8Xfw)
sed -i '' '/pub ext_token_program: Program<'\''info, Token2022>,/a\'$$'\n''\ pub dummy_account: Program<'\''info, Token2022>,' programs/m_ext/src/instructions/wrap.rs
cargo fmt
anchor build -p m_ext --skip-lint
@mv target/deploy/m_ext.so tests/programs/ext_c.so
sed -i '' '/pub dummy_account: Program<'\''info, Token2022>,/d' programs/m_ext/src/instructions/wrap.rs
$(call update-program-id,3C865D264L4NkAm78zfnDzQJJvXuU3fMjRUvRxyPi5da)
anchor build -p m_ext
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
# Solana M Extension Programs
The programs in this repository implement different version of an "M Extension", which is a stablecoin backed by M.

The program (`m_ext`) in this repository implements different versions of an "M Extension", which is a stablecoin backed by $M. The versions have a shared codebase for the majority of the code and use Rust feature flags to implement version-specific logic. The program relies on the underlying yield distribution of the $M token on Solana, which can be found in the [solana-m repository](https://github.com/m0-foundation/solana-m).

## Extensions

The list of implemented extensions is:

- NoYield - no yield is distributed to extension holders.
- ScaledUiAmount - yield is distributed to all extension token holders using the Token2022 ScaledUiAmount "rebasing" functionality.

## Development

The Solana programs in this repository are built using Anchor. The required toolchain is specified in the Anchor.toml file.

Use [`agave-install`](https://docs.anza.xyz/cli/install) to install and initialize the correct Solana CLI & runtime (`2.1.0`).
Expand All @@ -14,6 +19,6 @@ Then, use [`avm`](https://www.anchor-lang.com/docs/installation) to install and

Finally, the tests are written in Typescript using the LiteSVM framework. The javascript package manager is `yarn`. Install the required dependencies with `yarn install`.

The programs can then be built with: `anchor build`
The programs can then be built with: `make build-programs`. This will compile each variant of the `m_ext` program and save the bytecode plus the IDL in the target folder with the name of the extension. Yield features are not compatible with each other and only one can be selected.

The tests can be run with `yarn test`. If editing programs between test runs, be sure to recompile as the test runner doesn't do so automatically, i.e. `anchor build && yarn test`.
The tests can be run with `make test-programs`. If editing programs between test runs, be sure to recompile as the test runner doesn't do so automatically, i.e. `make build-programs && make test-programs`.
54 changes: 27 additions & 27 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
{
"scripts": {
"lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w",
"lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check",
"test": "yarn run jest --preset ts-jest --verbose",
"build": "anchor build"
},
"dependencies": {
"@coral-xyz/anchor": "^0.31.1",
"@m0-foundation/solana-m-sdk": "https://gitpkg.vercel.app/m0-foundation/solana-m/sdk?0991eb829a52f6ba5969fd47d3100ef6871f0a6d&scripts.postinstall=yarn%20build",
"@solana-developers/helpers": "^2.7.0",
"@solana/spl-token": "^0.4.13",
"@solana/web3.js": "^1.98",
"anchor-litesvm": "0.1.2",
"bn.js": "^5.2.1",
"litesvm": "0.2.0"
},
"devDependencies": {
"@types/bn.js": "^5.1.0",
"@types/jest": "^29.0.3",
"@types/node": "^22.13.13",
"jest": "^29.0.3",
"nock": "^14.0.2",
"prettier": "^2.6.2",
"ts-jest": "^29.0.2",
"ts-node": "^10.9.2",
"typescript": "5"
}
"scripts": {
"lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w",
"lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check",
"test": "make test-programs",
"build": "make build-programs"
},
"dependencies": {
"@coral-xyz/anchor": "^0.31.1",
"@m0-foundation/solana-m-sdk": "https://gitpkg.vercel.app/m0-foundation/solana-m/sdk?0991eb829a52f6ba5969fd47d3100ef6871f0a6d&scripts.postinstall=yarn%20build",
"@solana-developers/helpers": "^2.7.0",
"@solana/spl-token": "^0.4.13",
"@solana/web3.js": "^1.98",
"anchor-litesvm": "0.1.2",
"bn.js": "^5.2.1",
"litesvm": "0.2.0"
},
"devDependencies": {
"@types/bn.js": "^5.1.0",
"@types/jest": "^29.0.3",
"@types/node": "^22.13.13",
"jest": "^29.0.3",
"nock": "^14.0.2",
"prettier": "^2.6.2",
"ts-jest": "^29.0.2",
"ts-node": "^10.9.2",
"typescript": "5"
}
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
[package]
name = "scaled_ui_ext"
name = "ext_swap"
version = "0.1.0"
description = "Created with Anchor"
edition = "2021"

[lib]
crate-type = ["cdylib", "lib"]
name = "scaled_ui_ext"
name = "ext_swap"

[features]
default = []
cpi = ["no-entrypoint"]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
cpi = ["no-entrypoint"]
default = []
idl-build = ["anchor-lang/idl-build", "anchor-spl/idl-build"]


[dependencies]
anchor-lang.workspace = true
anchor-spl.workspace = true
spl-token-2022.workspace = true
cfg-if.workspace = true
solana-security-txt.workspace = true
solana-program.workspace = true
earn.workspace = true
solana-security-txt.workspace = true
m_ext = {path = "../m_ext", features = ["cpi"]}
File renamed without changes.
13 changes: 13 additions & 0 deletions programs/ext_swap/src/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use anchor_lang::prelude::*;

#[error_code]
pub enum SwapError {
#[msg("Extension is not whitelisted")]
InvalidExtension,
#[msg("Extension is already whitelisted")]
AlreadyWhitelisted,
#[msg("Index invalid for length of the array")]
InvalidIndex,
#[msg("Signer is not whitelisted")]
UnauthorizedUnwrapper,
}
34 changes: 34 additions & 0 deletions programs/ext_swap/src/instructions/initialize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use anchor_lang::prelude::*;

use crate::state::{SwapGlobal, GLOBAL_SEED};

#[derive(Accounts)]
pub struct InitializeGlobal<'info> {
#[account(mut)]
pub admin: Signer<'info>,

#[account(
init,
payer = admin,
space = SwapGlobal::size(0,0),
seeds = [GLOBAL_SEED],
bump,
)]
pub swap_global: Account<'info, SwapGlobal>,

pub system_program: Program<'info, System>,
}

impl InitializeGlobal<'_> {
pub fn handler(ctx: Context<Self>, m_mint: Pubkey) -> Result<()> {
ctx.accounts.swap_global.set_inner(SwapGlobal {
bump: ctx.bumps.swap_global,
admin: ctx.accounts.admin.key(),
m_mint: m_mint,
whitelisted_unwrappers: vec![],
whitelisted_extensions: vec![],
});

Ok(())
}
}
11 changes: 11 additions & 0 deletions programs/ext_swap/src/instructions/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
pub mod initialize;
pub mod swap;
pub mod unwrap;
pub mod whitelist;
pub mod wrap;

pub use initialize::*;
pub use swap::*;
pub use unwrap::*;
pub use whitelist::*;
pub use wrap::*;
Loading
Loading