Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
111 changes: 92 additions & 19 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,12 @@ on:
pull_request:
workflow_dispatch:

env:
FOUNDRY_PROFILE: ci

jobs:
check:
strategy:
fail-fast: true

name: Foundry project
evm:
name: EVM (Foundry)
runs-on: ubuntu-latest
env:
FOUNDRY_PROFILE: ci
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
Expand All @@ -29,23 +25,100 @@ jobs:
uses: foundry-rs/foundry-toolchain@8b0419c685ef46cb79ec93fbdc131174afceb730 # v1.6.0

- name: Show Forge version
run: |
forge --version
run: forge --version

- name: Run Forge fmt
run: |
forge fmt --check
id: fmt
run: forge fmt --check
working-directory: evm

- name: Run Forge build
run: |
forge build --sizes
id: build
run: forge build --sizes
working-directory: evm

- name: Run Forge tests
run: |
forge test -vvv
id: test
run: forge test -vvv
working-directory: evm

solana:
name: Solana (Anchor)
runs-on: ubuntu-latest
env:
SOLANA_VERSION: 3.0.13
ANCHOR_VERSION: 0.31.1
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
egress-policy: audit

- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- name: Show Rust version
run: rustc --version

- name: Check Rust formatting
working-directory: solana
run: cargo fmt --all -- --check

- name: Install JS dependencies
run: yarn install --frozen-lockfile
working-directory: solana

- name: Check TypeScript formatting
working-directory: solana
run: yarn lint

- name: Install Solana CLI
run: |
set -euo pipefail
curl --proto '=https' --tlsv1.2 -sSfL \
"https://github.com/anza-xyz/agave/releases/download/v${SOLANA_VERSION}/solana-release-x86_64-unknown-linux-gnu.tar.bz2" \
| tar -xj -C "$HOME"
echo "$HOME/solana-release/bin" >> "$GITHUB_PATH"

- name: Verify Solana toolchain
run: |
solana --version
solana-keygen --version

- name: Install Anchor CLI
run: |
cargo install --git https://github.com/coral-xyz/anchor \
--tag "v${ANCHOR_VERSION}" anchor-cli --locked

- name: Verify Anchor CLI
run: anchor --version

- name: Generate wallet keypair
run: |
set -euo pipefail
mkdir -p "$HOME/.config/solana"
solana-keygen new --no-bip39-passphrase --silent --force \
--outfile "$HOME/.config/solana/id.json"
solana address

- name: Generate ephemeral program keypair and align IDs
working-directory: solana
run: |
set -euo pipefail
mkdir -p target/deploy
solana-keygen new --no-bip39-passphrase --silent --force \
--outfile target/deploy/stable_swapper-keypair.json
TEST_ID=$(solana address -k target/deploy/stable_swapper-keypair.json)
perl -pi -e "s/declare_id!\\(\"[^\"]+\"\\)/declare_id!(\"$TEST_ID\")/" \
programs/stable-swapper/src/lib.rs
awk -v id="$TEST_ID" '
/^\[/ { in_localnet = ($0 ~ /^\[programs\.localnet\]$/) }
in_localnet && /^stable_swapper[[:space:]]*=/ {
print "stable_swapper = \"" id "\""; next
}
{ print }
' Anchor.toml > Anchor.toml.tmp && mv Anchor.toml.tmp Anchor.toml

- name: Build Anchor program
working-directory: solana
run: anchor build

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any formatting needed before the build like in evm?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed


- name: Run Anchor tests
working-directory: solana
run: anchor test --provider.cluster localnet --skip-build
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ stable-swapper/
└── PULL_REQUEST_TEMPLATE.md
```

Each implementation directory is self-contained with its own source, tests, scripts, and README.
Each implementation directory is self-contained with its own source, tests, and README.

## Development Workflow

Expand Down
9 changes: 3 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ StableSwapper enables swapping between stablecoins at a 1:1 ratio, automatically

- **1:1 Stablecoin Swaps** -- Swap between any listed stablecoins with automatic decimal normalization
- **Fee Collection** -- Configurable fee in basis points, charged on the input token
- **Role-Based Access Control** -- Distinct roles with separated concerns for administration, treasury, configuration, and pausing
- **Feature Flags** -- Independent toggles for swaps, withdrawals, and allowlist enforcement
- **Reserved Amounts** -- Reserve token balances from being consumed by swaps
- **Role-Based Access Control** -- Distinct authorities with separated concerns
- **Pause Controls** -- Independent toggles for swap and liquidity operations
- **Slippage Protection** -- Users specify a minimum output amount per swap

## Implementations
Expand All @@ -30,19 +29,17 @@ See each implementation's README for chain-specific quickstart, build, test, and
stable-swapper/
├── evm/ # EVM (Solidity) implementation
│ ├── src/ # Production contracts
│ ├── script/ # Deployment and utility scripts
│ ├── test/ # Unit and integration tests
│ └── README.md
├── solana/ # SVM (Rust / Anchor) implementation
│ ├── programs/ # On-chain Anchor program
│ ├── tests/ # Anchor / Mocha integration tests
│ ├── scripts/ # Admin and emergency scripts
│ ├── runbooks/ # Deployment runbooks
│ └── README.md
├── LICENSE
├── SECURITY.md
├── CONTRIBUTING.md
└── .github/
├── workflows/ # CI workflows
└── PULL_REQUEST_TEMPLATE.md
```

Expand Down
43 changes: 10 additions & 33 deletions evm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,17 @@ Solidity implementation of StableSwapper using the UUPS upgradeable proxy patter

## Features

- **1:1 Stablecoin Swaps** -- Fixed-rate swapping between any listed stablecoins with automatic decimal normalization
- **Configurable Fees** -- Fee in basis points charged on the input token, with a dedicated fee recipient
- **Slippage Protection** -- Users specify a minimum output amount per swap
- **Feature Flags** -- Independent toggles for swaps, withdrawals, and allowlist enforcement
- **Allowlist** -- Optional caller-allowlist gating for swaps (off by default; the recipient is never gated)
- **Reserved Amounts** -- Reserve token balances per token to protect against swap-side liquidity consumption (treasury role can still withdraw past the reserve)
- **Per-Token Pause** -- Disable individual tokens without disabling the rest of the pool
- **UUPS Upgradeability** -- Upgradeable via the [ERC-1967](https://eips.ethereum.org/EIPS/eip-1967) proxy pattern
- **ERC-7201 Namespaced Storage** -- Collision-resistant storage layout
- **Role-Based Access Control** -- Four distinct roles:
- `DEFAULT_ADMIN_ROLE` -- Upgrades and role management (single holder, 2-step transfer)
- `DEFAULT_ADMIN_ROLE` -- Upgrades and role management (single holder, 2-step transfer with configurable delay)
- `TREASURY_ROLE` -- Liquidity withdrawals and reserved amount management
- `CONFIGURE_ROLE` -- Token listing, fee updates, and allowlist management
- `PAUSE_ROLE` -- Pause/unpause operations and individual token status
Expand Down Expand Up @@ -38,46 +45,16 @@ forge test -vvv
forge fmt --check
```

## Deploy
## Deployment

Set the required environment variables and run the deployment script. `RPC_URL` should be set to an RPC endpoint for the target network (e.g., from [Alchemy](https://www.alchemy.com/), [Infura](https://www.infura.io/), or a self-hosted node):

```sh
export RPC_URL=<https://your-rpc-endpoint>
export DEFAULT_ADMIN=<address>
export TREASURY_AUTHORITY=<address>
export CONFIGURE_AUTHORITY=<address>
export PAUSE_AUTHORITY=<address>
export FEE_RECIPIENT=<address>
export FEE_BASIS_POINTS=<uint16>
export ADMIN_TRANSFER_DELAY=<seconds>

forge script script/DeployStableSwapper.s.sol:DeployStableSwapper \
--rpc-url $RPC_URL \
--broadcast \
--verify
```

## Verify an Existing Deployment

```sh
export RPC_URL=<https://your-rpc-endpoint>
export STABLE_SWAPPER_PROXY=<proxy_address>

forge script script/VerifyDeployment.s.sol:VerifyDeployment \
--rpc-url $RPC_URL
```
Deployment is performed via out-of-repo tooling. The contract under `src/` is the source of truth for the on-chain behavior; integrators should refer to the proxy ABI generated by `forge build` to interact with a deployed instance.

## Project Structure

```
evm/
├── src/
│ └── StableSwapper.sol # Core swap contract
├── script/
│ ├── DeployStableSwapper.s.sol # Deployment script (UUPS proxy)
│ ├── VerifyDeployment.s.sol # Post-deployment verification
│ └── GenerateStorageLocation.s.sol # ERC-7201 storage slot generator
├── test/
│ ├── unit/ # Unit tests by function
│ ├── integration/ # Multi-token swap scenarios
Expand Down
2 changes: 1 addition & 1 deletion evm/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ via_ir = true
optimizer = true
optimizer_runs = 200
solc_version = "0.8.30"
evm_version = "prague"
evm_version = "prague"

# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
Loading
Loading