From 67ef42779c195a7089ba40891bad44000c39737a Mon Sep 17 00:00:00 2001 From: Braden Thompson Date: Wed, 13 May 2026 18:12:29 -0600 Subject: [PATCH 1/6] Chore: cleanup repo for oss publish. --- evm/script/DeployStableSwapper.s.sol | 195 -- evm/script/GenerateStorageLocation.s.sol | 25 - evm/script/VerifyDeployment.s.sol | 149 -- evm/test/unit/DeployStableSwapper.t.sol | 119 - solana/migrations/deploy.ts | 12 - .../Cargo.toml | 0 .../Xargo.toml | 0 .../src/constants.rs | 0 .../src/errors.rs | 0 .../src/lib.rs | 0 .../src/state.rs | 0 .../src/utils.rs | 0 solana/runbooks/DEVNET_DEPLOYMENT-20260120.md | 984 -------- .../runbooks/MAINNET_DEPLOYMENT-20251219.md | 2243 ----------------- .../runbooks/MAINNET_DEPLOYMENT-20260108.md | 870 ------- solana/runbooks/MAINNET_DEPLOYMENT_RUNBOOK.md | 715 ------ solana/scripts/01-initialize-pool.ts | 170 -- solana/scripts/02-add-token.ts | 197 -- solana/scripts/04-test-swap.ts | 331 --- solana/scripts/emergency-pause-liquidity.ts | 152 -- solana/scripts/emergency-pause-swaps.ts | 144 -- solana/scripts/emergency-withdraw.ts | 255 -- solana/scripts/update-operations-authority.ts | 159 -- solana/scripts/update-pause-authority.ts | 152 -- solana/scripts/update-token-status.ts | 167 -- solana/scripts/verify-pool.ts | 135 - .../{scaas-liquidity.ts => stable-swapper.ts} | 0 27 files changed, 7174 deletions(-) delete mode 100644 evm/script/DeployStableSwapper.s.sol delete mode 100644 evm/script/GenerateStorageLocation.s.sol delete mode 100644 evm/script/VerifyDeployment.s.sol delete mode 100644 evm/test/unit/DeployStableSwapper.t.sol delete mode 100644 solana/migrations/deploy.ts rename solana/programs/{scaas-liquidity => stable-swapper}/Cargo.toml (100%) rename solana/programs/{scaas-liquidity => stable-swapper}/Xargo.toml (100%) rename solana/programs/{scaas-liquidity => stable-swapper}/src/constants.rs (100%) rename solana/programs/{scaas-liquidity => stable-swapper}/src/errors.rs (100%) rename solana/programs/{scaas-liquidity => stable-swapper}/src/lib.rs (100%) rename solana/programs/{scaas-liquidity => stable-swapper}/src/state.rs (100%) rename solana/programs/{scaas-liquidity => stable-swapper}/src/utils.rs (100%) delete mode 100644 solana/runbooks/DEVNET_DEPLOYMENT-20260120.md delete mode 100644 solana/runbooks/MAINNET_DEPLOYMENT-20251219.md delete mode 100644 solana/runbooks/MAINNET_DEPLOYMENT-20260108.md delete mode 100644 solana/runbooks/MAINNET_DEPLOYMENT_RUNBOOK.md delete mode 100644 solana/scripts/01-initialize-pool.ts delete mode 100644 solana/scripts/02-add-token.ts delete mode 100644 solana/scripts/04-test-swap.ts delete mode 100644 solana/scripts/emergency-pause-liquidity.ts delete mode 100644 solana/scripts/emergency-pause-swaps.ts delete mode 100644 solana/scripts/emergency-withdraw.ts delete mode 100644 solana/scripts/update-operations-authority.ts delete mode 100644 solana/scripts/update-pause-authority.ts delete mode 100644 solana/scripts/update-token-status.ts delete mode 100644 solana/scripts/verify-pool.ts rename solana/tests/{scaas-liquidity.ts => stable-swapper.ts} (100%) diff --git a/evm/script/DeployStableSwapper.s.sol b/evm/script/DeployStableSwapper.s.sol deleted file mode 100644 index b5fe0ef..0000000 --- a/evm/script/DeployStableSwapper.s.sol +++ /dev/null @@ -1,195 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.20; - -import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -import {console} from "forge-std/console.sol"; -import {Script} from "forge-std/Script.sol"; - -import {StableSwapper} from "../src/StableSwapper.sol"; - -/** - * @title DeployStableSwapper - * @notice Deployment script for StableSwapper using UUPS proxy pattern - * @dev This script deploys: - * 1. StableSwapper implementation contract - * 2. ERC1967Proxy pointing to the implementation - * 3. Initializes the proxy with authorities and fee configuration - * - * Usage: - * Set environment variables: - * - DEFAULT_ADMIN: Address with DEFAULT_ADMIN_ROLE (can upgrade contract and manage roles) - * - TREASURY_AUTHORITY: Address with TREASURY_ROLE (can manage liquidity) - * - CONFIGURE_AUTHORITY: Address with CONFIGURE_ROLE (can add tokens, update fees) - * - PAUSE_AUTHORITY: Address with PAUSE_ROLE (can pause operations) - * - FEE_RECIPIENT: Address that receives swap fees - * - FEE_BASIS_POINTS: Fee rate in basis points (e.g., 100 = 1%) - * - ADMIN_TRANSFER_DELAY: Delay in seconds for 2-step admin transfers (e.g., 259200 = 3 days) - * - * Deploy to network: - * forge script script/DeployStableSwapper.s.sol:DeployStableSwapper \ - * --rpc-url $RPC_URL \ - * --broadcast \ - * --verify - * - * Dry run (no broadcast): - * forge script script/DeployStableSwapper.s.sol:DeployStableSwapper \ - * --rpc-url $RPC_URL - */ -contract DeployStableSwapper is Script { - /// @notice Emitted when deployment is successful - event Deployed( - address indexed implementation, - address indexed proxy, - address defaultAdmin, - address treasuryAuthority, - address configureAuthority, - address pauseAuthority, - address feeRecipient, - uint16 feeBasisPoints, - uint48 adminTransferDelay - ); - - /// @notice Main deployment function - /// - /// @dev Reads configuration from environment variables and deploys contracts - function run() external { - // Read configuration from environment variables - address defaultAdmin = vm.envAddress("DEFAULT_ADMIN"); - address treasuryAuthority = vm.envAddress("TREASURY_AUTHORITY"); - address configureAuthority = vm.envAddress("CONFIGURE_AUTHORITY"); - address pauseAuthority = vm.envAddress("PAUSE_AUTHORITY"); - address feeRecipient = vm.envAddress("FEE_RECIPIENT"); - uint16 feeBasisPoints = uint16(vm.envUint("FEE_BASIS_POINTS")); - uint48 adminTransferDelay = uint48(vm.envUint("ADMIN_TRANSFER_DELAY")); - - // Validate configuration - require(defaultAdmin != address(0), "DEFAULT_ADMIN cannot be zero address"); - require(treasuryAuthority != address(0), "TREASURY_AUTHORITY cannot be zero address"); - require(configureAuthority != address(0), "CONFIGURE_AUTHORITY cannot be zero address"); - require(pauseAuthority != address(0), "PAUSE_AUTHORITY cannot be zero address"); - require(feeRecipient != address(0), "FEE_RECIPIENT cannot be zero address"); - - // Log deployment configuration - console.log("\n=== StableSwapper Deployment Configuration ==="); - console.log("Default Admin:", defaultAdmin); - console.log("Treasury Role Holder:", treasuryAuthority); - console.log("Configure Role Holder:", configureAuthority); - console.log("Pause Role Holder:", pauseAuthority); - console.log("Fee Recipient:", feeRecipient); - console.log("Fee (basis points):", feeBasisPoints); - console.log("Fee (percentage):", (uint256(feeBasisPoints) * 100) / 10000, "%"); - console.log("Admin Transfer Delay (seconds):", adminTransferDelay); - console.log("===========================================\n"); - - // Start broadcasting transactions - vm.startBroadcast(); - - // Deploy contracts - (address implementation, address proxy) = deploy( - defaultAdmin, - treasuryAuthority, - configureAuthority, - pauseAuthority, - feeRecipient, - feeBasisPoints, - adminTransferDelay - ); - - vm.stopBroadcast(); - - // Log deployment results - console.log("\n=== Deployment Results ==="); - console.log("Implementation:", implementation); - console.log("Proxy (StableSwapper):", proxy); - console.log("==========================\n"); - - // Emit deployment event - emit Deployed( - implementation, - proxy, - defaultAdmin, - treasuryAuthority, - configureAuthority, - pauseAuthority, - feeRecipient, - feeBasisPoints, - adminTransferDelay - ); - } - - /// @notice Deploys StableSwapper implementation and proxy - /// - /// @param defaultAdmin Address with DEFAULT_ADMIN_ROLE - /// @param treasuryAuthority Address with TREASURY_ROLE - /// @param configureAuthority Address with CONFIGURE_ROLE - /// @param pauseAuthority Address with PAUSE_ROLE - /// @param feeRecipient Address that receives swap fees - /// @param feeBasisPoints Fee in basis points (e.g., 100 = 1%) - /// @param adminTransferDelay Delay in seconds for 2-step admin transfers - /// - /// @return implementation Address of the implementation contract - /// @return proxy Address of the proxy contract (main entry point) - function deploy( - address defaultAdmin, - address treasuryAuthority, - address configureAuthority, - address pauseAuthority, - address feeRecipient, - uint16 feeBasisPoints, - uint48 adminTransferDelay - ) public returns (address implementation, address proxy) { - // Step 1: Deploy implementation contract - console.log("Deploying StableSwapper implementation..."); - StableSwapper implementationContract = new StableSwapper(); - implementation = address(implementationContract); - console.log("Implementation deployed at:", implementation); - - // Step 2: Encode initialization data - bytes memory initData = abi.encodeWithSelector( - StableSwapper.initialize.selector, - defaultAdmin, - treasuryAuthority, - configureAuthority, - pauseAuthority, - feeRecipient, - feeBasisPoints, - adminTransferDelay - ); - - // Step 3: Deploy ERC1967 proxy with initialization - console.log("Deploying ERC1967 proxy..."); - ERC1967Proxy proxyContract = new ERC1967Proxy(implementation, initData); - proxy = address(proxyContract); - console.log("Proxy deployed at:", proxy); - - // Step 4: Verify initialization - StableSwapper stableSwapper = StableSwapper(proxy); - - // Verify authorities - require( - stableSwapper.hasRole(stableSwapper.DEFAULT_ADMIN_ROLE(), defaultAdmin), "Default admin not set correctly" - ); - require( - stableSwapper.hasRole(stableSwapper.TREASURY_ROLE(), treasuryAuthority), "Treasury role not set correctly" - ); - require( - stableSwapper.hasRole(stableSwapper.CONFIGURE_ROLE(), configureAuthority), - "Configure role not set correctly" - ); - require(stableSwapper.hasRole(stableSwapper.PAUSE_ROLE(), pauseAuthority), "Pause role not set correctly"); - - // Verify fee configuration - require(stableSwapper.feeRecipient() == feeRecipient, "Fee recipient not set correctly"); - require(stableSwapper.feeBasisPoints() == feeBasisPoints, "Fee rate not set correctly"); - - // Verify initial state - require(stableSwapper.isFeatureEnabled(StableSwapper.FeatureFlag.SWAP), "Swaps should be enabled"); - require(stableSwapper.isFeatureEnabled(StableSwapper.FeatureFlag.WITHDRAW), "Liquidity should be enabled"); - require(!stableSwapper.isFeatureEnabled(StableSwapper.FeatureFlag.ALLOWLIST), "Allowlist should not be enabled"); - require(stableSwapper.getListedTokensCount() == 0, "No tokens should be supported initially"); - - console.log("Deployment verification successful!"); - - return (implementation, proxy); - } -} diff --git a/evm/script/GenerateStorageLocation.s.sol b/evm/script/GenerateStorageLocation.s.sol deleted file mode 100644 index 957a5bb..0000000 --- a/evm/script/GenerateStorageLocation.s.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.20; - -import {console} from "forge-std/console.sol"; -import {Script} from "forge-std/Script.sol"; - -/** - * @title GenerateStorageLocation - * @notice Generates ERC-7201 storage location from a namespace - * @dev Formula: keccak256(abi.encode(uint256(keccak256(id)) - 1)) & ~bytes32(uint256(0xff)) - * - * Usage: - * NAMESPACE="coinbase.storage.StableSwapper" forge script script/GenerateStorageLocation.s.sol:GenerateStorageLocation - */ -contract GenerateStorageLocation is Script { - function run() external view { - string memory namespace = vm.envString("NAMESPACE"); - - bytes32 location = keccak256(abi.encode(uint256(keccak256(bytes(namespace))) - 1)) & ~bytes32(uint256(0xff)); - - console.log("Namespace:", namespace); - console.log("ERC-7201 Storage Location:"); - console.logBytes32(location); - } -} diff --git a/evm/script/VerifyDeployment.s.sol b/evm/script/VerifyDeployment.s.sol deleted file mode 100644 index 9b746e3..0000000 --- a/evm/script/VerifyDeployment.s.sol +++ /dev/null @@ -1,149 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.20; - -import {console} from "forge-std/console.sol"; -import {Script} from "forge-std/Script.sol"; - -import {StableSwapper} from "../src/StableSwapper.sol"; - -/** - * @title VerifyDeployment - * @notice Script to verify an existing StableSwapper deployment - * @dev This script checks the configuration and state of a deployed StableSwapper - * - * Usage: - * Set environment variables: - * - STABLE_SWAPPER_PROXY: Address of the deployed proxy - * - * Run verification: - * forge script script/VerifyDeployment.s.sol:VerifyDeployment \ - * --rpc-url $RPC_URL - */ -contract VerifyDeployment is Script { - function run() external view { - address proxyAddress = vm.envAddress("STABLE_SWAPPER_PROXY"); - - require(proxyAddress != address(0), "STABLE_SWAPPER_PROXY must be set"); - - console.log("\n=== StableSwapper Deployment Verification ==="); - console.log("Proxy Address:", proxyAddress); - console.log("==========================================\n"); - - verify(proxyAddress); - } - - function verify(address proxyAddress) public view { - StableSwapper stableSwapper = StableSwapper(proxyAddress); - - // Check if contract exists - uint256 codeSize; - assembly { - codeSize := extcodesize(proxyAddress) - } - require(codeSize > 0, "No contract found at proxy address"); - console.log("[OK] Contract exists at proxy address"); - - // Basic configuration - console.log("\n--- Basic Configuration ---"); - - address feeRecipient = stableSwapper.feeRecipient(); - console.log("Fee Recipient:", feeRecipient); - - uint64 feeBasisPoints = stableSwapper.feeBasisPoints(); - console.log("Fee Rate (basis points):", feeBasisPoints); - console.log("Fee Rate (percentage):", (uint256(feeBasisPoints) * 100) / 10000, "%"); - - // Status state - console.log("\n--- Status State ---"); - bool swapsEnabled = stableSwapper.isFeatureEnabled(StableSwapper.FeatureFlag.SWAP); - console.log("Swaps Enabled:", swapsEnabled ? "YES" : "NO"); - - bool withdrawalEnabled = stableSwapper.isFeatureEnabled(StableSwapper.FeatureFlag.WITHDRAW); - console.log("Withdrawal Enabled:", withdrawalEnabled ? "YES" : "NO"); - - bool allowlistEnabled = stableSwapper.isFeatureEnabled(StableSwapper.FeatureFlag.ALLOWLIST); - console.log("Allowlist Enabled:", allowlistEnabled ? "YES" : "NO"); - - // Authorities - console.log("\n--- Authorities ---"); - bytes32 defaultAdminRole = stableSwapper.DEFAULT_ADMIN_ROLE(); - bytes32 treasuryAuthRole = stableSwapper.TREASURY_ROLE(); - bytes32 configureAuthRole = stableSwapper.CONFIGURE_ROLE(); - bytes32 pauseAuthRole = stableSwapper.PAUSE_ROLE(); - - // Note: DEFAULT_ADMIN_ROLE is single-holder via AccessControlDefaultAdminRules - address defaultAdminAddr = stableSwapper.defaultAdmin(); - console.log("Default Admin Role Exists:", stableSwapper.hasRole(defaultAdminRole, address(0)) ? "NO" : "YES"); - console.log("Default Admin:", defaultAdminAddr == address(0) ? "None" : vm.toString(defaultAdminAddr)); - - // Other roles can have multiple holders, so we just check if anyone has them - // For display purposes, we could use getRoleMemberCount() but it's not exposed - // So we'll just note if the initial role holders still have their roles - console.log("Treasury Role Exists:", stableSwapper.hasRole(treasuryAuthRole, address(0)) ? "NO" : "YES"); - console.log("Configure Role Exists:", stableSwapper.hasRole(configureAuthRole, address(0)) ? "NO" : "YES"); - console.log("Pause Role Exists:", stableSwapper.hasRole(pauseAuthRole, address(0)) ? "NO" : "YES"); - - // Pending DEFAULT_ADMIN_ROLE transfer - console.log("\n--- Pending Admin Transfer ---"); - (address pendingAdmin, uint48 acceptSchedule) = stableSwapper.pendingDefaultAdmin(); - console.log("Pending Default Admin:", pendingAdmin == address(0) ? "None" : vm.toString(pendingAdmin)); - if (pendingAdmin != address(0)) { - console.log("Accept Schedule:", acceptSchedule); - } - - // Listed tokens - console.log("\n--- Listed Tokens ---"); - uint256 tokenCount = stableSwapper.getListedTokensCount(); - console.log("Listed Token Count:", tokenCount); - - if (tokenCount > 0) { - address[] memory tokens = stableSwapper.getListedTokens(); - for (uint256 i = 0; i < tokens.length; i++) { - address token = tokens[i]; - - console.log("\n Token [", i, "]:", token); - console.log(" Swappable:", stableSwapper.isTokenSwappable(token) ? "YES" : "NO"); - - // Get decimals directly from token - (bool decSuccess, bytes memory decData) = token.staticcall(abi.encodeWithSignature("decimals()")); - if (decSuccess && decData.length >= 32) { - uint8 decimals = abi.decode(decData, (uint8)); - console.log(" Decimals:", decimals); - } - - console.log(" Reserved Amount:", stableSwapper.getReservedAmount(token)); - - // Get actual balance - (bool success, bytes memory data) = - token.staticcall(abi.encodeWithSignature("balanceOf(address)", proxyAddress)); - if (success && data.length >= 32) { - uint256 balance = abi.decode(data, (uint256)); - console.log(" Actual Balance:", balance); - - uint256 reserved = stableSwapper.getReservedAmount(token); - if (balance >= reserved) { - uint256 available = balance - reserved; - console.log(" Available Liquidity:", available); - } else { - console.log(" WARNING: Balance < Reserved Amount!"); - } - } - } - } else { - console.log(" No tokens supported yet"); - } - - // Allowlist - console.log("\n--- Allowlist ---"); - console.log("Allowlist Feature:", allowlistEnabled ? "ENABLED" : "DISABLED"); - - // Summary - console.log("\n=== Verification Summary ==="); - console.log("[OK] Fee Basis Points:", feeBasisPoints, "bp"); - console.log("[OK] Default Admin Set:", defaultAdminAddr != address(0) ? "YES" : "NO"); - console.log("[OK] Tokens Configured:", tokenCount); - console.log("[OK] Swaps Status:", swapsEnabled ? "ENABLED" : "DISABLED"); - console.log("[OK] Withdrawal Status:", withdrawalEnabled ? "ENABLED" : "DISABLED"); - console.log("============================\n"); - } -} diff --git a/evm/test/unit/DeployStableSwapper.t.sol b/evm/test/unit/DeployStableSwapper.t.sol deleted file mode 100644 index 3c70338..0000000 --- a/evm/test/unit/DeployStableSwapper.t.sol +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.20; - -import {Test} from "forge-std/Test.sol"; -import {Vm} from "forge-std/Vm.sol"; - -import {StableSwapper} from "../../src/StableSwapper.sol"; - -import {DeployStableSwapper} from "../../script/DeployStableSwapper.s.sol"; - -/** - * @title DeployStableSwapperTest - * @notice Tests for the DeployStableSwapper deployment script - */ -contract DeployStableSwapperTest is Test { - DeployStableSwapper deployer; - - address defaultAdmin = makeAddr("defaultAdmin"); - address treasuryAuthority = makeAddr("treasuryAuthority"); - address configureAuthority = makeAddr("configureAuthority"); - address pauseAuthority = makeAddr("pauseAuthority"); - address feeRecipient = makeAddr("feeRecipient"); - uint16 feeBasisPoints = 100; // 1% - - function setUp() public { - deployer = new DeployStableSwapper(); - } - - function test_deploy_success() public { - // Test that deployment triggers the Initialized event from StableSwapper - vm.recordLogs(); - - (address implementation, address proxy) = deployer.deploy( - defaultAdmin, treasuryAuthority, configureAuthority, pauseAuthority, feeRecipient, feeBasisPoints, 0 - ); - - // Get recorded logs - Vm.Log[] memory logs = vm.getRecordedLogs(); - - // Verify that at least one log was emitted - assertTrue(logs.length > 0, "Should emit at least one event"); - - // Verify deployment addresses are valid - assertNotEq(implementation, address(0), "Implementation should not be zero address"); - assertNotEq(proxy, address(0), "Proxy should not be zero address"); - assertNotEq(implementation, proxy, "Implementation and proxy should be different"); - - // Verify proxy is initialized correctly - StableSwapper stableSwapper = StableSwapper(proxy); - - assertEq(stableSwapper.feeRecipient(), feeRecipient, "Fee recipient should match"); - assertEq(stableSwapper.feeBasisPoints(), feeBasisPoints, "Fee rate should match"); - assertTrue(stableSwapper.isFeatureEnabled(StableSwapper.FeatureFlag.SWAP), "Swaps should be enabled"); - assertTrue(stableSwapper.isFeatureEnabled(StableSwapper.FeatureFlag.WITHDRAW), "Liquidity should be enabled"); - assertFalse( - stableSwapper.isFeatureEnabled(StableSwapper.FeatureFlag.ALLOWLIST), "Allowlist should not be enabled" - ); - assertEq(stableSwapper.getListedTokensCount(), 0, "Should have no supported tokens"); - - // Verify authorities - assertTrue( - stableSwapper.hasRole(stableSwapper.DEFAULT_ADMIN_ROLE(), defaultAdmin), "Default admin role should be set" - ); - assertTrue( - stableSwapper.hasRole(stableSwapper.TREASURY_ROLE(), treasuryAuthority), "Treasury role should be set" - ); - assertTrue( - stableSwapper.hasRole(stableSwapper.CONFIGURE_ROLE(), configureAuthority), "Configure role should be set" - ); - assertTrue(stableSwapper.hasRole(stableSwapper.PAUSE_ROLE(), pauseAuthority), "Pause role should be set"); - - // Test configure role can update fee rate - vm.prank(configureAuthority); - stableSwapper.updateFeeBasisPoints(200); - assertEq(stableSwapper.feeBasisPoints(), 200, "Configure role should be able to update fee rate"); - - // Test pause role can disable swaps - vm.prank(pauseAuthority); - stableSwapper.setFeatureFlag(StableSwapper.FeatureFlag.SWAP, false); - assertFalse( - stableSwapper.isFeatureEnabled(StableSwapper.FeatureFlag.SWAP), "Pause role should be able to disable swaps" - ); - - // Test pause role can enable swaps - vm.prank(pauseAuthority); - stableSwapper.setFeatureFlag(StableSwapper.FeatureFlag.SWAP, true); - assertTrue( - stableSwapper.isFeatureEnabled(StableSwapper.FeatureFlag.SWAP), "Pause role should be able to enable swaps" - ); - } - - function test_deploy_implementation_cannot_be_initialized() public { - (address implementation,) = deployer.deploy( - defaultAdmin, treasuryAuthority, configureAuthority, pauseAuthority, feeRecipient, feeBasisPoints, 0 - ); - - // Try to initialize the implementation directly (should fail) - StableSwapper impl = StableSwapper(implementation); - - vm.expectRevert(); - impl.initialize( - defaultAdmin, treasuryAuthority, configureAuthority, pauseAuthority, feeRecipient, feeBasisPoints, 0 - ); - } - - function test_deploy_proxy_cannot_be_initialized_twice() public { - (, address proxy) = deployer.deploy( - defaultAdmin, treasuryAuthority, configureAuthority, pauseAuthority, feeRecipient, feeBasisPoints, 0 - ); - - StableSwapper stableSwapper = StableSwapper(proxy); - - // Try to initialize again (should fail) - vm.expectRevert(); - stableSwapper.initialize( - defaultAdmin, treasuryAuthority, configureAuthority, pauseAuthority, feeRecipient, feeBasisPoints, 0 - ); - } -} diff --git a/solana/migrations/deploy.ts b/solana/migrations/deploy.ts deleted file mode 100644 index 439431e..0000000 --- a/solana/migrations/deploy.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Migrations are an early feature. Currently, they're nothing more than this -// single deploy script that's invoked from the CLI, injecting a provider -// configured from the workspace's Anchor.toml. - -import * as anchor from "@coral-xyz/anchor"; - -module.exports = async function (provider: anchor.AnchorProvider) { - // Configure client to use the provider. - anchor.setProvider(provider); - - // Add your deploy script here. -}; diff --git a/solana/programs/scaas-liquidity/Cargo.toml b/solana/programs/stable-swapper/Cargo.toml similarity index 100% rename from solana/programs/scaas-liquidity/Cargo.toml rename to solana/programs/stable-swapper/Cargo.toml diff --git a/solana/programs/scaas-liquidity/Xargo.toml b/solana/programs/stable-swapper/Xargo.toml similarity index 100% rename from solana/programs/scaas-liquidity/Xargo.toml rename to solana/programs/stable-swapper/Xargo.toml diff --git a/solana/programs/scaas-liquidity/src/constants.rs b/solana/programs/stable-swapper/src/constants.rs similarity index 100% rename from solana/programs/scaas-liquidity/src/constants.rs rename to solana/programs/stable-swapper/src/constants.rs diff --git a/solana/programs/scaas-liquidity/src/errors.rs b/solana/programs/stable-swapper/src/errors.rs similarity index 100% rename from solana/programs/scaas-liquidity/src/errors.rs rename to solana/programs/stable-swapper/src/errors.rs diff --git a/solana/programs/scaas-liquidity/src/lib.rs b/solana/programs/stable-swapper/src/lib.rs similarity index 100% rename from solana/programs/scaas-liquidity/src/lib.rs rename to solana/programs/stable-swapper/src/lib.rs diff --git a/solana/programs/scaas-liquidity/src/state.rs b/solana/programs/stable-swapper/src/state.rs similarity index 100% rename from solana/programs/scaas-liquidity/src/state.rs rename to solana/programs/stable-swapper/src/state.rs diff --git a/solana/programs/scaas-liquidity/src/utils.rs b/solana/programs/stable-swapper/src/utils.rs similarity index 100% rename from solana/programs/scaas-liquidity/src/utils.rs rename to solana/programs/stable-swapper/src/utils.rs diff --git a/solana/runbooks/DEVNET_DEPLOYMENT-20260120.md b/solana/runbooks/DEVNET_DEPLOYMENT-20260120.md deleted file mode 100644 index cc684b6..0000000 --- a/solana/runbooks/DEVNET_DEPLOYMENT-20260120.md +++ /dev/null @@ -1,984 +0,0 @@ -# Devnet Deployment Runbook - Liquidity Pool Program - -**Status:** Not Started -**Date Started:** 2026-01-20 -**Deployer:** `___________________` - ---- - -## ⚠️ CRITICAL: Devnet Environment Setup - -**Before running ANY scripts, you MUST set these environment variables:** - -```bash -export ANCHOR_PROVIDER_URL="https://api.devnet.solana.com" -export ANCHOR_WALLET="$HOME/.config/solana/id.json" -``` - -**Failure to set these will cause scripts to run against MAINNET instead of DEVNET!** - -Verify with: -```bash -echo $ANCHOR_PROVIDER_URL # Should output: https://api.devnet.solana.com -``` - ---- - -## Overview - -This runbook guides you through deploying the liquidity pool program to Solana **DEVNET** for testing and development purposes. - -**Program ID:** `___________________` - ---- - -## Key Information - -### Addresses -- **Deployer Wallet:** `13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD` -- **USDC Mint (Devnet):** `4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU` (Devnet USDC - 6 decimals) -- **USDF Mint (Devnet):** `Ejgz8rRPySWomDUnsYr1gNpHYm6ZBQnjqbAPfi1dbbwQ` (Devnet USDF - USD stablecoin for testing) -- **SFTUSD25 Mint (Devnet):** `AreQsswF44khJHLjsuWzgboJcG31JvALcWMkMcsVs2uC` (Devnet SFTUSD25 - stablecoin variant for testing) -- **Pool PDA:** `68xwTJYRyUdULbb5ve4JMWTc2xvw9wQmtEj9GRoHSenF` -- **USDC Vault:** `DHCU6cSXeEwk5GUhGVw7XkNfcWF6bY2gsUbtUEUitHhY` -- **USDC Vault Token Account:** `7hktVUoYS9q1c6iq5cnNVmKths3kJ1fJQkozvo4vpHR9` -- **USDF Vault:** `3g3UUaL6y6AjrQuc2cmJHbJmi1XWhEK7VqGrY4CL2Lgn` -- **USDF Vault Token Account:** `pH8X9mDMGEywYHH5fAprVE57LWaHtviwx1J6ZnhgJfm` -- **SFTUSD25 Vault:** `Hon72MLLmPMqQTNphjCvE9Epwdh88S5tZYa1mtLwtn1c` -- **SFTUSD25 Vault Token Account:** `4M8SWnpq6nu7Ud4Qctq3w8HNTWBZP89tYQ27VwaqfvNo` - -### Configuration -- **Fee Rate:** 0 basis points (0% - for 1:1 swaps) -- **Operations Authority:** 2yRhUBaydvJzkyVYpFtMBrWZWCnxdYRQtJmqtH2ugtzS -- **Pause Authority:** CC2ymqGpoSjk2estBTC4iYzuxooqSamnoYR2KGo8CYM6 -- **Fee Recipient:** Deployer wallet (13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD) - ---- - -## PHASE 1: Pre-Deployment Setup - -### ✅ Step 1.0: Install Solana Development Tools - -Follow the official Solana installation guide (installs Rust, Solana CLI, and Anchor Framework): - -**Guide:** https://solana.com/docs/intro/installation - -**After installation, verify:** -```bash -solana --version -anchor --version -spl-token --version -``` - -**Tested versions:** -- Solana CLI: 2.2.21+ -- Anchor CLI: 0.31.1+ -- SPL Token CLI: 5.3.0+ (included with Solana CLI) - -**Result:** -- [x] Completed -- Solana version: `2.2.21` -- Anchor version: `0.31.1` -- SPL Token version: `5.3.0` -- Notes: All versions verified - ---- - -### ✅ Step 1.1: Configure Environment to Solana Devnet -**Command:** -```bash -solana config set -u d -``` - -**Verify:** -```bash -solana config get -``` - -**Expected output should show:** -``` -RPC URL: https://api.devnet.solana.com -``` - -**Result:** -- [x] Completed -- RPC URL: `https://api.devnet.solana.com` -- Notes: Successfully configured for devnet - ---- - -### ✅ Step 1.2: Get Wallet Address -**Command:** -```bash -solana address -``` - -**Result:** -- [x] Completed -- Wallet Address: `13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD` - ---- - -### ✅ Step 1.3: Fund Wallet with Devnet SOL - -**Command:** -```bash -# Request 2 SOL from devnet faucet (can be run multiple times) -solana airdrop 2 - -# Check balance -solana balance -``` - -**Expected:** At least 4-5 SOL (run airdrop command multiple times if needed) - -**Alternative:** Use web faucet at https://faucet.solana.com - -**Result:** -- [x] Completed -- Balance: `9.23` SOL -- Notes: Sufficient balance for deployment - ---- - -### ✅ Step 1.4: Build the Program -**Command:** -```bash -anchor clean -anchor build -``` - -**Result:** -- [x] Completed -- Notes: Build successful - ---- - -### ✅ Step 1.5: Verify Program Build -**Command:** -```bash -# Check that the program is built -ls -lh target/deploy/scaas_liquidity.so - -# Verify program keypair exists -solana address -k target/deploy/scaas_liquidity-keypair.json - -# Compare with what's in Anchor.toml [programs.devnet] section -cat Anchor.toml | grep -A 3 "[programs.devnet]" -``` - -**Important:** For devnet, you can generate a new keypair or use an existing one. - -**Result:** -- [x] Completed -- Program Size: `452K` -- Program ID from keypair: `9vDwZVJXw5nxymWmUcgmNpemDH5EBcJwLNhtsznrgJDH` -- Program ID in Anchor.toml: `9vDwZVJXw5nxymWmUcgmNpemDH5EBcJwLNhtsznrgJDH` -- IDs Match: `Yes` -- Notes: All program IDs match correctly - ---- - -### ✅ Step 1.6: Create or Verify Custom Token - -**Option A: Create a new test token** -```bash -# Create a new token mint with 6 decimals -spl-token create-token --decimals 6 - -# Create token account for your wallet -spl-token create-account - -# Mint some tokens for testing (e.g., 100,000 tokens) -spl-token mint 100000 -``` - -**Option B: Use existing token** -```bash -# Check your token mint address -spl-token accounts - -# Get specific token balance and verify decimals -spl-token account-info - -# If you have no token account for the token mint, create it -spl-token create-account -``` - -**Result:** -- [ ] Completed -- Custom Token Mint: `___________________` -- Custom Token Decimals: `___` -- Your Token Account: `___________________` -- Your Token Balance: `___________________` -- Notes: `___________________` - ---- - -### ✅ Step 1.7: Get Devnet USDC (Optional) - -**Note:** You can use Devnet USDC or skip this for asymmetric liquidity testing. - -**Devnet USDC Mint:** `4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU` - -**Commands:** -```bash -# Create USDC token account -spl-token create-account 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU - -# Check balance -spl-token balance 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU -``` - -**To get devnet USDC:** -- Use SPL Token Faucet: https://spl-token-faucet.com -- Or request from other devnet faucets - -**Result:** -- [ ] Completed -- USDC Token Account: `___________________` -- USDC Balance: `___________________` -- Notes: `___________________` - ---- - -## PHASE 2: Program Deployment - -### ✅ Step 2.1: Deploy Program to Devnet -**Command:** -```bash -anchor deploy --provider.cluster devnet -``` - -**Result:** -- [x] Completed -- Transaction Signature: `83NapGXe2Zrk7CCwv496CbakQrXXnZRLo1umVQ8RZXpx3WvbGKSBYeyqcHEspxHQkZFVhoQiFNMwFovp3i2kuB8` -- Program ID: `9vDwZVJXw5nxymWmUcgmNpemDH5EBcJwLNhtsznrgJDH` -- Deployment Cost: `~3.22` SOL -- Timestamp: `2026-01-20` -- Notes: Deployment successful to devnet - ---- - -### ✅ Step 2.2: Verify Deployment -**Command:** -```bash -solana program show -``` - -**Result:** -- [x] Completed -- Program Exists: `Yes` -- **Program Id**: `9vDwZVJXw5nxymWmUcgmNpemDH5EBcJwLNhtsznrgJDH` -- **Owner**: `BPFLoaderUpgradeab1e11111111111111111111111` -- **ProgramData Address**: `6Hs6o7iXt4jPL7s9hrvgN7vZX3WMPpek4mhgzroCLXiR` -- **Authority**: `13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD` -- **Last Deployed In Slot**: `436473580` -- **Data Length**: `462928 bytes` -- **Balance**: `3.22318296 SOL` -- Notes: Program successfully deployed and verified on devnet - ---- - -## PHASE 3: Initialize Pool - -**⚠️ IMPORTANT:** Before running any scripts in this phase, ensure you have set the environment variables in Step 3.2 to point to devnet! - -### ✅ Step 3.1: Verify TypeScript Dependencies -**Command:** -```bash -# Check if already installed -ls node_modules/ts-node - -# If not installed, run: -yarn add -D ts-node typescript @types/node -``` - -**Result:** -- [ ] Completed -- Notes: `___________________` - ---- - -### ✅ Step 3.2: Set Environment Variables for Devnet - -**⚠️ CRITICAL:** You MUST set these environment variables to ensure scripts run against devnet, not mainnet! - -**Command:** -```bash -# Export these in your shell (or add to ~/.bashrc or ~/.zshrc) -export ANCHOR_PROVIDER_URL="https://api.devnet.solana.com" -export ANCHOR_WALLET="$HOME/.config/solana/id.json" -``` - -**Verify the variables are set:** -```bash -echo $ANCHOR_PROVIDER_URL -# Should output: https://api.devnet.solana.com - -echo $ANCHOR_WALLET -# Should output: /Users//.config/solana/id.json (or your wallet path) -``` - -**Alternative: Set them for a single command:** -```bash -ANCHOR_PROVIDER_URL="https://api.devnet.solana.com" ANCHOR_WALLET="$HOME/.config/solana/id.json" yarn ts-node scripts/01-initialize-pool.ts 0 -``` - -**Result:** -- [ ] Completed -- ANCHOR_PROVIDER_URL verified: `___________________` -- ANCHOR_WALLET verified: `___________________` -- Notes: `___________________` - ---- - -### ✅ Step 3.3: Run Pool Initialization - -**⚠️ Double-check before running:** -```bash -# Verify you're on devnet -echo $ANCHOR_PROVIDER_URL -# Must output: https://api.devnet.solana.com -``` - -**Command:** -```bash -# For 0% fee (1:1 swaps) -yarn ts-node scripts/01-initialize-pool.ts 0 - -# Or for 1% fee -yarn ts-node scripts/01-initialize-pool.ts 100 -``` - -**Result:** -- [x] Completed -- Fee Rate Used: `0` bps -- Transaction Signature: `43RNFGxYpLSQ2iUHWxQLA1HZfW6AiE3Y44hTemHwTWnZgDPqxLmutRZEaGc9ajZQhRnNym1n5wBb28gfvS4BDczL` -- Pool PDA: `68xwTJYRyUdULbb5ve4JMWTc2xvw9wQmtEj9GRoHSenF` -- Timestamp: `2026-01-20` -- Explorer URL: `https://solscan.io/tx/43RNFGxYpLSQ2iUHWxQLA1HZfW6AiE3Y44hTemHwTWnZgDPqxLmutRZEaGc9ajZQhRnNym1n5wBb28gfvS4BDczL?cluster=devnet` -- Notes: Pool initialized successfully with 0% fee - ---- - -### ✅ Step 3.4: Verify Pool State -**Command:** -```bash -yarn ts-node scripts/verify-pool.ts -``` - -**Result:** -- [ ] Completed -- Operations Authority: `___________________` -- Pause Authority: `___________________` -- Fee Recipient: `___________________` -- Fee Rate: `___________________` bps -- Swaps Paused: `___________________` -- Liquidity Paused: `___________________` -- Supported Tokens Count: `___________________` -- Notes: `___________________` - ---- - -## PHASE 4: Add USDC (Devnet) - -### ✅ Step 4.1: Add Devnet USDC to Pool -**Note:** We use the same `02-add-token.ts` script for all tokens. - -**Command:** -```bash -yarn ts-node scripts/02-add-token.ts 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU -``` - -**Result:** -- [x] Completed -- Transaction Signature: `xagREaP2vDJKPG5x5Gqb3t44HJvgMBksGBoMmk6JK7CMBVZ9x6BhoCYKJ1X8dHdju97pudj7cN1apmZrWQ3fxKB` -- USDC Vault PDA: `DHCU6cSXeEwk5GUhGVw7XkNfcWF6bY2gsUbtUEUitHhY` -- USDC Vault Token Account: `7hktVUoYS9q1c6iq5cnNVmKths3kJ1fJQkozvo4vpHR9` -- Fee Recipient Token Account: `ApRsuEag25j28AXDH7HQ68cG1oHzNyuauKtk98szm6Y8` -- Timestamp: `2026-01-20` -- Explorer URL: `https://solscan.io/tx/xagREaP2vDJKPG5x5Gqb3t44HJvgMBksGBoMmk6JK7CMBVZ9x6BhoCYKJ1X8dHdju97pudj7cN1apmZrWQ3fxKB?cluster=devnet` -- Notes: USDC added successfully - ---- - -### ✅ Step 4.2: Verify USDC Added -**Command:** -```bash -yarn ts-node scripts/verify-pool.ts -``` - -**Result:** -- [x] Completed -- Token in Supported List: `Yes` -- Vault Exists: `Yes` -- Vault Token Account Exists: `Yes` -- Fee Recipient ATA Created: `Yes` -- Notes: Pool has 1 supported token - ---- - -## PHASE 5: Add USDF (Devnet) - -### ✅ Step 5.1: Add Devnet USDF to Pool -**Note:** We use the same `02-add-token.ts` script for all tokens. - -**Command:** -```bash -yarn ts-node scripts/02-add-token.ts Ejgz8rRPySWomDUnsYr1gNpHYm6ZBQnjqbAPfi1dbbwQ -``` - -**Result:** -- [x] Completed -- Transaction Signature: `5i1eaaQM2qVGhYn81j2b8L1Gnj6kwFuE71ZRPdwq1ditDxtQ4NuwQXEA1ffhKyHPeDDnQ6GiqRyThYgeX3vKnFQG` -- USDF Vault PDA: `3g3UUaL6y6AjrQuc2cmJHbJmi1XWhEK7VqGrY4CL2Lgn` -- USDF Vault Token Account: `pH8X9mDMGEywYHH5fAprVE57LWaHtviwx1J6ZnhgJfm` -- Fee Recipient Token Account: `8scZbgPB2rWEsSbBtwiPYG77R5e591cLwEqAMiAHCuC1` -- Timestamp: `2026-01-20` -- Explorer URL: `https://solscan.io/tx/5i1eaaQM2qVGhYn81j2b8L1Gnj6kwFuE71ZRPdwq1ditDxtQ4NuwQXEA1ffhKyHPeDDnQ6GiqRyThYgeX3vKnFQG?cluster=devnet` -- Notes: USDF added successfully - ---- - -### ✅ Step 5.2: Verify USDF Added -**Command:** -```bash -yarn ts-node scripts/verify-pool.ts -``` - -**Result:** -- [x] Completed -- Token in Supported List: `Yes` -- Vault Exists: `Yes` -- Vault Token Account Exists: `Yes` -- Fee Recipient ATA Created: `Yes` -- Notes: Pool has 2 supported tokens - ---- - -## PHASE 6: Add SFTUSD25 (Devnet) - -### ✅ Step 6.1: Add Devnet SFTUSD25 to Pool -**Note:** We use the same `02-add-token.ts` script for all tokens. - -**Command:** -```bash -yarn ts-node scripts/02-add-token.ts AreQsswF44khJHLjsuWzgboJcG31JvALcWMkMcsVs2uC -``` - -**Result:** -- [x] Completed -- Transaction Signature: `3wUhgLRJHDnsUoTn4Ydi4ag13XKdHGqk7SLWkX9ivicruZ16ZPMHvQeQ3RdeGsNpA2d9RrZzFAnoiByAHswWikGb` -- SFTUSD25 Vault PDA: `Hon72MLLmPMqQTNphjCvE9Epwdh88S5tZYa1mtLwtn1c` -- SFTUSD25 Vault Token Account: `4M8SWnpq6nu7Ud4Qctq3w8HNTWBZP89tYQ27VwaqfvNo` -- Fee Recipient Token Account: `8w2c1QnSFHtSg5jzjN5GzaisjFb61mJgQSTdNEVPiiQo` -- Timestamp: `2026-01-20` -- Explorer URL: `https://solscan.io/tx/3wUhgLRJHDnsUoTn4Ydi4ag13XKdHGqk7SLWkX9ivicruZ16ZPMHvQeQ3RdeGsNpA2d9RrZzFAnoiByAHswWikGb?cluster=devnet` -- Notes: SFTUSD25 added successfully - ---- - -### ✅ Step 6.2: Verify SFTUSD25 Added -**Command:** -```bash -yarn ts-node scripts/verify-pool.ts -``` - -**Result:** -- [x] Completed -- Token in Supported List: `Yes` -- Vault Exists: `Yes` -- Vault Token Account Exists: `Yes` -- Fee Recipient ATA Created: `Yes` -- Notes: Pool has 3 supported tokens (USDC, USDF, SFTUSD25) - ---- - -## PHASE 7: Deposit Liquidity - -### ✅ Step 7.1: Check Token Balances -**Commands:** -```bash -# Check your custom token balance -spl-token balance - -# Check your USDC balance -spl-token balance 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU - -# Check your USDF balance -spl-token balance Ejgz8rRPySWomDUnsYr1gNpHYm6ZBQnjqbAPfi1dbbwQ - -# Check your SFTUSD25 balance -spl-token balance AreQsswF44khJHLjsuWzgboJcG31JvALcWMkMcsVs2uC -``` - -**Result:** -- [x] Completed -- Custom Token Balance: `N/A (skipped custom token)` -- USDC Balance: `1` -- USDF Balance: `10` -- SFTUSD25 Balance: `10` -- Notes: Decided to only deposit USDF and SFTUSD25, not USDC - ---- - -### ✅ Step 7.2: Deposit USDC Liquidity -**Planned Amount:** `0` USDC (Skipped for asymmetric liquidity testing) - -**Command:** -```bash -yarn ts-node scripts/03-deposit-liquidity.ts 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU - -# Example: Deposit 1000 USDC -yarn ts-node scripts/03-deposit-liquidity.ts 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU 1000 -``` - -**Result:** -- [x] Skipped -- Transaction Signature: `N/A` -- Amount Deposited: `0` USDC -- Vault Balance After: `0` -- Timestamp: `2026-01-20` -- Explorer URL: `N/A` -- Notes: Intentionally skipped USDC deposit to test asymmetric liquidity pool (only USDF and SFTUSD25 deposited) - ---- - -### ✅ Step 7.3: Deposit USDF Liquidity -**Planned Amount:** `10` USDF - -**Command:** -```bash -yarn ts-node scripts/03-deposit-liquidity.ts Ejgz8rRPySWomDUnsYr1gNpHYm6ZBQnjqbAPfi1dbbwQ - -# Example: Deposit 1000 USDF -yarn ts-node scripts/03-deposit-liquidity.ts Ejgz8rRPySWomDUnsYr1gNpHYm6ZBQnjqbAPfi1dbbwQ 1000 -``` - -**Result:** -- [x] Completed -- Transaction Signature: `Td2KqReYyavxBAzmP6xnaw52hqRDWFTRPPakfkExHctPjuqAnsa7xbKBtPtZ8jn598cFTZic8qte5zCpafV2RZ7` -- Amount Deposited: `10` USDF -- Vault Balance After: `10` -- Timestamp: `2026-01-20` -- Explorer URL: `https://solscan.io/tx/Td2KqReYyavxBAzmP6xnaw52hqRDWFTRPPakfkExHctPjuqAnsa7xbKBtPtZ8jn598cFTZic8qte5zCpafV2RZ7?cluster=devnet` -- Notes: Successfully deposited 10 USDF to vault - ---- - -### ✅ Step 7.4: Deposit SFTUSD25 Liquidity -**Planned Amount:** `10` SFTUSD25 - -**Command:** -```bash -yarn ts-node scripts/03-deposit-liquidity.ts AreQsswF44khJHLjsuWzgboJcG31JvALcWMkMcsVs2uC - -# Example: Deposit 1000 SFTUSD25 -yarn ts-node scripts/03-deposit-liquidity.ts AreQsswF44khJHLjsuWzgboJcG31JvALcWMkMcsVs2uC 1000 -``` - -**Result:** -- [x] Completed -- Transaction Signature: `47dH8PwZTdKLFjSDY6bxd9HLBfeUZxCYY26MspHEUf4yfa3YH3ef1N7xU75p6MRuhM74AbyqUneZJVNBpMgdcmQ7` -- Amount Deposited: `10` SFTUSD25 -- Vault Balance After: `10` -- Timestamp: `2026-01-20` -- Explorer URL: `https://solscan.io/tx/47dH8PwZTdKLFjSDY6bxd9HLBfeUZxCYY26MspHEUf4yfa3YH3ef1N7xU75p6MRuhM74AbyqUneZJVNBpMgdcmQ7?cluster=devnet` -- Notes: Successfully deposited 10 SFTUSD25 to vault - ---- - -## PHASE 8: Testing & Verification - -### ✅ Step 8.1: Verify Complete Pool State -**Command:** -```bash -yarn ts-node scripts/verify-pool.ts -``` - -**Result:** -- [ ] Completed -- Pool Initialized: `___________________` -- Supported Tokens: `5 (Custom Token, USDC, USDF, SFTUSD25, + any others)` -- USDC Vault Balance: `___________________` -- USDF Vault Balance: `___________________` -- SFTUSD25 Vault Balance: `___________________` -- Total Liquidity USD Value: `___________________` -- Notes: `___________________` - ---- - -### ✅ Step 8.2: Test Swap (USDC -> USDF) -**Test:** Small swap of 1 USDC -> USDF - -**Command:** -```bash -yarn ts-node scripts/04-test-swap.ts 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU Ejgz8rRPySWomDUnsYr1gNpHYm6ZBQnjqbAPfi1dbbwQ 1 -``` - -**Result:** -- [x] Completed -- Transaction Signature: `3Y12X8sn9ixE5rAK2YGa8UHUt4UcmLaqVJyRR7QvMYvCXypv3fH5mb9rdi267Q54SMvsEnNWjAB5TaY4soU8qKha` -- Input: `1` USDC -- Output: `1` USDF -- Exchange Rate: `1:1 (0% fee)` -- Timestamp: `2026-01-20` -- Explorer URL: `https://solscan.io/tx/3Y12X8sn9ixE5rAK2YGa8UHUt4UcmLaqVJyRR7QvMYvCXypv3fH5mb9rdi267Q54SMvsEnNWjAB5TaY4soU8qKha?cluster=devnet` -- Notes: Swap successful - pool now has 1 USDC, 9 USDF, 10 SFTUSD25 - ---- - -### ✅ Step 8.3: Test Reverse Swap (USDF -> USDC) -**Test:** Reverse swap of 1 USDF -> USDC - -**Command:** -```bash -yarn ts-node scripts/04-test-swap.ts Ejgz8rRPySWomDUnsYr1gNpHYm6ZBQnjqbAPfi1dbbwQ 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU 1 -``` - -**Result:** -- [ ] Completed -- Transaction Signature: `___________________` -- Input: `1` USDF -- Output: `___________________` USDC -- Exchange Rate: `___________________` -- Timestamp: `___________________` -- Explorer URL: `https://solscan.io/tx/?cluster=devnet` -- Notes: `___________________` - ---- - -### ✅ Step 8.4: Test Swap (USDC -> SFTUSD25) -**Test:** Small swap of 1 USDC -> SFTUSD25 - -**Command:** -```bash -yarn ts-node scripts/04-test-swap.ts 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU AreQsswF44khJHLjsuWzgboJcG31JvALcWMkMcsVs2uC 1 -``` - -**Result:** -- [ ] Completed -- Transaction Signature: `___________________` -- Input: `1` USDC -- Output: `___________________` SFTUSD25 -- Exchange Rate: `___________________` -- Timestamp: `___________________` -- Explorer URL: `https://solscan.io/tx/?cluster=devnet` -- Notes: `___________________` - ---- - -### ✅ Step 8.5: Test Swap (SFTUSD25 -> USDC) -**Test:** Reverse swap of 1 SFTUSD25 -> USDC - -**Command:** -```bash -yarn ts-node scripts/04-test-swap.ts AreQsswF44khJHLjsuWzgboJcG31JvALcWMkMcsVs2uC 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU 1 -``` - -**Result:** -- [ ] Completed -- Transaction Signature: `___________________` -- Input: `1` SFTUSD25 -- Output: `___________________` USDC -- Exchange Rate: `___________________` -- Timestamp: `___________________` -- Explorer URL: `https://solscan.io/tx/?cluster=devnet` -- Notes: `___________________` - ---- - -### ✅ Step 8.6: Test Swap (USDF -> SFTUSD25) -**Test:** Cross-stablecoin swap of 1 USDF -> SFTUSD25 - -**Command:** -```bash -yarn ts-node scripts/04-test-swap.ts Ejgz8rRPySWomDUnsYr1gNpHYm6ZBQnjqbAPfi1dbbwQ AreQsswF44khJHLjsuWzgboJcG31JvALcWMkMcsVs2uC 1 -``` - -**Result:** -- [x] Completed -- Transaction Signature: `4MPaC22MHHP8mXPNPKDEP3ryhNywpJ84UJJqEKAFHSG3SKtbgWcWfQDQhGAdcAMtijxAMmAgARbvhwD5VXGCS3Ay` -- Input: `1` USDF -- Output: `1` SFTUSD25 -- Exchange Rate: `1:1 (0% fee)` -- Timestamp: `2026-01-20` -- Explorer URL: `https://solscan.io/tx/4MPaC22MHHP8mXPNPKDEP3ryhNywpJ84UJJqEKAFHSG3SKtbgWcWfQDQhGAdcAMtijxAMmAgARbvhwD5VXGCS3Ay?cluster=devnet` -- Notes: Swap successful - pool now has 1 USDC, 10 USDF, 9 SFTUSD25 - ---- - -### ✅ Step 8.7: Test Swap (SFTUSD25 -> USDF) -**Test:** Cross-stablecoin swap of 1 SFTUSD25 -> USDF - -**Command:** -```bash -yarn ts-node scripts/04-test-swap.ts AreQsswF44khJHLjsuWzgboJcG31JvALcWMkMcsVs2uC Ejgz8rRPySWomDUnsYr1gNpHYm6ZBQnjqbAPfi1dbbwQ 1 -``` - -**Result:** -- [ ] Completed -- Transaction Signature: `___________________` -- Input: `1` SFTUSD25 -- Output: `___________________` USDF -- Exchange Rate: `___________________` -- Timestamp: `___________________` -- Explorer URL: `https://solscan.io/tx/?cluster=devnet` -- Notes: `___________________` - ---- - -## PHASE 9: Transfer Authorities - -### ✅ Step 9.1: Transfer Operations Authority - -**Command:** -```bash -yarn ts-node scripts/update-operations-authority.ts 2yRhUBaydvJzkyVYpFtMBrWZWCnxdYRQtJmqtH2ugtzS -``` - -**Result:** -- [x] Completed -- Transaction Signature: `pzuGXvAorujrzPmRAtQGoKBtd1hoitvV43kiNFSDMaUSKUXd4UVoZL5RNxAthYwqTWtnU8Tg5EExgSCdhM6Mn8n` -- Old Operations Authority: `13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD` -- New Operations Authority: `2yRhUBaydvJzkyVYpFtMBrWZWCnxdYRQtJmqtH2ugtzS` -- Timestamp: `2026-01-20` -- Explorer URL: `https://solscan.io/tx/pzuGXvAorujrzPmRAtQGoKBtd1hoitvV43kiNFSDMaUSKUXd4UVoZL5RNxAthYwqTWtnU8Tg5EExgSCdhM6Mn8n?cluster=devnet` -- Notes: Operations authority successfully transferred - ---- - -### ✅ Step 9.2: Transfer Pause Authority - -**Command:** -```bash -yarn ts-node scripts/update-pause-authority.ts CC2ymqGpoSjk2estBTC4iYzuxooqSamnoYR2KGo8CYM6 -``` - -**Result:** -- [x] Completed -- Transaction Signature: `4sCtcnni1CaHVEodqsKWvrcst4gR6oAjAhZCuMrrB3MqjcB5A2QLU6y3Vmx5j5WSgTjXLUDsZVAcYmus67e55eb` -- Old Pause Authority: `13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD` -- New Pause Authority: `CC2ymqGpoSjk2estBTC4iYzuxooqSamnoYR2KGo8CYM6` -- Timestamp: `2026-01-20` -- Explorer URL: `https://solscan.io/tx/4sCtcnni1CaHVEodqsKWvrcst4gR6oAjAhZCuMrrB3MqjcB5A2QLU6y3Vmx5j5WSgTjXLUDsZVAcYmus67e55eb?cluster=devnet` -- Notes: Pause authority successfully transferred - ---- - -### ✅ Step 9.3: Verify Authority Transfers - -**Command:** -```bash -yarn ts-node scripts/verify-pool.ts -``` - -**Result:** -- [x] Completed -- Operations Authority: `2yRhUBaydvJzkyVYpFtMBrWZWCnxdYRQtJmqtH2ugtzS` ✓ -- Pause Authority: `CC2ymqGpoSjk2estBTC4iYzuxooqSamnoYR2KGo8CYM6` ✓ -- Fee Recipient: `13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD` (unchanged) -- Notes: All authorities verified successfully - ---- - -## PHASE 10: Optional - Publish IDL to Devnet - -### ✅ Step 10.1: Publish IDL - -**Command:** -```bash -anchor idl init 9vDwZVJXw5nxymWmUcgmNpemDH5EBcJwLNhtsznrgJDH --filepath target/idl/scaas_liquidity.json --provider.cluster devnet -``` - -**Result:** -- [x] Completed -- IDL Account Created: `A86H6NXnycB7WPRtPFc9yL5pQJNFdtKSziGMNkNHorH1` -- IDL Data Length: `3352 bytes` -- Timestamp: `2026-01-20` -- Notes: IDL successfully published to devnet - ---- - -### ✅ Step 10.2: Verify IDL Published - -**Command:** -```bash -anchor idl fetch 9vDwZVJXw5nxymWmUcgmNpemDH5EBcJwLNhtsznrgJDH --provider.cluster devnet -``` - -**Result:** -- [x] Completed -- IDL Publicly Accessible: `Yes` -- Notes: IDL fetch successful - publicly accessible on devnet - ---- - -## Post-Deployment Summary - -### Total Costs -- Program Deployment: `~3.22` SOL -- Pool Initialization: `~0.01` SOL -- Add USDC: `~0.01` SOL -- Add USDF: `~0.01` SOL -- Add SFTUSD25: `~0.01` SOL -- Deposit USDF: `~0.0001` SOL -- Deposit SFTUSD25: `~0.0001` SOL -- Test Swaps (2 swaps): `~0.0002` SOL -- Transfer Authorities (2 txs): `~0.0002` SOL -- Publish IDL: `~0.02` SOL -- **Total:** `~3.27` SOL (all from devnet faucet) - -### Critical Addresses (SAVE THESE!) -``` -Network: Devnet -Program ID: 9vDwZVJXw5nxymWmUcgmNpemDH5EBcJwLNhtsznrgJDH -Pool PDA: 68xwTJYRyUdULbb5ve4JMWTc2xvw9wQmtEj9GRoHSenF -IDL Account: A86H6NXnycB7WPRtPFc9yL5pQJNFdtKSziGMNkNHorH1 - -Operations Authority: 2yRhUBaydvJzkyVYpFtMBrWZWCnxdYRQtJmqtH2ugtzS -Pause Authority: CC2ymqGpoSjk2estBTC4iYzuxooqSamnoYR2KGo8CYM6 -Fee Recipient: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD - -USDC Mint (Devnet): 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU -USDC Vault: DHCU6cSXeEwk5GUhGVw7XkNfcWF6bY2gsUbtUEUitHhY -USDC Vault Token Account: 7hktVUoYS9q1c6iq5cnNVmKths3kJ1fJQkozvo4vpHR9 - -USDF Mint (Devnet): Ejgz8rRPySWomDUnsYr1gNpHYm6ZBQnjqbAPfi1dbbwQ -USDF Vault: 3g3UUaL6y6AjrQuc2cmJHbJmi1XWhEK7VqGrY4CL2Lgn -USDF Vault Token Account: pH8X9mDMGEywYHH5fAprVE57LWaHtviwx1J6ZnhgJfm - -SFTUSD25 Mint (Devnet): AreQsswF44khJHLjsuWzgboJcG31JvALcWMkMcsVs2uC -SFTUSD25 Vault: Hon72MLLmPMqQTNphjCvE9Epwdh88S5tZYa1mtLwtn1c -SFTUSD25 Vault Token Account: 4M8SWnpq6nu7Ud4Qctq3w8HNTWBZP89tYQ27VwaqfvNo -``` - -### Explorer Links -``` -Program: https://solscan.io/account/9vDwZVJXw5nxymWmUcgmNpemDH5EBcJwLNhtsznrgJDH?cluster=devnet -Pool: https://solscan.io/account/68xwTJYRyUdULbb5ve4JMWTc2xvw9wQmtEj9GRoHSenF?cluster=devnet -IDL: https://solscan.io/account/A86H6NXnycB7WPRtPFc9yL5pQJNFdtKSziGMNkNHorH1?cluster=devnet -``` - -### Deployment Status -- ✅ Program deployed to devnet -- ✅ Pool initialized with 0% fee -- ✅ Three tokens added (USDC, USDF, SFTUSD25) -- ✅ Liquidity deposited (asymmetric: USDF=10, SFTUSD25=10, USDC=0 initially) -- ✅ Swaps tested (USDC→USDF, USDF→SFTUSD25) -- ✅ Authorities transferred -- ✅ IDL published to devnet - -### Next Steps (Optional) -- [ ] Test additional swap scenarios -- [ ] Test pause/unpause functionality with new authorities -- [ ] Add more liquidity if needed -- [ ] Monitor pool performance -- [ ] Prepare for mainnet deployment when ready - ---- - -## Testing Scenarios Checklist - -### Basic Functionality - Stablecoin Swaps -- [x] Swap USDC -> USDF (small amount) - 1 USDC → 1 USDF ✓ -- [ ] Swap USDF -> USDC (small amount) -- [ ] Swap USDC -> SFTUSD25 (small amount) -- [ ] Swap SFTUSD25 -> USDC (small amount) -- [x] Swap USDF -> SFTUSD25 (small amount) - 1 USDF → 1 SFTUSD25 ✓ -- [ ] Swap SFTUSD25 -> USDF (small amount) -- [ ] Swap USDC -> USDF (large amount) -- [ ] Swap USDC -> SFTUSD25 (large amount) -- [x] Verify fee collection (0% fee - no fees collected) ✓ - -### Liquidity Operations -- [x] Deposit USDC liquidity - Skipped (asymmetric pool) ✓ -- [x] Deposit USDF liquidity - 10 tokens ✓ -- [x] Deposit SFTUSD25 liquidity - 10 tokens ✓ -- [ ] Withdraw USDC liquidity -- [ ] Withdraw USDF liquidity -- [ ] Withdraw SFTUSD25 liquidity - -### Security & Controls -- [ ] Pause swaps -- [ ] Unpause swaps -- [ ] Pause liquidity operations -- [ ] Unpause liquidity operations -- [ ] Update fee rate -- [x] Update authorities - Operations & Pause authorities transferred ✓ - -### Edge Cases -- [ ] Insufficient liquidity error -- [ ] Slippage exceeded error -- [ ] Maximum swap amount -- [ ] Minimum swap amount - ---- - -## Emergency Commands - -### Pause Swaps -```bash -yarn ts-node scripts/emergency-pause-swaps.ts -``` - -### Pause Liquidity Operations -```bash -yarn ts-node scripts/emergency-pause-liquidity.ts -``` - -### Withdraw Emergency Liquidity -```bash -yarn ts-node scripts/emergency-withdraw.ts -``` - -### Resume Operations -```bash -yarn ts-node scripts/unpause-swaps.ts -yarn ts-node scripts/unpause-liquidity.ts -``` - ---- - -## Notes & Issues Encountered - -### Issue Log -1. `___________________` -2. `___________________` -3. `___________________` - -### Resolutions -1. `___________________` -2. `___________________` -3. `___________________` - -### Lessons Learned -1. `___________________` -2. `___________________` -3. `___________________` - ---- - -## Differences from Mainnet Deployment - -| Aspect | Mainnet | Devnet | -|--------|---------|--------| -| RPC URL | `https://api.mainnet-beta.solana.com` | `https://api.devnet.solana.com` | -| USDC Mint | `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v` | `4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU` | -| SOL Cost | Real SOL (~4-5 SOL) | Free from faucet | -| Explorer | `solscan.io/tx/` | `solscan.io/tx/?cluster=devnet` | -| Risk Level | High (real funds) | Low (test tokens) | -| Purpose | Production | Testing/Development | - ---- - -**Deployment Status:** ⏳ Not Started - -**Started Date:** `___________________` - -**Completion Date:** `___________________` - -**Deployed By:** `___________________` - -**Verified By:** `___________________` diff --git a/solana/runbooks/MAINNET_DEPLOYMENT-20251219.md b/solana/runbooks/MAINNET_DEPLOYMENT-20251219.md deleted file mode 100644 index 7c4d516..0000000 --- a/solana/runbooks/MAINNET_DEPLOYMENT-20251219.md +++ /dev/null @@ -1,2243 +0,0 @@ -# Mainnet Deployment Runbook - Liquidity Pool Program - -**Status:** In Progress -**Date Started:** 2025-12-19 -**Deployer:** Saliou Diallo - ---- - -## Overview - -This runbook guides you through deploying the liquidity pool program to Solana mainnet and initializing it with your custom token and USDC. - -**Program ID:** `GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv` - ---- - -## Key Information - -### Addresses -- **Deployer Wallet:** `13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD` -- **Custom Token Mint:** `9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms` -- **Custom Token Decimals:** `6` -- **USDC Mint:** `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v` -- **Pool PDA:** `5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh` -- **Custom Token Vault:** `EPFgtsNL4VVqwbh6cPwFoWCoqjW1WQvgbRAcitV49HYH` -- **Custom Token Vault Token Account:** `3y6MPgBvQqCS5EJkWE8w6dYwVLT3sue4s6inP8AoAkJv` -- **USDC Vault:** `GZHACYSwd3TWc4AVjWnHVwudVXrXWdh6qqKhgGcf1aFx` -- **USDC Vault Token Account:** `9XX1dwrvpxAqzZ8jEbE8NMmTA7nGK2eVApuoXJxPGrfS` - -### Configuration -- **Fee Rate:** 0 basis points (0% - for 1:1 swaps) -- **Operations Authority:** Deployer wallet -- **Pause Authority:** Deployer wallet -- **Fee Recipient:** Deployer wallet - ---- - -## PHASE 1: Pre-Deployment Setup - -Install `solana`, `anchor`, and `spl-token`. - -```bash -❯ solana --version -solana-cli 2.2.21 (src:23e01995; feat:3073396398, client:Agave) -❯ anchor --version -anchor-cli 0.31.1 -❯ spl-token --version -spl-token-cli 5.3.0 -``` - -### ✅ Step 1.1: Configure Environment to Solana Mainnet -**Command:** -```bash -solana config set -u m -``` - ---- - -### ✅ Step 1.2: Get Wallet Address -**Command:** -```bash -solana address -``` - -**Result:** -- [x] Completed -- Wallet Address: `13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD` - -Fund your wallet with enough SOL if needed. You will likely need about 4-5 SOL. - ---- - -### ✅ Step 1.3: Check SOL Balance -**Command:** -```bash -solana balance -``` - -**Expected:** At least 4-5 SOL - -**Result:** -- [x] Completed -- Balance: `5` SOL -- Notes: `___________________` - ---- - -### ✅ Step 1.4: Build the Program -**Command:** -```bash -anchor clean -anchor build -``` - ---- - -### ✅ Step 1.5: Verify Program Build -**Command:** -```bash -# Check that the program is built -ls -lh target/deploy/scaas_liquidity.so - -# Verify program keypair matches expected ID (should match Anchor.toml) -solana address -k target/deploy/scaas_liquidity-keypair.json -``` - -**Expected:** `GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv` - -**Result:** -- [x] Completed -- Program Size: `452K` -- Program ID Matches: `Yes` -- Notes: `___________________` - ---- - -### ✅ Step 1.6: Verify Custom Token Details -**Commands:** -```bash -# Check your token mint address -spl-token accounts - -# Get specific token balance and verify decimals -spl-token account-info - -# If you have no token account for the token mint, you may create it with -spl-token create-account -``` - -**Result:** -- [ ] Completed -- Custom Token Mint: `9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms` -- Custom Token Decimals: `6` -- Your Token Account: `A7vP6Ut36Tmc94ACNBPKWjfiPZPeSsadw8jYqSbKEdBk` -- Your Token Balance: `0` -- Notes: `___________________` - ---- - -## PHASE 2: Program Deployment - -### ✅ Step 2.1: Deploy Program to Mainnet -**Command:** -```bash -anchor deploy --provider.cluster mainnet -``` - -**Result:** -- [x] Completed -- Transaction Signature: `4hstLhbDQzxZ1d5WxZ1tn75tgG2GPhJ6q5jTy5qGkrZMBE3mXrjh14DB4EDegQvrxmDq8N1FEjHyTpq3HWMwrWYf` -- Program ID: `GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv` -- Deployment Cost: `3.226759396` SOL -- Timestamp: `Dec 19, 2025 at 22:17:38 Eastern Standard Time` -- Notes: `___________________` - ---- - -### ✅ Step 2.2: Verify Deployment -**Command:** -```bash -solana program show GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -``` - -**Result:** -- [x] Completed -- Program Exists: `Yes` -- **Program Id**: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- **Owner**: BPFLoaderUpgradeab1e11111111111111111111111 -- **ProgramData Address**: FVqHqZmj6WCBxhHC1k3P8J8YyoYW2weJ8yUw57a36zWz -- **Authority**: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- **Last Deployed In Slot**: 387881099 -- **Data Length**: 462928 (0x71050) bytes -- **Balance**: 3.22318296 SOL -- Notes: `___________________` - ---- - -## PHASE 3: Initialize Pool - -### ✅ Step 3.1: Install TypeScript Dependencies -**Command:** -```bash -yarn add -D ts-node typescript @types/node -``` - -**Result:** -- [x] Completed -- Notes: `Installed successfully` - ---- - -### ✅ Step 3.2: Set Environment Variables (Optional) -**Command:** -```bash -# Export these in your shell (or add to ~/.bashrc or ~/.zshrc) -export ANCHOR_PROVIDER_URL="https://api.mainnet-beta.solana.com" -export ANCHOR_WALLET="$HOME/.config/solana/id.json" -``` - -**Note:** The scripts will use these defaults if not set, so this step is optional. But it's good practice to set them explicitly. - -**Result:** -- [ ] Completed -- Notes: `___________________` - ---- - -### ✅ Step 3.3: Create Initialization Script -**File:** `scripts/01-initialize-pool.ts` - -Created at `scripts/01-initialize-pool.ts`. - -**Usage:** -```bash -yarn ts-node scripts/01-initialize-pool.ts -``` - -**Arguments:** -- `FEE_RATE_BPS`: Fee rate in basis points (0-1000) - - 0 = 0% fee (1:1 swaps) - - 10 = 0.1% fee - - 100 = 1% fee - - 1000 = 10% fee (maximum) - -**Result:** -- [x] Completed -- Script Created: `Yes` -- Notes: `Script accepts fee rate as command-line argument` - ---- - -### ✅ Step 3.4: Run Pool Initialization -**Command:** -```bash -# For 0% fee (1:1 swaps) -yarn ts-node scripts/01-initialize-pool.ts 0 - -# Or for 1% fee -yarn ts-node scripts/01-initialize-pool.ts 100 -``` - -**Result:** -- [x] Completed -- Fee Rate Used: `0` bps (0%) -- Transaction Signature: `G9jf2bis2HzXBg1zZkdwpWUTgAe442NQgBnM4nc8kUSnBcz3iaa8Goz1zvWZZnomrAGrWFSqbaYm8RZz4xt7pMX` -- Explorer: https://solscan.io/tx/G9jf2bis2HzXBg1zZkdwpWUTgAe442NQgBnM4nc8kUSnBcz3iaa8Goz1zvWZZnomrAGrWFSqbaYm8RZz4xt7pMX -- Pool PDA: `5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh` -- Timestamp: `2025-12-19` -- Notes: `Successfully initialized with 0% fee for 1:1 swaps` - -``` -============================================================ -INITIALIZING LIQUIDITY POOL -============================================================ - -Configuration: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh -- Deployer/Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD - -✓ Pool not yet initialized, proceeding... - -Initialization Parameters: -- Fee Rate: 0 basis points (0%) -- Operations Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Pause Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Fee Recipient: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD - -Sending transaction... -✅ Pool initialized successfully! - -Transaction Details: -- Signature: G9jf2bis2HzXBg1zZkdwpWUTgAe442NQgBnM4nc8kUSnBcz3iaa8Goz1zvWZZnomrAGrWFSqbaYm8RZz4xt7pMX -- Explorer: https://solscan.io/tx/G9jf2bis2HzXBg1zZkdwpWUTgAe442NQgBnM4nc8kUSnBcz3iaa8Goz1zvWZZnomrAGrWFSqbaYm8RZz4xt7pMX - -Pool State: -- Operations Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Pause Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Fee Recipient: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Fee Rate: 0 bps -- Swaps Paused: false -- Liquidity Paused: false -- Supported Tokens: 0 - - -============================================================ -SAVE THESE ADDRESSES: -============================================================ -Pool: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh -============================================================ -✨ Done in 6.50s. -``` - ---- - -### ✅ Step 3.5: Verify Pool State -**Command:** -```bash -yarn ts-node scripts/verify-pool.ts -``` - -**Result:** -- [x] Completed -- Operations Authority: `13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD` -- Pause Authority: `13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD` -- Fee Recipient: `13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD` -- Fee Rate: `0` bps (0%) -- Swaps Paused: `false` -- Liquidity Paused: `false` -- Supported Tokens Count: `0` -- Notes: `Pool successfully initialized and verified` - -``` -============================================================ -POOL STATE VERIFICATION -============================================================ - -Addresses: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh - -Pool Configuration: -- Operations Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Pause Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Fee Recipient: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Fee Rate: 0 bps (0%) -- Swaps Paused: false -- Liquidity Paused: false - -Supported Tokens: -- Count: 0 - - -============================================================ -✅ Pool verification complete! -============================================================ -✨ Done in 1.25s. -``` - ---- - -## PHASE 4: Add Custom Token - -### ✅ Step 4.1: Create Add Token Script -**File:** `scripts/02-add-token.ts` - -Create this TypeScript script to add your custom token. - -**Result:** -- [x] Completed -- Script Created: `Yes` -- Notes: `___________________` - ---- - -### ✅ Step 4.2: Add Custom Token to Pool -**Command:** -```bash -yarn ts-node scripts/02-add-token.ts 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms -``` - -**Result:** -- [x] Completed -- Transaction Signature: `pCLMbQkPc7QeTqhfxE7Quztmh7LPvGw11hguM5AQGZa5jX32G7cbSPVGL1y7RBUz3JPgS65DgXDkCDLnknRk3Ad` -- Explorer: https://solscan.io/tx/pCLMbQkPc7QeTqhfxE7Quztmh7LPvGw11hguM5AQGZa5jX32G7cbSPVGL1y7RBUz3JPgS65DgXDkCDLnknRk3Ad -- Custom Token Vault PDA: `EPFgtsNL4VVqwbh6cPwFoWCoqjW1WQvgbRAcitV49HYH` -- Custom Token Vault Token Account: `3y6MPgBvQqCS5EJkWE8w6dYwVLT3sue4s6inP8AoAkJv` -- Fee Recipient Token Account: `A7vP6Ut36Tmc94ACNBPKWjfiPZPeSsadw8jYqSbKEdBk` -- Timestamp: `2025-12-19` -- Notes: `Successfully added custom token to pool` - -``` -============================================================ -ADDING TOKEN TO POOL -============================================================ - -Configuration: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh -- Token Mint: 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms -- Vault PDA: EPFgtsNL4VVqwbh6cPwFoWCoqjW1WQvgbRAcitV49HYH -- Vault Token Account: 3y6MPgBvQqCS5EJkWE8w6dYwVLT3sue4s6inP8AoAkJv -- Fee Recipient Token Account: A7vP6Ut36Tmc94ACNBPKWjfiPZPeSsadw8jYqSbKEdBk -- Operations Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD - -✓ Token not yet added, proceeding... - -Sending transaction... -✅ Token added successfully! - -Transaction Details: -- Signature: pCLMbQkPc7QeTqhfxE7Quztmh7LPvGw11hguM5AQGZa5jX32G7cbSPVGL1y7RBUz3JPgS65DgXDkCDLnknRk3Ad -- Explorer: https://solscan.io/tx/pCLMbQkPc7QeTqhfxE7Quztmh7LPvGw11hguM5AQGZa5jX32G7cbSPVGL1y7RBUz3JPgS65DgXDkCDLnknRk3Ad - -Updated Pool State: -- Supported Tokens Count: 1 -- Supported Tokens: - 1. 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms - -============================================================ -SAVE THESE ADDRESSES: -============================================================ -Token Mint: 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms -Vault: EPFgtsNL4VVqwbh6cPwFoWCoqjW1WQvgbRAcitV49HYH -Vault Token Account: 3y6MPgBvQqCS5EJkWE8w6dYwVLT3sue4s6inP8AoAkJv -Fee Recipient Token Account: A7vP6Ut36Tmc94ACNBPKWjfiPZPeSsadw8jYqSbKEdBk -============================================================ -✨ Done in 2.74s. -``` - ---- - -### ✅ Step 4.3: Verify Custom Token Added -**Result:** -- [x] Completed -- Token in Supported List: `Yes (1 of 1 tokens)` -- Vault Exists: `Yes - EPFgtsNL4VVqwbh6cPwFoWCoqjW1WQvgbRAcitV49HYH` -- Vault Token Account Exists: `Yes - 3y6MPgBvQqCS5EJkWE8w6dYwVLT3sue4s6inP8AoAkJv` -- Fee Recipient ATA Created: `Yes - A7vP6Ut36Tmc94ACNBPKWjfiPZPeSsadw8jYqSbKEdBk` -- Notes: `Token successfully added and verified` - ---- - -## PHASE 5: Add USDC - -### ✅ Step 5.1: Add USDC to Pool -**Note:** We use the same `02-add-token.ts` script for all tokens. - -**Command:** -```bash -yarn ts-node scripts/02-add-token.ts EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v -``` - -**Result:** -- [x] Completed -- Transaction Signature: `67m6aUiSdbvskrKy55mz73VyFSwnuWxbd5CtRtetXGr4yi9Net2X9c5ehwqpMPz7jVXxoApF4ncXx8AdnyV3jRqU` -- Explorer: https://solscan.io/tx/67m6aUiSdbvskrKy55mz73VyFSwnuWxbd5CtRtetXGr4yi9Net2X9c5ehwqpMPz7jVXxoApF4ncXx8AdnyV3jRqU -- USDC Vault PDA: `GZHACYSwd3TWc4AVjWnHVwudVXrXWdh6qqKhgGcf1aFx` -- USDC Vault Token Account: `9XX1dwrvpxAqzZ8jEbE8NMmTA7nGK2eVApuoXJxPGrfS` -- Fee Recipient Token Account: `7vqUNuSd8jYFcbV3RXMyHFTV8kZx2uUVFVSCSQCoQQgw` -- Timestamp: `2025-12-19` -- Notes: `Successfully added USDC to pool` - -``` -============================================================ -ADDING TOKEN TO POOL -============================================================ - -Configuration: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh -- Token Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v -- Vault PDA: GZHACYSwd3TWc4AVjWnHVwudVXrXWdh6qqKhgGcf1aFx -- Vault Token Account: 9XX1dwrvpxAqzZ8jEbE8NMmTA7nGK2eVApuoXJxPGrfS -- Fee Recipient Token Account: 7vqUNuSd8jYFcbV3RXMyHFTV8kZx2uUVFVSCSQCoQQgw -- Operations Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD - -✓ Token not yet added, proceeding... - -Sending transaction... -✅ Token added successfully! - -Transaction Details: -- Signature: 67m6aUiSdbvskrKy55mz73VyFSwnuWxbd5CtRtetXGr4yi9Net2X9c5ehwqpMPz7jVXxoApF4ncXx8AdnyV3jRqU -- Explorer: https://solscan.io/tx/67m6aUiSdbvskrKy55mz73VyFSwnuWxbd5CtRtetXGr4yi9Net2X9c5ehwqpMPz7jVXxoApF4ncXx8AdnyV3jRqU - -Updated Pool State: -- Supported Tokens Count: 2 -- Supported Tokens: - 1. 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms - 2. EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v - -============================================================ -SAVE THESE ADDRESSES: -============================================================ -Token Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v -Vault: GZHACYSwd3TWc4AVjWnHVwudVXrXWdh6qqKhgGcf1aFx -Vault Token Account: 9XX1dwrvpxAqzZ8jEbE8NMmTA7nGK2eVApuoXJxPGrfS -Fee Recipient Token Account: 7vqUNuSd8jYFcbV3RXMyHFTV8kZx2uUVFVSCSQCoQQgw -============================================================ -✨ Done in 8.81s. -``` - ---- - -### ✅ Step 5.2: Verify USDC Added -**Result:** -- [x] Completed -- Token in Supported List: `Yes (2 of 2 tokens)` -- Vault Exists: `Yes - GZHACYSwd3TWc4AVjWnHVwudVXrXWdh6qqKhgGcf1aFx` -- Vault Token Account Exists: `Yes - 9XX1dwrvpxAqzZ8jEbE8NMmTA7nGK2eVApuoXJxPGrfS` -- Fee Recipient ATA Created: `Yes - 7vqUNuSd8jYFcbV3RXMyHFTV8kZx2uUVFVSCSQCoQQgw` -- Notes: `USDC successfully added. Pool now has 2 tokens.` - -``` -============================================================ -POOL STATE VERIFICATION -============================================================ - -Addresses: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh - -Pool Configuration: -- Operations Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Pause Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Fee Recipient: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Fee Rate: 0 bps (0%) -- Swaps Paused: false -- Liquidity Paused: false - -Supported Tokens: -- Count: 2 - 1. 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms - 2. EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v - - -============================================================ -✅ Pool verification complete! -============================================================ -✨ Done in 1.56s. -``` - ---- - -## PHASE 6: Deposit Liquidity - -### ✅ Step 6.1: Check Token Balances -**Commands:** -```bash -# Check your custom token balance -spl-token balance 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms - -# Check your USDC balance -spl-token balance EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v -``` - -Fund your wallet with your custom token and USDC, e.g. 10 of each. - -**Result:** -- [x] Completed -- Custom Token Balance: `20` -- USDC Balance: `10` -- Notes: `___________________` - ---- - -### ✅ Step 6.2: Deposit Custom Token Liquidity -**Planned Amount:** `5` tokens - -**Note:** We use the universal `03-deposit-liquidity.ts` script for all tokens. - -**Command:** -```bash -yarn ts-node scripts/03-deposit-liquidity.ts 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms 5 -``` - -**Result:** -- [x] Completed -- Transaction Signature: `5f31q7pFpad95eZBwR6CP7KgNMH1TZKWCdt93A6gsergYnsYACj4fuYy7JJzY6uAHJU4sR6uzmXM9sCzpHdM2FMZ` -- Explorer: https://solscan.io/tx/5f31q7pFpad95eZBwR6CP7KgNMH1TZKWCdt93A6gsergYnsYACj4fuYy7JJzY6uAHJU4sR6uzmXM9sCzpHdM2FMZ -- Amount Deposited: `5` tokens -- Vault Balance After: `5` tokens -- Timestamp: `2025-12-19` -- Notes: `Successfully deposited 5 custom tokens` - -``` -============================================================ -DEPOSITING LIQUIDITY -============================================================ - -Configuration: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh -- Token Mint: 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms -- Token Decimals: 6 -- Vault PDA: EPFgtsNL4VVqwbh6cPwFoWCoqjW1WQvgbRAcitV49HYH -- Vault Token Account: 3y6MPgBvQqCS5EJkWE8w6dYwVLT3sue4s6inP8AoAkJv -- Operations Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Operations Authority Token Account: A7vP6Ut36Tmc94ACNBPKWjfiPZPeSsadw8jYqSbKEdBk - -Deposit Details: -- Amount (tokens): 5 -- Amount (base units): 5000000 - -Your Balance: 20 tokens -Vault Balance Before: 0 tokens - -Sending transaction... -✅ Liquidity deposited successfully! - -Transaction Details: -- Signature: 5f31q7pFpad95eZBwR6CP7KgNMH1TZKWCdt93A6gsergYnsYACj4fuYy7JJzY6uAHJU4sR6uzmXM9sCzpHdM2FMZ -- Explorer: https://solscan.io/tx/5f31q7pFpad95eZBwR6CP7KgNMH1TZKWCdt93A6gsergYnsYACj4fuYy7JJzY6uAHJU4sR6uzmXM9sCzpHdM2FMZ - -Vault Balance After: 5 tokens -Amount Deposited: 5 tokens - -============================================================ -✅ Deposit complete! -============================================================ -✨ Done in 2.35s. -``` - ---- - -### ✅ Step 6.3: Deposit USDC Liquidity -**Planned Amount:** `5` USDC - -**Command:** -```bash -yarn ts-node scripts/03-deposit-liquidity.ts EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v 5 -``` - -**Result:** -- [x] Completed -- Transaction Signature: `5hmVPYTq8DxCtiMV3MeSJnTH7A6ctq2EqzucL592wTWq2nhs4Dk4rnVcjRtQwnDkkRK3dopfDhkA987eZhoLE1mY` -- Explorer: https://solscan.io/tx/5hmVPYTq8DxCtiMV3MeSJnTH7A6ctq2EqzucL592wTWq2nhs4Dk4rnVcjRtQwnDkkRK3dopfDhkA987eZhoLE1mY -- Amount Deposited: `5` USDC -- Vault Balance After: `5` USDC -- Timestamp: `2025-12-19` -- Notes: `Successfully deposited 5 USDC` - -``` -============================================================ -DEPOSITING LIQUIDITY -============================================================ - -Configuration: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh -- Token Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v -- Token Decimals: 6 -- Vault PDA: GZHACYSwd3TWc4AVjWnHVwudVXrXWdh6qqKhgGcf1aFx -- Vault Token Account: 9XX1dwrvpxAqzZ8jEbE8NMmTA7nGK2eVApuoXJxPGrfS -- Operations Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Operations Authority Token Account: 7vqUNuSd8jYFcbV3RXMyHFTV8kZx2uUVFVSCSQCoQQgw - -Deposit Details: -- Amount (tokens): 5 -- Amount (base units): 5000000 - -Your Balance: 10 tokens -Vault Balance Before: 0 tokens - -Sending transaction... -✅ Liquidity deposited successfully! - -Transaction Details: -- Signature: 5hmVPYTq8DxCtiMV3MeSJnTH7A6ctq2EqzucL592wTWq2nhs4Dk4rnVcjRtQwnDkkRK3dopfDhkA987eZhoLE1mY -- Explorer: https://solscan.io/tx/5hmVPYTq8DxCtiMV3MeSJnTH7A6ctq2EqzucL592wTWq2nhs4Dk4rnVcjRtQwnDkkRK3dopfDhkA987eZhoLE1mY - -Vault Balance After: 5 tokens -Amount Deposited: 5 tokens - -============================================================ -✅ Deposit complete! -============================================================ -✨ Done in 5.74s. -``` - ---- - -## PHASE 7: Final Verification - -### ✅ Step 7.1: Verify Complete Pool State -**Command:** -```bash -yarn ts-node scripts/verify-pool.ts -``` - -**Result:** -- [x] Completed -- Pool Initialized: `Yes` -- Supported Tokens: `2 (Custom Token + USDC)` -- Custom Token Vault Balance: `5 tokens` -- USDC Vault Balance: `5 USDC` -- Total Liquidity USD Value: `~$10 USD (assuming 1:1 with USDC)` -- Fee Rate: `0 bps (0%)` -- Swaps Paused: `false` -- Liquidity Paused: `false` -- Notes: `Pool fully operational and ready for swaps` - ---- - -### ✅ Step 7.2: Test Swap (Optional but Recommended) -**Test:** Small swap of 1 USDC -> Custom Token - -**Command:** -```bash -yarn ts-node scripts/04-test-swap.ts EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms 1 -``` - -**Result:** -- [x] Completed -- Transaction Signature: `4p9YnvLZPa2iW6ZkyNmgrrg8QnE8SEv8KgRXP85fQDVtSWqCLjXr8DHUt4DBVmnysdRBH7Ef9mH8ZVJBJzUAGppx` -- Explorer: https://solscan.io/tx/4p9YnvLZPa2iW6ZkyNmgrrg8QnE8SEv8KgRXP85fQDVtSWqCLjXr8DHUt4DBVmnysdRBH7Ef9mH8ZVJBJzUAGppx -- Input: `1` USDC -- Output: `1` Custom Token -- Exchange Rate: `1:1 (0% fee)` -- Balance Before: 5 USDC, 15 Custom Token -- Balance After: 4 USDC, 16 Custom Token -- Timestamp: `2025-12-19` -- Notes: `Swap executed perfectly at 1:1 rate with 0% fee` - -``` -============================================================ -TEST SWAP -============================================================ - -Configuration: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh -- User: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD - -Swap Details: -- From Token: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v -- To Token: 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms -- Amount In: 1 tokens -- Amount In (base units): 1000000 -- Min Amount Out (base units): 990000 -- From Decimals: 6 -- To Decimals: 6 - -Your Balance (From Token): 5 tokens -Your Balance (To Token): 15 tokens - -Sending transaction... -✅ Swap successful! - -Transaction Details: -- Signature: 4p9YnvLZPa2iW6ZkyNmgrrg8QnE8SEv8KgRXP85fQDVtSWqCLjXr8DHUt4DBVmnysdRBH7Ef9mH8ZVJBJzUAGppx -- Explorer: https://solscan.io/tx/4p9YnvLZPa2iW6ZkyNmgrrg8QnE8SEv8KgRXP85fQDVtSWqCLjXr8DHUt4DBVmnysdRBH7Ef9mH8ZVJBJzUAGppx - -Your Balance After (From Token): 4 tokens -Your Balance After (To Token): 16 tokens - -============================================================ -✅ Swap complete! -============================================================ -✨ Done in 3.28s. -``` - ---- - -### ✅ Step 7.3: Test Reverse Swap -**Test:** Reverse swap of 1 Custom Token -> USDC - -**Command:** -```bash -yarn ts-node scripts/04-test-swap.ts 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v 1 -``` - -**Result:** -- [x] Completed -- Transaction Signature: `41ucd8CqGkNjPbFYkDhgYVzGZUCwVfjgSK218f96YRTkw5huQzMBjeNEh5ESHq7yF7Dx9pMLNftQx3AHVhCikf3Q` -- Explorer: https://solscan.io/tx/41ucd8CqGkNjPbFYkDhgYVzGZUCwVfjgSK218f96YRTkw5huQzMBjeNEh5ESHq7yF7Dx9pMLNftQx3AHVhCikf3Q -- Input: `1` Custom Token -- Output: `1` USDC -- Exchange Rate: `1:1 (0% fee)` -- Balance Before: 16 Custom Token, 4 USDC -- Balance After: 15 Custom Token, 5 USDC -- Timestamp: `2025-12-19` -- Notes: `Reverse swap successful! Round-trip complete - both directions work perfectly at 1:1` - -``` -============================================================ -TEST SWAP -============================================================ - -Configuration: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh -- User: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD - -Swap Details: -- From Token: 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms -- To Token: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v -- Amount In: 1 tokens -- Amount In (base units): 1000000 -- Min Amount Out (base units): 990000 -- From Decimals: 6 -- To Decimals: 6 - -Your Balance (From Token): 16 tokens -Your Balance (To Token): 4 tokens - -Sending transaction... -✅ Swap successful! - -Transaction Details: -- Signature: 41ucd8CqGkNjPbFYkDhgYVzGZUCwVfjgSK218f96YRTkw5huQzMBjeNEh5ESHq7yF7Dx9pMLNftQx3AHVhCikf3Q -- Explorer: https://solscan.io/tx/41ucd8CqGkNjPbFYkDhgYVzGZUCwVfjgSK218f96YRTkw5huQzMBjeNEh5ESHq7yF7Dx9pMLNftQx3AHVhCikf3Q - -Your Balance After (From Token): 15 tokens -Your Balance After (To Token): 5 tokens - -============================================================ -✅ Swap complete! -============================================================ -✨ Done in 5.68s. -``` - ---- - -### ✅ Step 7.4: Test Delegation Swap (Optional) -**Test:** Swap tokens to a different recipient's account (delegation pattern) - -**Command:** -```bash -yarn ts-node scripts/04-test-swap.ts EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms 1 9TcjK2ToqoAtCr5jrLdDXNTNbZBbDQB1zy2BNGMr7nQE -``` - -**Result:** -- [x] Completed -- Transaction Signature: `4oJxzARyEWeMrUYin36eATPGpTSvQDr2xCG5CTytBguY9QDVNaWoDigYvCHUgAobmhrnDzqVq2xHf2GspuvLDmAe` -- Explorer: https://solscan.io/tx/4oJxzARyEWeMrUYin36eATPGpTSvQDr2xCG5CTytBguY9QDVNaWoDigYvCHUgAobmhrnDzqVq2xHf2GspuvLDmAe -- Signer: `13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD` -- Recipient: `9TcjK2ToqoAtCr5jrLdDXNTNbZBbDQB1zy2BNGMr7nQE` -- Input: `1` USDC (from signer) -- Output: `1` Custom Token (to recipient) -- Exchange Rate: `1:1 (0% fee)` -- Signer Balance Before: 5 USDC -- Signer Balance After: 4 USDC -- Recipient Balance Before: 0 Custom Token (account didn't exist) -- Recipient Balance After: 1 Custom Token -- Auto-created recipient's ATA: Yes -- Timestamp: `2025-12-23` -- Notes: `Delegation swap successful! Tokens swapped directly into recipient's account. Recipient's ATA was auto-created in the same transaction.` - -``` -============================================================ -TEST SWAP -============================================================ - -Configuration: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh -- User (Signer): 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Recipient (Destination): 9TcjK2ToqoAtCr5jrLdDXNTNbZBbDQB1zy2BNGMr7nQE - -Swap Details: -- From Token: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v -- To Token: 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms -- Amount In: 1 tokens -- Amount In (base units): 1000000 -- Min Amount Out (base units): 990000 -- From Decimals: 6 -- To Decimals: 6 - -Your Balance (From Token): 5 tokens -Recipient Balance (To Token): 0 tokens (account doesn't exist) - -Sending transaction... -Creating destination token account for recipient... -✅ Swap successful! - -Transaction Details: -- Signature: 4oJxzARyEWeMrUYin36eATPGpTSvQDr2xCG5CTytBguY9QDVNaWoDigYvCHUgAobmhrnDzqVq2xHf2GspuvLDmAe -- Explorer: https://solscan.io/tx/4oJxzARyEWeMrUYin36eATPGpTSvQDr2xCG5CTytBguY9QDVNaWoDigYvCHUgAobmhrnDzqVq2xHf2GspuvLDmAe - -Your Balance After (From Token): 4 tokens -Recipient Balance After (To Token): 1 tokens - -============================================================ -✅ Swap complete! -============================================================ -✨ Done in 6.41s. -``` - ---- - -## PHASE 9: Emergency Pause Testing - -### ✅ Step 9.1: Pause Swaps - -**Command:** -```bash -yarn ts-node scripts/emergency-pause-swaps.ts -``` - -**Result:** -- [x] Completed -- Transaction Signature: `3sXpiSANtWA7XBDVbBCqJeaMSLGJm79YNWctVzaotzcGbqnwf6oQuhvPNjQZM1sYwUGRna58hf38BDsqqK7XqgTA` -- Explorer: https://solscan.io/tx/3sXpiSANtWA7XBDVbBCqJeaMSLGJm79YNWctVzaotzcGbqnwf6oQuhvPNjQZM1sYwUGRna58hf38BDsqqK7XqgTA -- Previous State: Swaps Paused = false -- New State: Swaps Paused = true -- Timestamp: `2025-12-23` -- Notes: `Emergency pause activated successfully` - -``` -============================================================ -EMERGENCY PAUSE SWAPS -============================================================ - -Configuration: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh -- Pause Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Your Wallet: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD - -Current State: -- Swaps Paused: false -- Liquidity Paused: false - -⚠️ PAUSING SWAPS -Sending transaction... - -🛑 Swaps paused successfully! - -Transaction Details: -- Signature: 3sXpiSANtWA7XBDVbBCqJeaMSLGJm79YNWctVzaotzcGbqnwf6oQuhvPNjQZM1sYwUGRna58hf38BDsqqK7XqgTA -- Explorer: https://solscan.io/tx/3sXpiSANtWA7XBDVbBCqJeaMSLGJm79YNWctVzaotzcGbqnwf6oQuhvPNjQZM1sYwUGRna58hf38BDsqqK7XqgTA - -New State: -- Swaps Paused: true - -============================================================ -🛑 SWAPS PAUSED -============================================================ -✨ Done in 1.49s. -``` - ---- - -### ✅ Step 9.2: Test Swap (Paused - Should Fail) - -**Command:** -```bash -yarn ts-node scripts/04-test-swap.ts 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v 1 -``` - -**Result:** -- [x] Completed -- Transaction: Failed (as expected) -- Error Code: `SwapsPaused (0x1770)` -- Error Message: `Swaps are paused` -- Error Location: `programs/scaas-liquidity/src/lib.rs:167` -- Compute Units Used: 33,431 -- Notes: `✅ Emergency pause working correctly - swap blocked when paused` - -``` -❌ Error performing swap: -SendTransactionError: Simulation failed. -Message: Transaction simulation failed: Error processing Instruction 0: custom program error: 0x1770. -Logs: -[ - "Program GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv invoke [1]", - "Program log: Instruction: Swap", - "Program log: AnchorError thrown in programs/scaas-liquidity/src/lib.rs:167. Error Code: SwapsPaused. Error Number: 6000. Error Message: Swaps are paused.", - "Program GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv consumed 33431 of 200000 compute units", - "Program GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv failed: custom program error: 0x1770" -] -``` - ---- - -### ✅ Step 9.3: Unpause Swaps - -**Command:** -```bash -yarn ts-node scripts/emergency-pause-swaps.ts false -``` - -**Result:** -- [x] Completed -- Transaction Signature: `2bBjZSWDdPfmpobeMzNH1MWnbAahyrztYMproE4FEmbYvtEkqXr7XeD1Uz5xiAhUqnZRe3v5tNeoFJ4tPquEUhkW` -- Explorer: https://solscan.io/tx/2bBjZSWDdPfmpobeMzNH1MWnbAahyrztYMproE4FEmbYvtEkqXr7XeD1Uz5xiAhUqnZRe3v5tNeoFJ4tPquEUhkW -- Previous State: Swaps Paused = true -- New State: Swaps Paused = false -- Timestamp: `2025-12-23` -- Notes: `Emergency pause lifted - normal operations restored` - -``` -============================================================ -EMERGENCY PAUSE SWAPS -============================================================ - -Configuration: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh -- Pause Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Your Wallet: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD - -Current State: -- Swaps Paused: true -- Liquidity Paused: false - -✅ UNPAUSING SWAPS -Sending transaction... - -✅ Swaps unpaused successfully! - -Transaction Details: -- Signature: 2bBjZSWDdPfmpobeMzNH1MWnbAahyrztYMproE4FEmbYvtEkqXr7XeD1Uz5xiAhUqnZRe3v5tNeoFJ4tPquEUhkW -- Explorer: https://solscan.io/tx/2bBjZSWDdPfmpobeMzNH1MWnbAahyrztYMproE4FEmbYvtEkqXr7XeD1Uz5xiAhUqnZRe3v5tNeoFJ4tPquEUhkW - -New State: -- Swaps Paused: false - -============================================================ -✅ SWAPS RESTORED -============================================================ -✨ Done in 2.21s. -``` - ---- - -### ✅ Step 9.4: Test Swap (Unpaused - Should Succeed) - -**Command:** -```bash -yarn ts-node scripts/04-test-swap.ts 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v 1 -``` - -**Result:** -- [x] Completed -- Transaction Signature: `2SKpcXUSRydoHJiGvW5QsmM1wReFosHE7iLikqLyYeq6h2RCQk9cB2kso6f7rRZ1Dgaiky8a4ivghqimFmhNhLSg` -- Explorer: https://solscan.io/tx/2SKpcXUSRydoHJiGvW5QsmM1wReFosHE7iLikqLyYeq6h2RCQk9cB2kso6f7rRZ1Dgaiky8a4ivghqimFmhNhLSg -- Input: 1 Custom Token -- Output: 1 USDC -- Balance Before: 10 Custom Token, 8 USDC -- Balance After: 9 Custom Token, 9 USDC -- Exchange Rate: 1:1 (0% fee) -- Notes: `✅ Swap successful after unpausing - emergency procedures verified` - -``` -============================================================ -TEST SWAP -============================================================ - -Configuration: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh -- User (Signer): 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD - -Swap Details: -- From Token: 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms -- To Token: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v -- Amount In: 1 tokens -- Amount In (base units): 1000000 -- Min Amount Out (base units): 990000 -- From Decimals: 6 -- To Decimals: 6 - -Your Balance (From Token): 10 tokens -Your Balance (To Token): 8 tokens - -Sending transaction... -✅ Swap successful! - -Transaction Details: -- Signature: 2SKpcXUSRydoHJiGvW5QsmM1wReFosHE7iLikqLyYeq6h2RCQk9cB2kso6f7rRZ1Dgaiky8a4ivghqimFmhNhLSg -- Explorer: https://solscan.io/tx/2SKpcXUSRydoHJiGvW5QsmM1wReFosHE7iLikqLyYeq6h2RCQk9cB2kso6f7rRZ1Dgaiky8a4ivghqimFmhNhLSg - -Your Balance After (From Token): 9 tokens -Your Balance After (To Token): 9 tokens - -============================================================ -✅ Swap complete! -============================================================ -✨ Done in 5.86s. -``` - ---- - -### Emergency Pause Testing Summary - -**All Tests Passed:** ✅ - -| Test | Expected | Actual | Result | -|------|----------|--------|--------| -| Pause swaps | Paused | Paused | ✅ Pass | -| Swap (paused) | Fail | Failed (SwapsPaused) | ✅ Pass | -| Unpause swaps | Unpaused | Unpaused | ✅ Pass | -| Swap (unpaused) | Success | Success | ✅ Pass | - -**Key Findings:** -1. ✅ Emergency pause mechanism works correctly -2. ✅ Swaps are blocked when paused (error 0x1770) -3. ✅ Pause can be toggled bidirectionally (pause/unpause) -4. ✅ Normal operations resume after unpausing -5. ✅ Only pause_authority can execute pause operations - -**Emergency Pause Transactions:** -- Pause swaps: `3sXpiSANtWA7XBDVbBCqJeaMSLGJm79YNWctVzaotzcGbqnwf6oQuhvPNjQZM1sYwUGRna58hf38BDsqqK7XqgTA` -- Unpause swaps: `2bBjZSWDdPfmpobeMzNH1MWnbAahyrztYMproE4FEmbYvtEkqXr7XeD1Uz5xiAhUqnZRe3v5tNeoFJ4tPquEUhkW` -- Swap (after unpause): `2SKpcXUSRydoHJiGvW5QsmM1wReFosHE7iLikqLyYeq6h2RCQk9cB2kso6f7rRZ1Dgaiky8a4ivghqimFmhNhLSg` - ---- - -## PHASE 10: Liquidity Pause Testing - -### ✅ Step 10.1: Pause Liquidity Operations - -**Command:** -```bash -yarn ts-node scripts/emergency-pause-liquidity.ts -``` - -**Result:** -- [x] Completed -- Transaction Signature: `4XeTPiKxmvuXNd1YZi9TFYQCxXAN1Eo7SdVq18ErsJQpX9Li3Lc9HirbG3AQL53yEURRhCf7vppvw3FRfX7miwTe` -- Explorer: https://solscan.io/tx/4XeTPiKxmvuXNd1YZi9TFYQCxXAN1Eo7SdVq18ErsJQpX9Li3Lc9HirbG3AQL53yEURRhCf7vppvw3FRfX7miwTe -- Previous State: Liquidity Paused = false -- New State: Liquidity Paused = true -- Timestamp: `2025-12-23` -- Notes: `Liquidity operations paused successfully` - -``` -============================================================ -EMERGENCY PAUSE LIQUIDITY -============================================================ - -Configuration: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh -- Pause Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Your Wallet: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD - -Current State: -- Swaps Paused: false -- Liquidity Paused: false - -⚠️ PAUSING LIQUIDITY OPERATIONS -Sending transaction... - -🛑 Liquidity operations paused successfully! - -Transaction Details: -- Signature: 4XeTPiKxmvuXNd1YZi9TFYQCxXAN1Eo7SdVq18ErsJQpX9Li3Lc9HirbG3AQL53yEURRhCf7vppvw3FRfX7miwTe -- Explorer: https://solscan.io/tx/4XeTPiKxmvuXNd1YZi9TFYQCxXAN1Eo7SdVq18ErsJQpX9Li3Lc9HirbG3AQL53yEURRhCf7vppvw3FRfX7miwTe - -New State: -- Liquidity Paused: true - -============================================================ -🛑 LIQUIDITY PAUSED -============================================================ -✨ Done in 2.79s. -``` - ---- - -### ✅ Step 10.2: Test Deposit (Paused - Should Fail) - -**Command:** -```bash -yarn ts-node scripts/03-deposit-liquidity.ts 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms 1 -``` - -**Result:** -- [x] Completed -- Transaction: Failed (as expected) -- Error Code: `LiquidityPaused (0x1771)` -- Error Message: `Liquidity management is paused` -- Error Location: `programs/scaas-liquidity/src/lib.rs:143` -- Compute Units Used: 17,162 -- Notes: `✅ Liquidity pause working correctly - deposit blocked when paused` - -``` -❌ Error depositing liquidity: -AnchorError: AnchorError thrown in programs/scaas-liquidity/src/lib.rs:143. Error Code: LiquidityPaused. Error Number: 6001. Error Message: Liquidity management is paused. - -Program Logs: -Program GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv invoke [1] -Program log: Instruction: DepositLiquidity -Program log: AnchorError thrown in programs/scaas-liquidity/src/lib.rs:143. Error Code: LiquidityPaused. Error Number: 6001. Error Message: Liquidity management is paused. -Program GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv consumed 17162 of 200000 compute units -Program GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv failed: custom program error: 0x1771 -``` - ---- - -### ✅ Step 10.3: Unpause Liquidity Operations - -**Command:** -```bash -yarn ts-node scripts/emergency-pause-liquidity.ts false -``` - -**Result:** -- [x] Completed -- Transaction Signature: `2xgDjKSuN7EHaA1WrhwTLqpVH2S7drkRqsdeUfxoktsiE9Qz78RWk8vAcrpEjJm1Y7SbBoPw49gnrWVkS6fybYV8` -- Explorer: https://solscan.io/tx/2xgDjKSuN7EHaA1WrhwTLqpVH2S7drkRqsdeUfxoktsiE9Qz78RWk8vAcrpEjJm1Y7SbBoPw49gnrWVkS6fybYV8 -- Previous State: Liquidity Paused = true -- New State: Liquidity Paused = false -- Timestamp: `2025-12-23` -- Notes: `Liquidity operations restored successfully` - -``` -============================================================ -EMERGENCY PAUSE LIQUIDITY -============================================================ - -Configuration: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh -- Pause Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Your Wallet: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD - -Current State: -- Swaps Paused: false -- Liquidity Paused: true - -✅ UNPAUSING LIQUIDITY OPERATIONS -Sending transaction... - -✅ Liquidity operations unpaused successfully! - -Transaction Details: -- Signature: 2xgDjKSuN7EHaA1WrhwTLqpVH2S7drkRqsdeUfxoktsiE9Qz78RWk8vAcrpEjJm1Y7SbBoPw49gnrWVkS6fybYV8 -- Explorer: https://solscan.io/tx/2xgDjKSuN7EHaA1WrhwTLqpVH2S7drkRqsdeUfxoktsiE9Qz78RWk8vAcrpEjJm1Y7SbBoPw49gnrWVkS6fybYV8 - -New State: -- Liquidity Paused: false - -============================================================ -✅ LIQUIDITY RESTORED -============================================================ -✨ Done in 3.27s. -``` - ---- - -### ✅ Step 10.4: Test Deposit (Unpaused - Should Succeed) - -**Command:** -```bash -yarn ts-node scripts/03-deposit-liquidity.ts 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms 1 -``` - -**Result:** -- [x] Completed -- Transaction Signature: `KMGuXJeVRQCXYcygXcUnMeSUGmRULi1VBfZt1JxcAX7TG1CkgMeiWQRimxCRxbCnt22YizCmKjJAEMdBiV4Lv8M` -- Explorer: https://solscan.io/tx/KMGuXJeVRQCXYcygXcUnMeSUGmRULi1VBfZt1JxcAX7TG1CkgMeiWQRimxCRxbCnt22YizCmKjJAEMdBiV4Lv8M -- Amount Deposited: 1 Custom Token -- Vault Balance Before: 7 tokens -- Vault Balance After: 8 tokens -- Notes: `✅ Deposit successful after unpausing - liquidity pause verified` - -``` -============================================================ -DEPOSITING LIQUIDITY -============================================================ - -Configuration: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh -- Token Mint: 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms -- Token Decimals: 6 -- Vault PDA: EPFgtsNL4VVqwbh6cPwFoWCoqjW1WQvgbRAcitV49HYH -- Vault Token Account: 3y6MPgBvQqCS5EJkWE8w6dYwVLT3sue4s6inP8AoAkJv -- Operations Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Operations Authority Token Account: A7vP6Ut36Tmc94ACNBPKWjfiPZPeSsadw8jYqSbKEdBk - -Deposit Details: -- Amount (tokens): 1 -- Amount (base units): 1000000 - -Your Balance: 9 tokens -Vault Balance Before: 7 tokens - -Sending transaction... -✅ Liquidity deposited successfully! - -Transaction Details: -- Signature: KMGuXJeVRQCXYcygXcUnMeSUGmRULi1VBfZt1JxcAX7TG1CkgMeiWQRimxCRxbCnt22YizCmKjJAEMdBiV4Lv8M -- Explorer: https://solscan.io/tx/KMGuXJeVRQCXYcygXcUnMeSUGmRULi1VBfZt1JxcAX7TG1CkgMeiWQRimxCRxbCnt22YizCmKjJAEMdBiV4Lv8M - -Vault Balance After: 8 tokens -Amount Deposited: 1 tokens - -============================================================ -✅ Deposit complete! -============================================================ -✨ Done in 3.76s. -``` - ---- - -### ✅ Step 10.5: Pause Liquidity Again (For Withdrawal Test) - -**Command:** -```bash -yarn ts-node scripts/emergency-pause-liquidity.ts -``` - -**Result:** -- [x] Completed -- Transaction Signature: `5PAbgCCSpvqyTHFCFVUKD7v8pV8WVjfW2ccThTfsrUU5UPNajq95dEYtdKAQSCyVgazfW9zJuF4anPmtKCWhUzBi` -- Explorer: https://solscan.io/tx/5PAbgCCSpvqyTHFCFVUKD7v8pV8WVjfW2ccThTfsrUU5UPNajq95dEYtdKAQSCyVgazfW9zJuF4anPmtKCWhUzBi -- Previous State: Liquidity Paused = false -- New State: Liquidity Paused = true -- Timestamp: `2025-12-23` -- Notes: `Liquidity paused for withdrawal testing` - ---- - -### ✅ Step 10.6: Test Withdraw (Paused - Should Fail) - -**Command:** -```bash -yarn ts-node scripts/emergency-withdraw.ts 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms 1 -``` - -**Result:** -- [x] Completed -- Transaction: Failed (as expected) -- Error Code: `LiquidityPaused (0x1771)` -- Error Message: `Liquidity management is paused` -- Error Location: `programs/scaas-liquidity/src/lib.rs:317` -- Compute Units Used: 17,167 -- Notes: `✅ Liquidity pause correctly blocks withdrawals` - -``` -❌ Error withdrawing liquidity: -AnchorError: AnchorError thrown in programs/scaas-liquidity/src/lib.rs:317. Error Code: LiquidityPaused. Error Number: 6001. Error Message: Liquidity management is paused. - -Program Logs: -Program GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv invoke [1] -Program log: Instruction: WithdrawLiquidity -Program log: AnchorError thrown in programs/scaas-liquidity/src/lib.rs:317. Error Code: LiquidityPaused. Error Number: 6001. Error Message: Liquidity management is paused. -Program GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv consumed 17167 of 200000 compute units -Program GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv failed: custom program error: 0x1771 -``` - ---- - -### ✅ Step 10.7: Unpause and Withdraw Successfully - -**Unpause Command:** -```bash -yarn ts-node scripts/emergency-pause-liquidity.ts false -``` - -**Unpause Result:** -- [x] Completed -- Transaction Signature: `55oijzSiuUVy4qjDuqWrAG8dfWURgW2L1YNSz2C5wGzsEK248pzG2DyHNH93AGNakGVkNu8GsXbn9rmXuq6ACGiy` -- Explorer: https://solscan.io/tx/55oijzSiuUVy4qjDuqWrAG8dfWURgW2L1YNSz2C5wGzsEK248pzG2DyHNH93AGNakGVkNu8GsXbn9rmXuq6ACGiy -- New State: Liquidity Paused = false - -**Withdraw Command:** -```bash -yarn ts-node scripts/emergency-withdraw.ts 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms 1 -``` - -**Withdraw Result:** -- [x] Completed -- Transaction Signature: `4VcUdmRMS4xJLonfF173tRdUWSHjBWeyNgNQm4fEVSfSBjY1ALioDfK5ScQn3m2kY7Gj4c9CQYLizfFJouAZkQeS` -- Explorer: https://solscan.io/tx/4VcUdmRMS4xJLonfF173tRdUWSHjBWeyNgNQm4fEVSfSBjY1ALioDfK5ScQn3m2kY7Gj4c9CQYLizfFJouAZkQeS -- Amount Withdrawn: 1 Custom Token -- Vault Balance Before: 8 tokens -- Vault Balance After: 7 tokens (verified via verify-pool.ts) -- Notes: `Transaction timed out but succeeded. ✅ Withdrawal successful after unpausing` - ---- - -### Liquidity Pause Testing Summary - -**All Tests Passed:** ✅ - -| Test | Expected | Actual | Result | -|------|----------|--------|--------| -| Pause liquidity | Paused | Paused | ✅ Pass | -| Deposit (paused) | Fail | Failed (LiquidityPaused) | ✅ Pass | -| Unpause liquidity | Unpaused | Unpaused | ✅ Pass | -| Deposit (unpaused) | Success | Success | ✅ Pass | -| Pause liquidity (again) | Paused | Paused | ✅ Pass | -| Withdraw (paused) | Fail | Failed (LiquidityPaused) | ✅ Pass | -| Unpause liquidity | Unpaused | Unpaused | ✅ Pass | -| Withdraw (unpaused) | Success | Success | ✅ Pass | - -**Key Findings:** -1. ✅ Liquidity pause mechanism works correctly -2. ✅ Deposits are blocked when paused (error 0x1771 at lib.rs:143) -3. ✅ Withdrawals are blocked when paused (error 0x1771 at lib.rs:317) -4. ✅ Pause can be toggled bidirectionally (pause/unpause) -5. ✅ Normal operations resume after unpausing -6. ✅ Only pause_authority can execute pause operations -7. ✅ Both deposit and withdraw respect the same pause flag - -**Liquidity Pause Transactions:** -- Pause liquidity (1st): `4XeTPiKxmvuXNd1YZi9TFYQCxXAN1Eo7SdVq18ErsJQpX9Li3Lc9HirbG3AQL53yEURRhCf7vppvw3FRfX7miwTe` -- Unpause liquidity (1st): `2xgDjKSuN7EHaA1WrhwTLqpVH2S7drkRqsdeUfxoktsiE9Qz78RWk8vAcrpEjJm1Y7SbBoPw49gnrWVkS6fybYV8` -- Deposit (after unpause): `KMGuXJeVRQCXYcygXcUnMeSUGmRULi1VBfZt1JxcAX7TG1CkgMeiWQRimxCRxbCnt22YizCmKjJAEMdBiV4Lv8M` -- Pause liquidity (2nd): `5PAbgCCSpvqyTHFCFVUKD7v8pV8WVjfW2ccThTfsrUU5UPNajq95dEYtdKAQSCyVgazfW9zJuF4anPmtKCWhUzBi` -- Unpause liquidity (2nd): `55oijzSiuUVy4qjDuqWrAG8dfWURgW2L1YNSz2C5wGzsEK248pzG2DyHNH93AGNakGVkNu8GsXbn9rmXuq6ACGiy` -- Withdraw (after unpause): `4VcUdmRMS4xJLonfF173tRdUWSHjBWeyNgNQm4fEVSfSBjY1ALioDfK5ScQn3m2kY7Gj4c9CQYLizfFJouAZkQeS` - ---- - -## PHASE 11: Token Disable Testing - -### ✅ Step 11.1: Disable Custom Token - -**Command:** -```bash -yarn ts-node scripts/update-token-status.ts 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms true -``` - -**Result:** -- [x] Completed -- Transaction Signature: `2tPVNX1CaVUzuYvbRnAPksmechRFymdd9hnuRkx7fdDsvUvrvU3YsD8cZpsBoVghz6ZtFBdW3AB5p7cqHPfT8PNq` -- Explorer: https://solscan.io/tx/2tPVNX1CaVUzuYvbRnAPksmechRFymdd9hnuRkx7fdDsvUvrvU3YsD8cZpsBoVghz6ZtFBdW3AB5p7cqHPfT8PNq -- Previous State: Token Disabled = false -- New State: Token Disabled = true -- Timestamp: `2025-12-23` -- Notes: `Custom token disabled successfully` - -``` -============================================================ -UPDATE TOKEN STATUS -============================================================ - -Configuration: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh -- Token Mint: 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms -- Vault PDA: EPFgtsNL4VVqwbh6cPwFoWCoqjW1WQvgbRAcitV49HYH -- Pause Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Your Wallet: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD - -Current State: -- Token Disabled: false - -Setting token to: DISABLED -Sending transaction... - -🛑 Token status updated successfully! - -Transaction Details: -- Signature: 2tPVNX1CaVUzuYvbRnAPksmechRFymdd9hnuRkx7fdDsvUvrvU3YsD8cZpsBoVghz6ZtFBdW3AB5p7cqHPfT8PNq -- Explorer: https://solscan.io/tx/2tPVNX1CaVUzuYvbRnAPksmechRFymdd9hnuRkx7fdDsvUvrvU3YsD8cZpsBoVghz6ZtFBdW3AB5p7cqHPfT8PNq - -New State: -- Token Disabled: true - -============================================================ -🛑 TOKEN DISABLED -============================================================ - -Note: Disabled tokens cannot be used in swaps. -This is useful for emergency situations or token migrations. -✨ Done in 2.37s. -``` - ---- - -### ✅ Step 11.2: Test Swap (Disabled Token - Should Fail) - -**Command:** -```bash -yarn ts-node scripts/04-test-swap.ts 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v 1 -``` - -**Result:** -- [x] Completed -- Transaction: Failed (as expected) -- Error Code: `TokenDisabled (0x177e)` -- Error Message: `Token is disabled and cannot be used in swaps` -- Error Location: `programs/scaas-liquidity/src/lib.rs:188` -- Compute Units Used: 33,682 -- Notes: `✅ Token disable working correctly - swap blocked for disabled token` - -``` -❌ Error performing swap: -SendTransactionError: Simulation failed. -Message: Transaction simulation failed: Error processing Instruction 0: custom program error: 0x177e. -Logs: -[ - "Program GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv invoke [1]", - "Program log: Instruction: Swap", - "Program log: AnchorError thrown in programs/scaas-liquidity/src/lib.rs:188. Error Code: TokenDisabled. Error Number: 6014. Error Message: Token is disabled and cannot be used in swaps.", - "Program GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv consumed 33682 of 200000 compute units", - "Program GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv failed: custom program error: 0x177e" -] -``` - ---- - -### ✅ Step 11.3: Enable Custom Token - -**Command:** -```bash -yarn ts-node scripts/update-token-status.ts 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms false -``` - -**Result:** -- [x] Completed -- Transaction Signature: `298gU2MmJqJNi3mhKs48dipvQz9LBDMxo3ExbNVPxWUJP2dZCpuqaRu3Pi3K9Gkc2Uoua9ADJqxruzZucz7tXtoG` -- Explorer: https://solscan.io/tx/298gU2MmJqJNi3mhKs48dipvQz9LBDMxo3ExbNVPxWUJP2dZCpuqaRu3Pi3K9Gkc2Uoua9ADJqxruzZucz7tXtoG -- Previous State: Token Disabled = true -- New State: Token Disabled = false -- Timestamp: `2025-12-23` -- Notes: `Token re-enabled successfully` - -``` -============================================================ -UPDATE TOKEN STATUS -============================================================ - -Configuration: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh -- Token Mint: 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms -- Vault PDA: EPFgtsNL4VVqwbh6cPwFoWCoqjW1WQvgbRAcitV49HYH -- Pause Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Your Wallet: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD - -Current State: -- Token Disabled: true - -Setting token to: ENABLED -Sending transaction... - -✅ Token status updated successfully! - -Transaction Details: -- Signature: 298gU2MmJqJNi3mhKs48dipvQz9LBDMxo3ExbNVPxWUJP2dZCpuqaRu3Pi3K9Gkc2Uoua9ADJqxruzZucz7tXtoG -- Explorer: https://solscan.io/tx/298gU2MmJqJNi3mhKs48dipvQz9LBDMxo3ExbNVPxWUJP2dZCpuqaRu3Pi3K9Gkc2Uoua9ADJqxruzZucz7tXtoG - -New State: -- Token Disabled: false - -============================================================ -✅ TOKEN ENABLED -============================================================ -✨ Done in 4.07s. -``` - ---- - -### ✅ Step 11.4: Test Swap (Enabled Token - Should Succeed) - -**Command:** -```bash -yarn ts-node scripts/04-test-swap.ts 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v 1 -``` - -**Result:** -- [x] Completed -- Transaction Signature: `yLMk1oQDkWsF8n2vVYjPsitw8eMdP8DEei1X7oaVdJ8FzBdAVGSnb7fcqnkEWM7VAohiF9Jyz55vQZyv8nrPfHp` -- Explorer: https://solscan.io/tx/yLMk1oQDkWsF8n2vVYjPsitw8eMdP8DEei1X7oaVdJ8FzBdAVGSnb7fcqnkEWM7VAohiF9Jyz55vQZyv8nrPfHp -- Input: 1 Custom Token -- Output: 1 USDC -- Balance Before: 9 Custom Token, 9 USDC -- Balance After: 8 Custom Token, 10 USDC -- Exchange Rate: 1:1 (0% fee) -- Notes: `✅ Swap successful after re-enabling token` - -``` -============================================================ -TEST SWAP -============================================================ - -Configuration: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh -- User (Signer): 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD - -Swap Details: -- From Token: 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms -- To Token: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v -- Amount In: 1 tokens -- Amount In (base units): 1000000 -- Min Amount Out (base units): 990000 -- From Decimals: 6 -- To Decimals: 6 - -Your Balance (From Token): 9 tokens -Your Balance (To Token): 9 tokens - -Sending transaction... -✅ Swap successful! - -Transaction Details: -- Signature: yLMk1oQDkWsF8n2vVYjPsitw8eMdP8DEei1X7oaVdJ8FzBdAVGSnb7fcqnkEWM7VAohiF9Jyz55vQZyv8nrPfHp -- Explorer: https://solscan.io/tx/yLMk1oQDkWsF8n2vVYjPsitw8eMdP8DEei1X7oaVdJ8FzBdAVGSnb7fcqnkEWM7VAohiF9Jyz55vQZyv8nrPfHp - -Your Balance After (From Token): 8 tokens -Your Balance After (To Token): 10 tokens - -============================================================ -✅ Swap complete! -============================================================ -✨ Done in 2.18s. -``` - ---- - -### Token Disable Testing Summary - -**All Tests Passed:** ✅ - -| Test | Expected | Actual | Result | -|------|----------|--------|--------| -| Disable token | Disabled | Disabled | ✅ Pass | -| Swap (disabled token) | Fail | Failed (TokenDisabled) | ✅ Pass | -| Enable token | Enabled | Enabled | ✅ Pass | -| Swap (enabled token) | Success | Success | ✅ Pass | - -**Key Findings:** -1. ✅ Token disable mechanism works correctly -2. ✅ Swaps are blocked when token is disabled (error 0x177e at lib.rs:188) -3. ✅ Token status can be toggled bidirectionally (disable/enable) -4. ✅ Swaps resume after re-enabling token -5. ✅ Only pause_authority can update token status -6. ✅ Useful for emergency situations or token migrations - -**Token Disable Transactions:** -- Disable token: `2tPVNX1CaVUzuYvbRnAPksmechRFymdd9hnuRkx7fdDsvUvrvU3YsD8cZpsBoVghz6ZtFBdW3AB5p7cqHPfT8PNq` -- Enable token: `298gU2MmJqJNi3mhKs48dipvQz9LBDMxo3ExbNVPxWUJP2dZCpuqaRu3Pi3K9Gkc2Uoua9ADJqxruzZucz7tXtoG` -- Swap (after enable): `yLMk1oQDkWsF8n2vVYjPsitw8eMdP8DEei1X7oaVdJ8FzBdAVGSnb7fcqnkEWM7VAohiF9Jyz55vQZyv8nrPfHp` - ---- - -## Phase 12: Pause Authority Transfer Testing - -**Purpose:** Verify that pause authority can be safely transferred to another wallet and restored - -**Timestamp:** 2025-01-06 - -### Test Setup - -**Original Wallet:** -``` -Address: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -Path: ~/.config/solana/id.json -``` - -**Second Wallet (Created for Testing):** -``` -Address: Fj33DQzvJf3GkLa11ejpvNhDvhCFrSdpk5rJQ7KHJhor -Path: ~/.config/solana/id2.json -``` - -### Step 1: Transfer Pause Authority to New Wallet - -```bash -yarn ts-node scripts/update-pause-authority.ts Fj33DQzvJf3GkLa11ejpvNhDvhCFrSdpk5rJQ7KHJhor -``` - -**Output:** -``` -============================================================ -UPDATE PAUSE AUTHORITY -============================================================ - -Configuration: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh -- Current Pause Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Your Wallet: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- New Pause Authority: Fj33DQzvJf3GkLa11ejpvNhDvhCFrSdpk5rJQ7KHJhor - -⚠️ WARNING: This will transfer pause authority control! - The new pause authority will be able to: - - Pause/unpause swaps and liquidity operations - - Enable/disable tokens - - Transfer pause authority to another address - -Sending transaction... - -✅ Pause authority updated successfully! - -Transaction Details: -- Signature: 3xRZv2XfUN3UrP1Lbj7CVDbDyv7137ZhDFtdtErkdLNzTg2tywWhMRH9ie4PrcLmNA3fRGfgDLiUkH58U4zknV3F -- Explorer: https://solscan.io/tx/3xRZv2XfUN3UrP1Lbj7CVDbDyv7137ZhDFtdtErkdLNzTg2tywWhMRH9ie4PrcLmNA3fRGfgDLiUkH58U4zknV3F - -Authority Transfer: -- Old Pause Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- New Pause Authority: Fj33DQzvJf3GkLa11ejpvNhDvhCFrSdpk5rJQ7KHJhor - -============================================================ -✅ Authority transfer complete! -============================================================ -``` - -**Transaction:** `3xRZv2XfUN3UrP1Lbj7CVDbDyv7137ZhDFtdtErkdLNzTg2tywWhMRH9ie4PrcLmNA3fRGfgDLiUkH58U4zknV3F` - -**Result:** Pause authority successfully transferred to new wallet ✅ - -### Step 2: Verify Old Wallet Cannot Pause (Authority Rejected) - -```bash -export ANCHOR_WALLET=~/.config/solana/id.json -yarn ts-node scripts/emergency-pause-swaps.ts -``` - -**Output:** -``` -❌ Error: You are not the pause authority - Pause authority is: Fj33DQzvJf3GkLa11ejpvNhDvhCFrSdpk5rJQ7KHJhor - Your wallet is: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -``` - -**Result:** Old wallet correctly rejected ✅ - -### Step 3: Verify New Wallet Can Pause Swaps - -```bash -export ANCHOR_WALLET=~/.config/solana/id2.json -yarn ts-node scripts/emergency-pause-swaps.ts -``` - -**Output:** -``` -⚠️ WARNING: This will PAUSE all swap operations! - Users will NOT be able to swap tokens until unpaused. - -Are you sure you want to pause swaps? Type 'PAUSE' to confirm: PAUSE - -Sending transaction... - -⛔️ Swaps paused successfully! - -Transaction Details: -- Signature: EBNWFTFcU4A84dev5HcYEN7csg8F89Ke9LMGqGkjxXY4nAjfaej8op7RZFiCsdVCJSXhCnGjeXUBSTWWtmZHTr4 -- Explorer: https://solscan.io/tx/EBNWFTFcU4A84dev5HcYEN7csg8F89Ke9LMGqGkjxXY4nAjfaej8op7RZFiCsdVCJSXhCnGjeXUBSTWWtmZHTr4 -``` - -**Transaction:** `EBNWFTFcU4A84dev5HcYEN7csg8F89Ke9LMGqGkjxXY4nAjfaej8op7RZFiCsdVCJSXhCnGjeXUBSTWWtmZHTr4` - -**Result:** New wallet successfully paused swaps ✅ - -### Step 4: Transfer Pause Authority Back to Original Wallet - -```bash -export ANCHOR_WALLET=~/.config/solana/id2.json -yarn ts-node scripts/update-pause-authority.ts 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -``` - -**Output:** -``` -✅ Pause authority updated successfully! - -Transaction Details: -- Signature: 2HJfbLeqByseSa1pQu2eN8RbgWKApEAjXaccG7YZ6JwwTWFy5kkDDtU1dxc9mGkCuAV8AP4aWRo9QwsFX3tAxfKR -- Explorer: https://solscan.io/tx/2HJfbLeqByseSa1pQu2eN8RbgWKApEAjXaccG7YZ6JwwTWFy5kkDDtU1dxc9mGkCuAV8AP4aWRo9QwsFX3tAxfKR - -Authority Transfer: -- Old Pause Authority: Fj33DQzvJf3GkLa11ejpvNhDvhCFrSdpk5rJQ7KHJhor -- New Pause Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -``` - -**Transaction:** `2HJfbLeqByseSa1pQu2eN8RbgWKApEAjXaccG7YZ6JwwTWFy5kkDDtU1dxc9mGkCuAV8AP4aWRo9QwsFX3tAxfKR` - -**Result:** Authority transferred back successfully ✅ - -### Step 5: Verify Original Wallet Can Unpause Swaps - -```bash -export ANCHOR_WALLET=~/.config/solana/id.json -yarn ts-node scripts/emergency-unpause-swaps.ts -``` - -**Output:** -``` -⚠️ WARNING: This will UNPAUSE swap operations! - Users will be able to swap tokens after this. - -Are you sure you want to unpause swaps? Type 'UNPAUSE' to confirm: UNPAUSE - -Sending transaction... - -✅ Swaps unpaused successfully! - -Transaction Details: -- Signature: 5pzZfPyJJPKxz9fk4Woim6V9NbXj7KvsSfR52AXVxvk3FTk8wCWFtuCLqX7cmZiMCP99Qjbj3WwJEfkPJV3xwwyb -- Explorer: https://solscan.io/tx/5pzZfPyJJPKxz9fk4Woim6V9NbXj7KvsSfR52AXVxvk3FTk8wCWFtuCLqX7cmZiMCP99Qjbj3WwJEfkPJV3xwwyb -``` - -**Transaction:** `5pzZfPyJJPKxz9fk4Woim6V9NbXj7KvsSfR52AXVxvk3FTk8wCWFtuCLqX7cmZiMCP99Qjbj3WwJEfkPJV3xwwyb` - -**Result:** Original wallet successfully unpaused swaps ✅ - ---- - -### Pause Authority Transfer Testing Summary - -**All Tests Passed:** ✅ - -| Test | Expected | Actual | Result | -|------|----------|--------|--------| -| Transfer to new wallet | Success | Success | ✅ Pass | -| Old wallet rejected | Error | Error (Not pause authority) | ✅ Pass | -| New wallet can pause | Success | Success | ✅ Pass | -| Transfer back to original | Success | Success | ✅ Pass | -| Original wallet can unpause | Success | Success | ✅ Pass | - -**Key Findings:** -1. ✅ Pause authority can be transferred to another wallet -2. ✅ Old authority is immediately revoked after transfer -3. ✅ New authority can exercise pause/unpause functions -4. ✅ Authority can be transferred back (round-trip works) -5. ✅ Access control enforcement is immediate and correct -6. ✅ Useful for multi-signature setups or authority rotation - -**Pause Authority Transfer Transactions:** -- Transfer to new wallet: `3xRZv2XfUN3UrP1Lbj7CVDbDyv7137ZhDFtdtErkdLNzTg2tywWhMRH9ie4PrcLmNA3fRGfgDLiUkH58U4zknV3F` -- New wallet pause swaps: `EBNWFTFcU4A84dev5HcYEN7csg8F89Ke9LMGqGkjxXY4nAjfaej8op7RZFiCsdVCJSXhCnGjeXUBSTWWtmZHTr4` -- Transfer back to original: `2HJfbLeqByseSa1pQu2eN8RbgWKApEAjXaccG7YZ6JwwTWFy5kkDDtU1dxc9mGkCuAV8AP4aWRo9QwsFX3tAxfKR` -- Original wallet unpause: `5pzZfPyJJPKxz9fk4Woim6V9NbXj7KvsSfR52AXVxvk3FTk8wCWFtuCLqX7cmZiMCP99Qjbj3WwJEfkPJV3xwwyb` - ---- - -## Phase 13: Operations Authority Transfer Testing - -**Purpose:** Verify that operations authority can be safely transferred to another wallet and restored - -**Timestamp:** 2025-01-06 - -### Test Setup - -Same wallets as Phase 12: -- **Original Wallet:** `13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD` (~/.config/solana/id.json) -- **Second Wallet:** `Fj33DQzvJf3GkLa11ejpvNhDvhCFrSdpk5rJQ7KHJhor` (~/.config/solana/id2.json) - -### Step 1: Transfer Operations Authority to New Wallet - -```bash -export ANCHOR_WALLET=~/.config/solana/id.json -yarn ts-node scripts/update-operations-authority.ts Fj33DQzvJf3GkLa11ejpvNhDvhCFrSdpk5rJQ7KHJhor -``` - -**Output:** -``` -============================================================ -UPDATE OPERATIONS AUTHORITY -============================================================ - -Configuration: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh -- Current Operations Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Your Wallet: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- New Operations Authority: Fj33DQzvJf3GkLa11ejpvNhDvhCFrSdpk5rJQ7KHJhor - -⚠️ WARNING: This will transfer operations authority control! - The new operations authority will be able to: - - Add and remove tokens - - Deposit and withdraw liquidity - - Update reserved amounts - - Transfer operations authority to another address - -Sending transaction... - -✅ Operations authority updated successfully! - -Transaction Details: -- Signature: W6bEKXiUs2y3W1LKn1s84MqEhCjyZgfQu64VsoaPBMTH8bijedV5oxAVWpYV7LQRkbutMPuxtGUSNaNbDUiZvKD -- Explorer: https://solscan.io/tx/W6bEKXiUs2y3W1LKn1s84MqEhCjyZgfQu64VsoaPBMTH8bijedV5oxAVWpYV7LQRkbutMPuxtGUSNaNbDUiZvKD - -Authority Transfer: -- Old Operations Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- New Operations Authority: Fj33DQzvJf3GkLa11ejpvNhDvhCFrSdpk5rJQ7KHJhor - -============================================================ -✅ Authority transfer complete! -============================================================ -``` - -**Transaction:** `W6bEKXiUs2y3W1LKn1s84MqEhCjyZgfQu64VsoaPBMTH8bijedV5oxAVWpYV7LQRkbutMPuxtGUSNaNbDUiZvKD` - -**Result:** Operations authority successfully transferred to new wallet ✅ - -### Step 2: Verify Old Wallet Cannot Withdraw (Operations Authority Rejected) - -```bash -export ANCHOR_WALLET=~/.config/solana/id.json -yarn ts-node scripts/emergency-withdraw.ts EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v 0.01 -``` - -**Output:** -``` -============================================================ -EMERGENCY WITHDRAW LIQUIDITY -============================================================ - -Configuration: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh -- Token Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v -- Operations Authority: Fj33DQzvJf3GkLa11ejpvNhDvhCFrSdpk5rJQ7KHJhor -- Your Wallet: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD - -❌ Error: You are not the operations authority - Operations authority is: Fj33DQzvJf3GkLa11ejpvNhDvhCFrSdpk5rJQ7KHJhor - Your wallet is: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -``` - -**Result:** Old wallet correctly rejected ✅ - -### Step 3: Create Token Account and Withdraw with New Wallet - -**Create USDC token account for second wallet:** -```bash -solana config set -k /Users/salioudiallo/.config/solana/id2.json -spl-token create-account EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v --owner Fj33DQzvJf3GkLa11ejpvNhDvhCFrSdpk5rJQ7KHJhor --fee-payer ~/.config/solana/id2.json -``` - -**Output:** -``` -Creating account G5FuJnAFh7QF5hueW1mrRafi99QpYxUytfFZpVUS1bvq - -Signature: 5yKYJTG4SBYuVeKUWuL1NFM6EsZRyEFB4vae1Mx1JRt9WgwnQPgikXoxAog7BCpy7MCUxtZUNPxiyMavoxVCGDci -``` - -**Token Account Creation Transaction:** `5yKYJTG4SBYuVeKUWuL1NFM6EsZRyEFB4vae1Mx1JRt9WgwnQPgikXoxAog7BCpy7MCUxtZUNPxiyMavoxVCGDci` - -**Withdraw liquidity:** -```bash -export ANCHOR_WALLET=~/.config/solana/id2.json -yarn ts-node scripts/emergency-withdraw.ts EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v 0.01 -``` - -**Output:** -``` -============================================================ -EMERGENCY WITHDRAW LIQUIDITY -============================================================ - -Configuration: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh -- Token Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v -- Token Decimals: 6 -- Vault PDA: GZHACYSwd3TWc4AVjWnHVwudVXrXWdh6qqKhgGcf1aFx -- Vault Token Account: 9XX1dwrvpxAqzZ8jEbE8NMmTA7nGK2eVApuoXJxPGrfS -- Operations Authority: Fj33DQzvJf3GkLa11ejpvNhDvhCFrSdpk5rJQ7KHJhor -- Operations Authority Token Account: G5FuJnAFh7QF5hueW1mrRafi99QpYxUytfFZpVUS1bvq -- Your Wallet: Fj33DQzvJf3GkLa11ejpvNhDvhCFrSdpk5rJQ7KHJhor - -Current State: -- Vault Total Balance: 2 tokens -- Reserved Amount: 0 tokens -- Available to Withdraw: 2 tokens -- Token Disabled: false - -⚠️ WITHDRAWING 0.01 TOKENS - -Withdrawal Details: -- Amount (tokens): 0.01 -- Amount (base units): 10000 - -Sending transaction... - -✅ Liquidity withdrawn successfully! - -Transaction Details: -- Signature: 4P4CbFWuH5Lj4dvckNAmnTdVMZGq9oSTRShDUTXFtXPWXNPJ4QuYwHNohZGT7oazZeTyQ8T6L3KFmogcJ4KGmBiD -- Explorer: https://solscan.io/tx/4P4CbFWuH5Lj4dvckNAmnTdVMZGq9oSTRShDUTXFtXPWXNPJ4QuYwHNohZGT7oazZeTyQ8T6L3KFmogcJ4KGmBiD - -Updated State: -- Vault Balance After: 1.99 tokens -- Amount Withdrawn: 0.01 tokens - -============================================================ -✅ EMERGENCY WITHDRAWAL COMPLETE -============================================================ -``` - -**Withdrawal Transaction:** `4P4CbFWuH5Lj4dvckNAmnTdVMZGq9oSTRShDUTXFtXPWXNPJ4QuYwHNohZGT7oazZeTyQ8T6L3KFmogcJ4KGmBiD` - -**Verification:** -```bash -spl-token accounts -``` - -**Output:** -``` -Token Balance ------------------------------------------------------ -EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v 0.01 -``` - -**Result:** New wallet successfully withdrew liquidity, balance confirmed ✅ - -### Step 4: Transfer Operations Authority Back to Original Wallet - -```bash -export ANCHOR_WALLET=~/.config/solana/id2.json -yarn ts-node scripts/update-operations-authority.ts 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -``` - -**Output:** -``` -============================================================ -UPDATE OPERATIONS AUTHORITY -============================================================ - -Configuration: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh -- Current Operations Authority: Fj33DQzvJf3GkLa11ejpvNhDvhCFrSdpk5rJQ7KHJhor -- Your Wallet: Fj33DQzvJf3GkLa11ejpvNhDvhCFrSdpk5rJQ7KHJhor -- New Operations Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD - -⚠️ WARNING: This will transfer operations authority control! - The new operations authority will be able to: - - Add and remove tokens - - Deposit and withdraw liquidity - - Update reserved amounts - - Transfer operations authority to another address - -Sending transaction... - -✅ Operations authority updated successfully! - -Transaction Details: -- Signature: 2gAeBnkNpQb6uC71gpwscj6w7e2VYE3rFy79N4XEGYVEoSC1dKH3SBxQBLUMEzcHhUqd8BzYUgBUXvkktjxNuatD -- Explorer: https://solscan.io/tx/2gAeBnkNpQb6uC71gpwscj6w7e2VYE3rFy79N4XEGYVEoSC1dKH3SBxQBLUMEzcHhUqd8BzYUgBUXvkktjxNuatD - -Authority Transfer: -- Old Operations Authority: Fj33DQzvJf3GkLa11ejpvNhDvhCFrSdpk5rJQ7KHJhor -- New Operations Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD - -============================================================ -✅ Authority transfer complete! -============================================================ -``` - -**Transaction:** `2gAeBnkNpQb6uC71gpwscj6w7e2VYE3rFy79N4XEGYVEoSC1dKH3SBxQBLUMEzcHhUqd8BzYUgBUXvkktjxNuatD` - -**Result:** Authority transferred back successfully ✅ - -### Step 5: Verify Original Wallet Can Deposit Liquidity - -```bash -export ANCHOR_WALLET=~/.config/solana/id.json -solana config set -k /Users/salioudiallo/.config/solana/id.json -yarn ts-node scripts/03-deposit-liquidity.ts EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v 0.01 -``` - -**Output:** -``` -============================================================ -DEPOSITING LIQUIDITY -============================================================ - -Configuration: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh -- Token Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v -- Token Decimals: 6 -- Vault PDA: GZHACYSwd3TWc4AVjWnHVwudVXrXWdh6qqKhgGcf1aFx -- Vault Token Account: 9XX1dwrvpxAqzZ8jEbE8NMmTA7nGK2eVApuoXJxPGrfS -- Operations Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Operations Authority Token Account: 7vqUNuSd8jYFcbV3RXMyHFTV8kZx2uUVFVSCSQCoQQgw - -Deposit Details: -- Amount (tokens): 0.01 -- Amount (base units): 10000 - -Your Balance: 10 tokens -Vault Balance Before: 1.99 tokens - -Sending transaction... -✅ Liquidity deposited successfully! - -Transaction Details: -- Signature: 37TKsDbHLHL49XXp3EpRUzzoK71DTCZfWp7xs5mZtHSb1NEkp61qYvUrAYqbBNPSEokQRTR3yMpZ1Ehn9k5hjR6w -- Explorer: https://solscan.io/tx/37TKsDbHLHL49XXp3EpRUzzoK71DTCZfWp7xs5mZtHSb1NEkp61qYvUrAYqbBNPSEokQRTR3yMpZ1Ehn9k5hjR6w - -Vault Balance After: 2 tokens -Amount Deposited: 0.010000000000000009 tokens - -============================================================ -✅ Deposit complete! -============================================================ -``` - -**Transaction:** `37TKsDbHLHL49XXp3EpRUzzoK71DTCZfWp7xs5mZtHSb1NEkp61qYvUrAYqbBNPSEokQRTR3yMpZ1Ehn9k5hjR6w` - -**Result:** Original wallet successfully deposited liquidity ✅ - ---- - -### Final Pool State Verification - -After completing all authority transfer tests, verified final pool state: - -```bash -yarn ts-node scripts/view-pool-state.ts -``` - -**Output:** -``` -============================================================ -POOL STATE VERIFICATION -============================================================ - -Addresses: -- Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -- Pool PDA: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh - -Pool Configuration: -- Operations Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Pause Authority: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Fee Recipient: 13V7ou4zHHwDVaAGWxqHSwU2sVzRR4m62XWqCFxhA5fD -- Fee Rate: 0 bps (0%) -- Swaps Paused: false -- Liquidity Paused: false - -Supported Tokens: -- Count: 2 - -Token 1: -- Mint: 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms -- Vault: EPFgtsNL4VVqwbh6cPwFoWCoqjW1WQvgbRAcitV49HYH -- Vault Token Account: 3y6MPgBvQqCS5EJkWE8w6dYwVLT3sue4s6inP8AoAkJv -- Decimals: 6 -- Total Balance: 8 tokens -- Reserved Amount: 0 tokens -- Available Liquidity: 8 tokens -- Disabled: false - -Token 2: -- Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v -- Vault: GZHACYSwd3TWc4AVjWnHVwudVXrXWdh6qqKhgGcf1aFx -- Vault Token Account: 9XX1dwrvpxAqzZ8jEbE8NMmTA7nGK2eVApuoXJxPGrfS -- Decimals: 6 -- Total Balance: 2 tokens -- Reserved Amount: 0 tokens -- Available Liquidity: 2 tokens -- Disabled: false - - -============================================================ -✅ Pool verification complete! -============================================================ -✨ Done in 1.58s. -``` - -**Verification Summary:** -- ✅ Both authorities restored to original wallet -- ✅ No pausing active (swaps and liquidity operational) -- ✅ Fee rate at 0% as configured -- ✅ Both tokens active and available -- ✅ Pool fully operational after all testing phases - ---- - -### Operations Authority Transfer Testing Summary - -**All Tests Passed:** ✅ - -| Test | Expected | Actual | Result | -|------|----------|--------|--------| -| Transfer to new wallet | Success | Success | ✅ Pass | -| Old wallet rejected | Error | Error (Not operations authority) | ✅ Pass | -| Create token account | Success | Success | ✅ Pass | -| New wallet can withdraw | Success | Success (0.01 USDC) | ✅ Pass | -| Transfer back to original | Success | Success | ✅ Pass | -| Original wallet can deposit | Success | Success (0.01 USDC) | ✅ Pass | - -**Key Findings:** -1. ✅ Operations authority can be transferred to another wallet -2. ✅ Old authority is immediately revoked after transfer -3. ✅ New authority can exercise liquidity operations (withdraw/deposit) -4. ✅ Token accounts must be created for new authority before withdrawals -5. ✅ Authority can be transferred back (round-trip works) -6. ✅ Access control enforcement is immediate and correct -7. ✅ Useful for multi-signature setups or authority rotation - -**Operations Authority Transfer Transactions:** -- Transfer to new wallet: `W6bEKXiUs2y3W1LKn1s84MqEhCjyZgfQu64VsoaPBMTH8bijedV5oxAVWpYV7LQRkbutMPuxtGUSNaNbDUiZvKD` -- Token account creation: `5yKYJTG4SBYuVeKUWuL1NFM6EsZRyEFB4vae1Mx1JRt9WgwnQPgikXoxAog7BCpy7MCUxtZUNPxiyMavoxVCGDci` -- New wallet withdraw: `4P4CbFWuH5Lj4dvckNAmnTdVMZGq9oSTRShDUTXFtXPWXNPJ4QuYwHNohZGT7oazZeTyQ8T6L3KFmogcJ4KGmBiD` -- Transfer back to original: `2gAeBnkNpQb6uC71gpwscj6w7e2VYE3rFy79N4XEGYVEoSC1dKH3SBxQBLUMEzcHhUqd8BzYUgBUXvkktjxNuatD` -- Original wallet deposit: `37TKsDbHLHL49XXp3EpRUzzoK71DTCZfWp7xs5mZtHSb1NEkp61qYvUrAYqbBNPSEokQRTR3yMpZ1Ehn9k5hjR6w` - ---- - -## Post-Deployment Summary - -### Critical Addresses (SAVE THESE!) -``` -Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -Pool: 5QoFtuiVkt5cXcBxmPUGp1waq3b9H5Q9RgKTpV3NcjEh -Custom Token Mint: 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms -Custom Token Vault: EPFgtsNL4VVqwbh6cPwFoWCoqjW1WQvgbRAcitV49HYH -Custom Token Vault Token Account: 3y6MPgBvQqCS5EJkWE8w6dYwVLT3sue4s6inP8AoAkJv -USDC Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v -USDC Vault: GZHACYSwd3TWc4AVjWnHVwudVXrXWdh6qqKhgGcf1aFx -USDC Vault Token Account: 9XX1dwrvpxAqzZ8jEbE8NMmTA7nGK2eVApuoXJxPGrfS -``` - -### Pool Statistics -- **Liquidity:** 5 Custom Tokens + 5 USDC (~$10 total) -- **Fee Rate:** 0% (perfect 1:1 swaps) -- **Swaps Executed:** 2 (bidirectional test complete) -- **Status:** Live and operational ✅ - -### Next Steps -- [ ] Set up monitoring -- [ ] Update frontend with new addresses -- [ ] Communicate pool address to users -- [ ] Set up alerts for vault balances - ---- - -## Emergency Contacts & Commands - -### Pause Swaps -```bash -ts-node scripts/emergency-pause-swaps.ts -``` - -### Pause Liquidity Operations -```bash -ts-node scripts/emergency-pause-liquidity.ts -``` - -### Withdraw Emergency Liquidity -```bash -ts-node scripts/emergency-withdraw.ts -``` - ---- - -## Notes & Issues Encountered - -### Issue Log -1. `___________________` -2. `___________________` -3. `___________________` - -### Resolutions -1. `___________________` -2. `___________________` -3. `___________________` - ---- - -**Deployment Status:** ⏳ In Progress - -**Completion Date:** `___________________` - -**Deployed By:** `___________________` - -**Verified By:** `___________________` diff --git a/solana/runbooks/MAINNET_DEPLOYMENT-20260108.md b/solana/runbooks/MAINNET_DEPLOYMENT-20260108.md deleted file mode 100644 index 3acd1d5..0000000 --- a/solana/runbooks/MAINNET_DEPLOYMENT-20260108.md +++ /dev/null @@ -1,870 +0,0 @@ -# Mainnet Deployment Runbook - Liquidity Pool Program - -**Status:** In Progress -**Date Started:** 2026-01-08 -**Deployer:** DxRhDVvDbUhM5jCkyVsFoG5qoydnUFkkkvx5DzPfjahN - ---- - -## Overview - -This runbook guides you through deploying the liquidity pool program to Solana mainnet and initializing it with your custom token and USDC. - -**Program ID:** `pqgqKahpG1y2wsgxFhzaAnkV1cL9vk8MSg9qm4q646F` - ---- - -## Key Information - -### Addresses -- **Deployer Wallet:** `DxRhDVvDbUhM5jCkyVsFoG5qoydnUFkkkvx5DzPfjahN` -- **Custom Token Mint:** `5AMAA9JV9H97YYVxx8F6FsCMmTwXSuTTQneiup4RYAUQ` -- **Custom Token Decimals:** `6` -- **USDC Mint:** `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v` -- **Pool PDA:** `CrDL9SoCyW1tBgn8k7rgGSpWhnszneWDbvKvqPAU4PL9` -- **Custom Token Vault:** `3vxe5BnJUWNz3kgSLXKaGuibTnjofxgGuAjhpMeEq95s` -- **Custom Token Vault Token Account:** `ZR8euZnAt7duoF7PfEqkq6ZqFJmaLQzKqEWAmozH4uq` -- **USDC Vault:** `2bQv8iFVXm9Z6wJk7KMFhhtLegNFZPtcDeJc5qrwJNqZ` -- **USDC Vault Token Account:** `YioohQk1msG36osqTZ9bUG9GwaygVpq9ACQ7gUrtUHr` - -### Configuration -- **Fee Rate:** 0 basis points (0% - for 1:1 swaps) -- **Operations Authority:** `7GSEFPKdHUZabc4qJkrrEmE4ZVVC1QJ721HmQTQBaUTw` (Squad multisig) -- **Pause Authority:** `7GSEFPKdHUZabc4qJkrrEmE4ZVVC1QJ721HmQTQBaUTw` (Squad multisig) -- **Fee Recipient:** `DxRhDVvDbUhM5jCkyVsFoG5qoydnUFkkkvx5DzPfjahN` (Deployer wallet) - ---- - -## PHASE 1: Pre-Deployment Setup - -### ✅ Step 1.0: Install Solana Development Tools - -Follow the official Solana installation guide (installs Rust, Solana CLI, and Anchor Framework): - -**Guide:** https://solana.com/docs/intro/installation - -**After installation, verify:** -```bash -solana --version -anchor --version -spl-token --version -``` - -**Tested versions:** -- Solana CLI: 2.2.21+ -- Anchor CLI: 0.31.1+ -- SPL Token CLI: 5.3.0+ (included with Solana CLI) - -**Result:** -- [x] Completed -- Solana version: `2.2.21` -- Anchor version: `0.31.1` -- SPL Token version: `5.3.0` -- Notes: All versions verified and compatible - ---- - -### ✅ Step 1.1: Configure Environment to Solana Mainnet -**Command:** -```bash -solana config set -u m -``` - ---- - -### ✅ Step 1.2: Get Wallet Address -**Command:** -```bash -solana address -``` - -**Result:** -- [x] Completed -- Wallet Address: `DxRhDVvDbUhM5jCkyVsFoG5qoydnUFkkkvx5DzPfjahN` - -Fund your wallet with enough SOL if needed. You will likely need about 4-5 SOL. - ---- - -### ✅ Step 1.3: Check SOL Balance -**Command:** -```bash -solana balance -``` - -**Expected:** At least 4-5 SOL - -**Result:** -- [x] Completed -- Balance: `4.5` SOL -- Notes: `___________________` - ---- - -### ✅ Step 1.4: Build the Program -**Command:** -```bash -anchor clean -anchor build -``` - -**Result:** -- [x] Completed -- Notes: Build successful - ---- - -### ✅ Step 1.5: Verify Program Build -**Command:** -```bash -# Check that the program is built -ls -lh target/deploy/scaas_liquidity.so - -# Verify program keypair matches Anchor.toml -solana address -k target/deploy/scaas_liquidity-keypair.json - -# Compare with what's in Anchor.toml [programs.mainnet] section -cat Anchor.toml | grep "scaas_liquidity =" -``` - -**Important:** The keypair address MUST match the program ID in Anchor.toml! - -**Common Issues:** -- If they don't match, you likely ran `anchor clean` and lost the original keypair -- For upgrades: You need the original keypair file from the initial deployment -- For new deployments: Update Anchor.toml and lib.rs `declare_id!` to match the generated keypair - -**Result:** -- [x] Completed -- Program Size: (verified) -- Program ID from keypair: `pqgqKahpG1y2wsgxFhzaAnkV1cL9vk8MSg9qm4q646F` -- Program ID in Anchor.toml: `pqgqKahpG1y2wsgxFhzaAnkV1cL9vk8MSg9qm4q646F` -- IDs Match: `Yes` -- Notes: All program IDs verified and matching - ---- - -### ✅ Step 1.6: Verify Custom Token Details -**Commands:** -```bash -# Check your token mint address -spl-token accounts - -# Get specific token balance and verify decimals -spl-token account-info - -# If you have no token account for the token mint, you may create it with -spl-token create-account -``` - -**Result:** -- [ ] Completed -- Custom Token Mint: `___________________` -- Custom Token Decimals: `___` -- Your Token Account: `___________________` -- Your Token Balance: `___________________` -- Notes: `___________________` - ---- - -## PHASE 2: Program Deployment - -### ✅ Step 2.1: Deploy Program to Mainnet -**Command:** -```bash -anchor deploy --provider.cluster mainnet -``` - -**Result:** -- [x] Completed -- Transaction Signature: `5whPL6zDk3bnCSXfqAFNEe5CJLyNsXsyMXV2apq7sopz1GbrbxidHspGnD4onLaerGLSB4dsrgSrhfdHoYptzZZJ` -- Program ID: `pqgqKahpG1y2wsgxFhzaAnkV1cL9vk8MSg9qm4q646F` -- Deployment Cost: (to be calculated from balance change) -- Timestamp: `2026-01-08` -- Notes: Deploy success - ---- - -### ✅ Step 2.3: Verify Deployment -**Command:** -```bash -solana program show GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -``` - -**Result:** -- [x] Completed -- Program Exists: `Yes` -- **Program Id**: `pqgqKahpG1y2wsgxFhzaAnkV1cL9vk8MSg9qm4q646F` -- **Owner**: `BPFLoaderUpgradeab1e11111111111111111111111` -- **ProgramData Address**: `DTm7oAyMA5VZEthWxYVz4T4hC7rReH5gWAVndi7X38hc` -- **Authority**: `DxRhDVvDbUhM5jCkyVsFoG5qoydnUFkkkvx5DzPfjahN` -- **Last Deployed In Slot**: `392183756` -- **Data Length**: `462928 bytes` -- **Balance**: `3.22318296 SOL` -- Notes: Program successfully deployed and verified on mainnet - ---- - -## PHASE 3: Initialize Pool - -### ✅ Step 3.1: Install TypeScript Dependencies -**Command:** -```bash -yarn add -D ts-node typescript @types/node -``` - -**Result:** -- [ ] Completed -- Notes: `___________________` - ---- - -### ✅ Step 3.2: Set Environment Variables (Optional) -**Command:** -```bash -# Export these in your shell (or add to ~/.bashrc or ~/.zshrc) -export ANCHOR_PROVIDER_URL="https://api.mainnet-beta.solana.com" -export ANCHOR_WALLET="$HOME/.config/solana/prod-deploy.json" -``` - -**Note:** The scripts will use these defaults if not set, so this step is optional. But it's good practice to set them explicitly. - -**Result:** -- [ ] Completed -- Notes: `___________________` - ---- - -### ✅ Step 3.3: Create Initialization Script -**File:** `scripts/01-initialize-pool.ts` - -Created at `scripts/01-initialize-pool.ts`. - -**Usage:** -```bash -yarn ts-node scripts/01-initialize-pool.ts -``` - -**Arguments:** -- `FEE_RATE_BPS`: Fee rate in basis points (0-1000) - - 0 = 0% fee (1:1 swaps) - - 10 = 0.1% fee - - 100 = 1% fee - - 1000 = 10% fee (maximum) - -**Result:** -- [ ] Completed -- Script Created: `___________________` -- Notes: `___________________` - ---- - -### ✅ Step 3.4: Run Pool Initialization -**Command:** -```bash -# For 0% fee (1:1 swaps) -yarn ts-node scripts/01-initialize-pool.ts 0 - -# Or for 1% fee -yarn ts-node scripts/01-initialize-pool.ts 100 -``` - -**Result:** -- [x] Completed -- Fee Rate Used: `0` bps -- Transaction Signature: `4K3GHRFcfirJhvWGf6ztqebSAjx5enab49Ymx9TRjs94mNA3wcQSXmcqk9m4v9uYrdKMk2bqMTxRKxKYNpDHHaEm` -- Pool PDA: `CrDL9SoCyW1tBgn8k7rgGSpWhnszneWDbvKvqPAU4PL9` -- Timestamp: `2026-01-08` -- Notes: Pool initialized successfully with 0% fee for 1:1 swaps - ---- - -### ✅ Step 3.5: Verify Pool State -**Command:** -```bash -ts-node scripts/verify-pool.ts -``` - -**Result:** -- [ ] Completed -- Operations Authority: `___________________` -- Pause Authority: `___________________` -- Fee Recipient: `___________________` -- Fee Rate: `___________________` bps -- Swaps Paused: `___________________` -- Liquidity Paused: `___________________` -- Supported Tokens Count: `___________________` -- Notes: `___________________` - ---- - -## PHASE 4: Add Custom Token - -### ✅ Step 4.1: Add Custom Token to Pool -**Note:** We use a universal `02-add-token.ts` script that works for any token. - -**Command:** -```bash -yarn ts-node scripts/02-add-token.ts 5AMAA9JV9H97YYVxx8F6FsCMmTwXSuTTQneiup4RYAUQ -``` - -**Result:** -- [x] Completed -- Transaction Signature: `4mnyk5f2d8DZnMmYNvbgxheeka7skmzd9coNoXqVNBpxmz11i6z9WhyATkg2csJ6jf4dbT9FnS2Ya3odAXF5q7fF` -- Custom Token Vault PDA: `3vxe5BnJUWNz3kgSLXKaGuibTnjofxgGuAjhpMeEq95s` -- Custom Token Vault Token Account: `ZR8euZnAt7duoF7PfEqkq6ZqFJmaLQzKqEWAmozH4uq` -- Fee Recipient Token Account: `4EPEmWxcw1bAjskVStpWpuDzstT8Vuv67r7j1aCFQhAp` -- Timestamp: `2026-01-08` -- Notes: Custom token 5AMAA9JV9H97YYVxx8F6FsCMmTwXSuTTQneiup4RYAUQ added successfully - ---- - -### ✅ Step 4.2: Verify Custom Token Added -**Result:** -- [x] Completed -- Token in Supported List: `Yes` -- Vault Exists: `Yes` -- Vault Token Account Exists: `Yes` -- Fee Recipient ATA Created: `Yes` -- Notes: Pool now has 2 supported tokens (Custom Token + USDC) - ---- - -## PHASE 5: Add USDC - -### ✅ Step 5.1: Add USDC to Pool -**Note:** We use the same `02-add-token.ts` script for all tokens. - -**Command:** -```bash -yarn ts-node scripts/02-add-token.ts EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v -``` - -**Result:** -- [x] Completed -- Transaction Signature: `4PK7HfJZY7cy8pbQCQcMEDzmgiJgApY1A1FZng3hguTUYupe2QVSvMEyXGzF5UFvxaF5hmBjavQxQM1qjuu9okHt` -- USDC Vault PDA: `2bQv8iFVXm9Z6wJk7KMFhhtLegNFZPtcDeJc5qrwJNqZ` -- USDC Vault Token Account: `YioohQk1msG36osqTZ9bUG9GwaygVpq9ACQ7gUrtUHr` -- Fee Recipient Token Account: `C9jw5StZLXWwM6N7NGqfxeZfvKmToKivT74148EnNGBJ` -- Timestamp: `2026-01-08` -- Notes: USDC added successfully to pool - ---- - -### ✅ Step 5.2: Verify USDC Added -**Result:** -- [x] Completed -- Token in Supported List: `Yes` -- Vault Exists: `Yes` -- Vault Token Account Exists: `Yes` -- Fee Recipient ATA Created: `Yes` -- Notes: Pool verified with 1 supported token (USDC), 0 balance - ---- - -## PHASE 6: Deposit Liquidity - -### ✅ Step 6.1: Check Token Balances -**Commands:** -```bash -# Check your custom token balance -spl-token balance 5AMAA9JV9H97YYVxx8F6FsCMmTwXSuTTQneiup4RYAUQ - -# Check your USDC balance -spl-token balance EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v -``` - -Fund your wallet with your custom token and USDC, e.g. 10 of each. - -**Result:** -- [ ] Completed -- Custom Token Balance: `___________________` -- USDC Balance: `___________________` -- Notes: `___________________` - ---- - -### ✅ Step 6.2: Deposit Custom Token Liquidity -**Planned Amount:** `10000` tokens - -**Note:** Custom token liquidity was added via direct transfer to the vault token account instead of using the deposit-liquidity script. - -**Method Used:** Direct SPL token transfer - -**Transfer Details:** -- From: External account with token supply -- To: Vault Token Account `ZR8euZnAt7duoF7PfEqkq6ZqFJmaLQzKqEWAmozH4uq` -- Transfer 1: 1 token -- Transfer 2: 9999 tokens -- Total: 10000 tokens - -**Result:** -- [x] Completed -- Transaction Signature: `(direct transfers - not via program)` -- Amount Deposited: `10000` tokens -- Vault Balance After: `10000` tokens -- Timestamp: `2026-01-08` -- Notes: Direct transfers bypass program deposit tracking but add liquidity to vault. For future deposits, prefer using deposit-liquidity script for proper tracking. - ---- - -### ✅ Step 6.3: Deposit USDC Liquidity -**Planned Amount:** `0` USDC (skipped for initial deployment) - -**Note:** USDC liquidity will be added organically through swaps. Starting with only custom token liquidity allows users to swap USDC → Custom Token. - -**Result:** -- [x] Completed (skipped) -- Transaction Signature: `N/A` -- Amount Deposited: `0` USDC -- Vault Balance After: `0` USDC -- Timestamp: `2026-01-08` -- Notes: Pool launched with asymmetric liquidity - 10,000 Custom Tokens only. USDC will accumulate as users swap. - ---- - -## PHASE 7: Final Verification - -### ✅ Step 7.1: Verify Complete Pool State -**Command:** -```bash -yarn ts-node scripts/verify-pool.ts -``` - -**Result:** -- [x] Completed -- Pool Initialized: `Yes` -- Supported Tokens: `2 (Custom Token + USDC)` -- Custom Token Vault Balance: `10,000 tokens` -- USDC Vault Balance: `0 USDC` -- Total Liquidity USD Value: `~$10,000 (assuming 1:1 peg)` -- Notes: Pool verified and ready for swaps. Asymmetric liquidity - only custom token side funded initially. - ---- - -### ✅ Step 7.2: Test Swap (Optional but Recommended) -**Test:** Small swap of 1 USDC -> Custom Token - -**Command:** -```bash -yarn ts-node scripts/04-test-swap.ts EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v 5AMAA9JV9H97YYVxx8F6FsCMmTwXSuTTQneiup4RYAUQ 1 -``` - -**Result:** -- [x] Completed -- Transaction Signature: `3wexohabLBTZTjrA8knVqU6Td6aGrioYJAo61SpStchg4AUc15AtyobYNMSovYTrHCJPdbx8jmaa8cRQhhiy9r5` -- Input: `1` USDC -- Output: `1` Custom Token -- Exchange Rate: `1:1 (0% fee)` -- Timestamp: `2026-01-08` -- Notes: Swap successful. Pool now has 1 USDC and 9,999 Custom Tokens - ---- - -### ✅ Step 7.3: Test Reverse Swap (Optional but Recommended) -**Test:** Reverse swap of 1 Custom Token -> USDC - -**Command:** -```bash -yarn ts-node scripts/04-test-swap.ts 5AMAA9JV9H97YYVxx8F6FsCMmTwXSuTTQneiup4RYAUQ EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v 1 -``` - -**Result:** -- [x] Completed -- Transaction Signature: `C4XLd2L1sGiifWp8c4ALrwXA6AbNobhTc2ZiogiizCWmQ4Wtchx3YjRL7iUWHpLXKhcEQJZejM2WNDyMTmwDHCE` -- Input: `1` Custom Token -- Output: `1` USDC -- Exchange Rate: `1:1 (0% fee)` -- Timestamp: `2026-01-08` -- Notes: Swap successful (confirmation timed out but transaction succeeded). Pool restored to original state: 0 USDC and 10,000 Custom Tokens - ---- - -## PHASE 8: Transfer Pause Authority to Squad Multisig - -### ✅ Step 8.1: Transfer Pause Authority to Squad - -**Squad Multisig Address:** `7GSEFPKdHUZabc4qJkrrEmE4ZVVC1QJ721HmQTQBaUTw` - -**Command:** -```bash -yarn ts-node scripts/update-pause-authority.ts 7GSEFPKdHUZabc4qJkrrEmE4ZVVC1QJ721HmQTQBaUTw -``` - -**Result:** -- [x] Completed -- Transaction Signature: `4rzuiFqW7uNLQcC5PoXPAsFJ8tMLrFLasQotZDvAwRStJct4b95a43GyMo4BSUf2jgr9paeMiaZee4sKGZw1BZpG` -- Old Pause Authority: `DxRhDVvDbUhM5jCkyVsFoG5qoydnUFkkkvx5DzPfjahN` (Deployer wallet) -- New Pause Authority: `7GSEFPKdHUZabc4qJkrrEmE4ZVVC1QJ721HmQTQBaUTw` (Squad multisig) -- Timestamp: `2026-01-08` -- Notes: Pause authority successfully transferred to Squad multisig for enhanced security - ---- - -### ✅ Step 8.2: Verify Authority Transfer - -**Command:** -```bash -yarn ts-node scripts/view-pool-state.ts -``` - -**Result:** -- [x] Completed -- Pause Authority Verified: `7GSEFPKdHUZabc4qJkrrEmE4ZVVC1QJ721HmQTQBaUTw` -- Operations Authority: `DxRhDVvDbUhM5jCkyVsFoG5qoydnUFkkkvx5DzPfjahN` (Still with deployer) -- Notes: Pause operations now require Squad multisig approval - ---- - -## PHASE 9: Transfer Operations Authority to Squad Multisig - -### ✅ Step 9.1: Transfer Operations Authority to Squad - -**Squad Multisig Address:** `7GSEFPKdHUZabc4qJkrrEmE4ZVVC1QJ721HmQTQBaUTw` - -**Command:** -```bash -yarn ts-node scripts/update-operations-authority.ts 7GSEFPKdHUZabc4qJkrrEmE4ZVVC1QJ721HmQTQBaUTw -``` - -**Result:** -- [x] Completed -- Transaction Signature: `3GHiRxe4K13FnupTCsYpcZ57Z6VaiU4maq3nKehCSuv8FC7nMPKYZB36vLcDSFVjN5pLVxNcZ2zNjQ5cAPGr958w` -- Old Operations Authority: `DxRhDVvDbUhM5jCkyVsFoG5qoydnUFkkkvx5DzPfjahN` (Deployer wallet) -- New Operations Authority: `7GSEFPKdHUZabc4qJkrrEmE4ZVVC1QJ721HmQTQBaUTw` (Squad multisig) -- Timestamp: `2026-01-08` -- Notes: Operations authority successfully transferred to Squad multisig for enhanced security - ---- - -### ✅ Step 9.2: Verify Authority Transfer - -**Command:** -```bash -yarn ts-node scripts/view-pool-state.ts -``` - -**Result:** -- [x] Completed -- Operations Authority Verified: `7GSEFPKdHUZabc4qJkrrEmE4ZVVC1QJ721HmQTQBaUTw` -- Pause Authority Verified: `7GSEFPKdHUZabc4qJkrrEmE4ZVVC1QJ721HmQTQBaUTw` -- Notes: Both critical authorities now require Squad multisig approval for maximum security - ---- - -## PHASE 10: Transfer Program Upgrade Authority to Squad Multisig - -### ✅ Step 10.1: Verify Current Upgrade Authority - -**Command:** -```bash -solana program show pqgqKahpG1y2wsgxFhzaAnkV1cL9vk8MSg9qm4q646F -``` - -**Result:** -- Authority: `DxRhDVvDbUhM5jCkyVsFoG5qoydnUFkkkvx5DzPfjahN` (Deployer wallet) - ---- - -### ✅ Step 10.2: Transfer Upgrade Authority to Squad - -**Squad Multisig Address:** `7GSEFPKdHUZabc4qJkrrEmE4ZVVC1QJ721HmQTQBaUTw` - -**Command:** -```bash -solana program set-upgrade-authority pqgqKahpG1y2wsgxFhzaAnkV1cL9vk8MSg9qm4q646F --new-upgrade-authority /Users/salioudiallo/.config/solana/upgrade-authority-keypair.json -``` - -**Result:** -- [x] Completed -- Account Type: Program -- New Authority: `7GSEFPKdHUZabc4qJkrrEmE4ZVVC1QJ721HmQTQBaUTw` (Squad multisig) -- Timestamp: `2026-01-08` -- Notes: Program upgrade authority successfully transferred to Squad multisig - ---- - -### ✅ Step 10.3: Verify Upgrade Authority Transfer - -**Command:** -```bash -solana program show pqgqKahpG1y2wsgxFhzaAnkV1cL9vk8MSg9qm4q646F -``` - -**Result:** -- [x] Completed -- Program Id: `pqgqKahpG1y2wsgxFhzaAnkV1cL9vk8MSg9qm4q646F` -- Authority Verified: `7GSEFPKdHUZabc4qJkrrEmE4ZVVC1QJ721HmQTQBaUTw` -- ProgramData Address: `DTm7oAyMA5VZEthWxYVz4T4hC7rReH5gWAVndi7X38hc` -- Data Length: `462928 bytes` -- Balance: `3.22318296 SOL` -- Notes: All program upgrades now require Squad multisig approval - ---- - -## PHASE 11: Publish IDL to Mainnet - -### ✅ Step 11.1: Publish IDL - -**Command:** -```bash -anchor idl init pqgqKahpG1y2wsgxFhzaAnkV1cL9vk8MSg9qm4q646F --filepath target/idl/scaas_liquidity.json --provider.cluster mainnet -``` - -**Result:** -- [x] Completed -- IDL Data Length: `3348 bytes` -- IDL Account Created: `GaoCsbFejpUYNjnLaHYh4bc4QPtth7Gq5oCtsJ6d4PKc` -- Timestamp: `2026-01-08` -- Notes: IDL successfully published to mainnet, making program interface publicly available - ---- - -### ✅ Step 11.2: Verify IDL Published - -**IDL Explorer URL:** -``` -https://www.orbmarkets.io/address/pqgqKahpG1y2wsgxFhzaAnkV1cL9vk8MSg9qm4q646F/anchor-idl -``` - -**Result:** -- [x] Completed -- IDL Publicly Accessible: Yes -- Notes: Program interface now available for wallets, explorers, and frontends to interact with the liquidity pool - ---- - -## Post-Deployment Summary - -### Total Costs -- Program Deployment: `___________________` SOL -- Pool Initialization: `___________________` SOL -- Add Custom Token: `___________________` SOL -- Add USDC: `___________________` SOL -- Deposit Custom Token: `___________________` SOL -- Deposit USDC: `___________________` SOL -- Test Swap: `___________________` SOL -- **Total:** `___________________` SOL - -### Critical Addresses (SAVE THESE!) -``` -Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -Pool: ___________________ -Custom Token Mint: ___________________ -Custom Token Vault: ___________________ -Custom Token Vault Token Account: ___________________ -USDC Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v -USDC Vault: ___________________ -USDC Vault Token Account: ___________________ -``` - -### Next Steps -- [ ] Set up monitoring -- [ ] Update frontend with new addresses -- [ ] Communicate pool address to users -- [ ] Set up alerts for vault balances - ---- - -## Emergency Contacts & Commands - -### Pause Swaps -```bash -ts-node scripts/emergency-pause-swaps.ts -``` - -### Pause Liquidity Operations -```bash -ts-node scripts/emergency-pause-liquidity.ts -``` - -### Withdraw Emergency Liquidity -```bash -ts-node scripts/emergency-withdraw.ts -``` - ---- - -## APPENDIX: Upgrading an Existing Program - -If you need to upgrade an already-deployed program (bug fixes, new features, etc.), follow these steps: - -### Step U1: Obtain the Original Program Keypair - -**Critical:** You MUST have the original keypair from the initial deployment. - -```bash -# Get scaas_liquidity-keypair.json from whoever did the original deployment -# Place it at: target/deploy/scaas_liquidity-keypair.json -``` - -**Verify the keypair:** -```bash -solana address -k target/deploy/scaas_liquidity-keypair.json -# Should output your deployed program ID -``` - ---- - -### Step U2: Make Your Code Changes - -Edit the program code as needed in `programs/scaas-liquidity/src/` - -**Important:** If you're adding new instructions or changing account structures, test thoroughly on devnet first! - ---- - -### Step U3: Build the Updated Program - -```bash -anchor build -``` - -**Result:** -- [ ] Completed -- Build successful: `___________________` -- Program size: `___________________` -- Notes: `___________________` - ---- - -### Step U4: Verify Program ID Match - -```bash -# Check keypair address -solana address -k target/deploy/scaas_liquidity-keypair.json - -# Check Anchor.toml -cat Anchor.toml | grep "scaas_liquidity =" - -# Check lib.rs declare_id! -grep "declare_id!" programs/scaas-liquidity/src/lib.rs -``` - -**All three MUST show the same program ID!** - -**Result:** -- [ ] Completed -- Keypair ID: `___________________` -- Anchor.toml ID: `___________________` -- lib.rs ID: `___________________` -- All match: `___________________` - ---- - -### Step U5: Upgrade the Program - -**Command:** -```bash -# Use 'upgrade' not 'deploy' for existing programs -anchor upgrade --program-id GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv target/deploy/scaas_liquidity.so --provider.cluster mainnet -``` - -**What this does:** -- Replaces the program code at the existing address -- Maintains the same program ID -- Preserves all existing accounts (pool, vaults, etc.) -- Requires upgrade authority (your wallet) - -**Result:** -- [ ] Completed -- Transaction Signature: `___________________` -- Upgrade successful: `___________________` -- Timestamp: `___________________` -- Notes: `___________________` - ---- - -### Step U6: Verify the Upgrade - -```bash -# Check program info -solana program show GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv - -# Look for "Last Deployed In Slot" - should be recent -``` - -**Result:** -- [ ] Completed -- Last Deployed Slot: `___________________` -- Data Length: `___________________` -- Authority: `___________________` - ---- - -### Step U7: Update the IDL Onchain - -```bash -# Update the onchain IDL -anchor idl upgrade --filepath target/idl/scaas_liquidity.json GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv --provider.cluster mainnet - -# Verify -anchor idl fetch GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv --provider.cluster mainnet -``` - -**Result:** -- [ ] Completed -- IDL updated: `___________________` -- Transaction Signature: `___________________` - ---- - -### Step U8: Test the Upgrade - -Run your test scripts to verify the upgrade works: - -```bash -# Verify pool state -yarn ts-node scripts/verify-pool.ts - -# Test swap (if applicable) -yarn ts-node scripts/04-test-swap.ts -``` - -**Result:** -- [ ] Completed -- Pool state verified: `___________________` -- Test swap successful: `___________________` -- All functionality working: `___________________` -- Notes: `___________________` - ---- - -### Upgrade Summary - -**Upgrade Date:** `___________________` -**Upgraded By:** `___________________` -**Changes Made:** -- `___________________` -- `___________________` -- `___________________` - -**Upgrade Transaction:** `___________________` -**IDL Update Transaction:** `___________________` - -**Testing Results:** -- `___________________` - ---- - -## Notes & Issues Encountered - -### Issue Log -1. `___________________` -2. `___________________` -3. `___________________` - -### Resolutions -1. `___________________` -2. `___________________` -3. `___________________` - ---- - -**Deployment Status:** ⏳ In Progress - -**Completion Date:** `___________________` - -**Deployed By:** `___________________` - -**Verified By:** `___________________` diff --git a/solana/runbooks/MAINNET_DEPLOYMENT_RUNBOOK.md b/solana/runbooks/MAINNET_DEPLOYMENT_RUNBOOK.md deleted file mode 100644 index f1a180f..0000000 --- a/solana/runbooks/MAINNET_DEPLOYMENT_RUNBOOK.md +++ /dev/null @@ -1,715 +0,0 @@ -# Mainnet Deployment Runbook - Liquidity Pool Program - -**Status:** In Progress -**Date Started:** 2025-12-19 -**Deployer:** [To be filled] - ---- - -## Overview - -This runbook guides you through deploying the liquidity pool program to Solana mainnet and initializing it with your custom token and USDC. - -**Program ID:** `GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv` - ---- - -## Key Information - -### Addresses -- **Deployer Wallet:** `___________________` -- **Custom Token Mint:** `___________________` -- **Custom Token Decimals:** `___` -- **USDC Mint:** `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v` -- **Pool PDA:** `___________________` -- **Custom Token Vault:** `___________________` -- **Custom Token Vault Token Account:** `___________________` -- **USDC Vault:** `___________________` -- **USDC Vault Token Account:** `___________________` - -### Configuration -- **Fee Rate:** 0 basis points (0% - for 1:1 swaps) -- **Operations Authority:** Deployer wallet -- **Pause Authority:** Deployer wallet -- **Fee Recipient:** Deployer wallet - ---- - -## PHASE 1: Pre-Deployment Setup - -### ✅ Step 1.0: Install Solana Development Tools - -Follow the official Solana installation guide (installs Rust, Solana CLI, and Anchor Framework): - -**Guide:** https://solana.com/docs/intro/installation - -**After installation, verify:** -```bash -solana --version -anchor --version -spl-token --version -``` - -**Tested versions:** -- Solana CLI: 2.2.21+ -- Anchor CLI: 0.31.1+ -- SPL Token CLI: 5.3.0+ (included with Solana CLI) - -**Result:** -- [ ] Completed -- Solana version: `___________________` -- Anchor version: `___________________` -- SPL Token version: `___________________` -- Notes: `___________________` - ---- - -### ✅ Step 1.1: Configure Environment to Solana Mainnet -**Command:** -```bash -solana config set -u m -``` - ---- - -### ✅ Step 1.2: Get Wallet Address -**Command:** -```bash -solana address -``` - -**Result:** -- [ ] Completed -- Wallet Address: `___________________` - -Fund your wallet with enough SOL if needed. You will likely need about 4-5 SOL. - ---- - -### ✅ Step 1.3: Check SOL Balance -**Command:** -```bash -solana balance -``` - -**Expected:** At least 4-5 SOL - -**Result:** -- [ ] Completed -- Balance: `___________________` SOL -- Notes: `___________________` - ---- - -### ✅ Step 1.4: Build the Program -**Command:** -```bash -anchor clean -anchor build -``` - ---- - -### ✅ Step 1.5: Verify Program Build -**Command:** -```bash -# Check that the program is built -ls -lh target/deploy/scaas_liquidity.so - -# Verify program keypair matches Anchor.toml -solana address -k target/deploy/scaas_liquidity-keypair.json - -# Compare with what's in Anchor.toml [programs.mainnet] section -cat Anchor.toml | grep "scaas_liquidity =" -``` - -**Important:** The keypair address MUST match the program ID in Anchor.toml! - -**Common Issues:** -- If they don't match, you likely ran `anchor clean` and lost the original keypair -- For upgrades: You need the original keypair file from the initial deployment -- For new deployments: Update Anchor.toml and lib.rs `declare_id!` to match the generated keypair - -**Result:** -- [ ] Completed -- Program Size: `___________________` -- Program ID from keypair: `___________________` -- Program ID in Anchor.toml: `___________________` -- IDs Match: `___________________` -- Notes: `___________________` - ---- - -### ✅ Step 1.6: Verify Custom Token Details -**Commands:** -```bash -# Check your token mint address -spl-token accounts - -# Get specific token balance and verify decimals -spl-token account-info - -# If you have no token account for the token mint, you may create it with -spl-token create-account -``` - -**Result:** -- [ ] Completed -- Custom Token Mint: `___________________` -- Custom Token Decimals: `___` -- Your Token Account: `___________________` -- Your Token Balance: `___________________` -- Notes: `___________________` - ---- - -## PHASE 2: Program Deployment - -### ✅ Step 2.1: Deploy Program to Mainnet -**Command:** -```bash -anchor deploy --provider.cluster mainnet -``` - -**Result:** -- [ ] Completed -- Transaction Signature: `___________________` -- Program ID: `___________________` -- Deployment Cost: `___________________` SOL -- Timestamp: `___________________` -- Notes: `___________________` - ---- - -### ✅ Step 2.3: Verify Deployment -**Command:** -```bash -solana program show GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -``` - -**Result:** -- [ ] Completed -- Program Exists: `___________________` -- **Program Id**: `___________________` -- **Owner**: `___________________` -- **ProgramData Address**: `___________________` -- **Authority**: `___________________` -- **Last Deployed In Slot**: `___________________` -- **Data Length**: `___________________` -- **Balance**: `___________________` SOL -- Notes: `___________________` - ---- - -## PHASE 3: Initialize Pool - -### ✅ Step 3.1: Install TypeScript Dependencies -**Command:** -```bash -yarn add -D ts-node typescript @types/node -``` - -**Result:** -- [ ] Completed -- Notes: `___________________` - ---- - -### ✅ Step 3.2: Set Environment Variables (Optional) -**Command:** -```bash -# Export these in your shell (or add to ~/.bashrc or ~/.zshrc) -export ANCHOR_PROVIDER_URL="https://api.mainnet-beta.solana.com" -export ANCHOR_WALLET="$HOME/.config/solana/id.json" -``` - -**Note:** The scripts will use these defaults if not set, so this step is optional. But it's good practice to set them explicitly. - -**Result:** -- [ ] Completed -- Notes: `___________________` - ---- - -### ✅ Step 3.3: Create Initialization Script -**File:** `scripts/01-initialize-pool.ts` - -Created at `scripts/01-initialize-pool.ts`. - -**Usage:** -```bash -yarn ts-node scripts/01-initialize-pool.ts -``` - -**Arguments:** -- `FEE_RATE_BPS`: Fee rate in basis points (0-1000) - - 0 = 0% fee (1:1 swaps) - - 10 = 0.1% fee - - 100 = 1% fee - - 1000 = 10% fee (maximum) - -**Result:** -- [ ] Completed -- Script Created: `___________________` -- Notes: `___________________` - ---- - -### ✅ Step 3.4: Run Pool Initialization -**Command:** -```bash -# For 0% fee (1:1 swaps) -yarn ts-node scripts/01-initialize-pool.ts 0 - -# Or for 1% fee -yarn ts-node scripts/01-initialize-pool.ts 100 -``` - -**Result:** -- [ ] Completed -- Fee Rate Used: `___________________` bps -- Transaction Signature: `___________________` -- Pool PDA: `___________________` -- Timestamp: `___________________` -- Notes: `___________________` - ---- - -### ✅ Step 3.5: Verify Pool State -**Command:** -```bash -ts-node scripts/verify-pool.ts -``` - -**Result:** -- [ ] Completed -- Operations Authority: `___________________` -- Pause Authority: `___________________` -- Fee Recipient: `___________________` -- Fee Rate: `___________________` bps -- Swaps Paused: `___________________` -- Liquidity Paused: `___________________` -- Supported Tokens Count: `___________________` -- Notes: `___________________` - ---- - -## PHASE 4: Add Custom Token - -### ✅ Step 4.1: Add Custom Token to Pool -**Note:** We use a universal `02-add-token.ts` script that works for any token. - -**Command:** -```bash -yarn ts-node scripts/02-add-token.ts -``` - -**Result:** -- [ ] Completed -- Transaction Signature: `___________________` -- Custom Token Vault PDA: `___________________` -- Custom Token Vault Token Account: `___________________` -- Fee Recipient Token Account: `___________________` -- Timestamp: `___________________` -- Notes: `___________________` - ---- - -### ✅ Step 4.2: Verify Custom Token Added -**Result:** -- [ ] Completed -- Token in Supported List: `___________________` -- Vault Exists: `___________________` -- Vault Token Account Exists: `___________________` -- Fee Recipient ATA Created: `___________________` -- Notes: `___________________` - ---- - -## PHASE 5: Add USDC - -### ✅ Step 5.1: Add USDC to Pool -**Note:** We use the same `02-add-token.ts` script for all tokens. - -**Command:** -```bash -yarn ts-node scripts/02-add-token.ts EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v -``` - -**Result:** -- [ ] Completed -- Transaction Signature: `___________________` -- USDC Vault PDA: `___________________` -- USDC Vault Token Account: `___________________` -- Fee Recipient Token Account: `___________________` -- Timestamp: `___________________` -- Notes: `___________________` - ---- - -### ✅ Step 5.2: Verify USDC Added -**Result:** -- [ ] Completed -- Token in Supported List: `___________________` -- Vault Exists: `___________________` -- Vault Token Account Exists: `___________________` -- Fee Recipient ATA Created: `___________________` -- Notes: `___________________` - ---- - -## PHASE 6: Deposit Liquidity - -### ✅ Step 6.1: Check Token Balances -**Commands:** -```bash -# Check your custom token balance -spl-token balance - -# Check your USDC balance -spl-token balance EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v -``` - -Fund your wallet with your custom token and USDC, e.g. 10 of each. - -**Result:** -- [ ] Completed -- Custom Token Balance: `___________________` -- USDC Balance: `___________________` -- Notes: `___________________` - ---- - -### ✅ Step 6.2: Seed Custom Token Liquidity -**Planned Amount:** `___________________` tokens - -**Note:** Liquidity is seeded by sending tokens directly to the vault token account with a standard SPL Token transfer. The vault token account address for each mint is printed by `scripts/verify-pool.ts`. - -**Command:** -```bash -# Look up the vault token account address -yarn ts-node scripts/verify-pool.ts - -# Transfer liquidity to it -spl-token transfer --fund-recipient --allow-unfunded-recipient -``` - -**Result:** -- [ ] Completed -- Transaction Signature: `___________________` -- Amount Transferred: `___________________` tokens -- Vault Balance After: `___________________` -- Timestamp: `___________________` -- Notes: `___________________` - ---- - -### ✅ Step 6.3: Seed USDC Liquidity -**Planned Amount:** `___________________` USDC - -**Command:** -```bash -spl-token transfer EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v --fund-recipient --allow-unfunded-recipient -``` - -**Result:** -- [ ] Completed -- Transaction Signature: `___________________` -- Amount Transferred: `___________________` USDC -- Vault Balance After: `___________________` -- Timestamp: `___________________` -- Notes: `___________________` - ---- - -## PHASE 7: Final Verification - -### ✅ Step 7.1: Verify Complete Pool State -**Command:** -```bash -yarn ts-node scripts/verify-pool.ts -``` - -**Result:** -- [ ] Completed -- Pool Initialized: `___________________` -- Supported Tokens: `___________________` -- Custom Token Vault Balance: `___________________` -- USDC Vault Balance: `___________________` -- Total Liquidity USD Value: `___________________` -- Notes: `___________________` - ---- - -### ✅ Step 7.2: Test Swap (Optional but Recommended) -**Test:** Small swap of 1 USDC -> Custom Token - -**Command:** -```bash -yarn ts-node scripts/04-test-swap.ts EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v 1 -``` - -**Result:** -- [ ] Completed -- Transaction Signature: `___________________` -- Input: `___________________` USDC -- Output: `___________________` Custom Token -- Exchange Rate: `___________________` -- Timestamp: `___________________` -- Notes: `___________________` - ---- - -### ✅ Step 7.3: Test Reverse Swap (Optional but Recommended) -**Test:** Reverse swap of 1 Custom Token -> USDC - -**Command:** -```bash -yarn ts-node scripts/04-test-swap.ts EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v 1 -``` - -**Result:** -- [ ] Completed -- Transaction Signature: `___________________` -- Input: `___________________` Custom Token -- Output: `___________________` USDC -- Exchange Rate: `___________________` -- Timestamp: `___________________` -- Notes: `___________________` - ---- - -## Post-Deployment Summary - -### Total Costs -- Program Deployment: `___________________` SOL -- Pool Initialization: `___________________` SOL -- Add Custom Token: `___________________` SOL -- Add USDC: `___________________` SOL -- Deposit Custom Token: `___________________` SOL -- Deposit USDC: `___________________` SOL -- Test Swap: `___________________` SOL -- **Total:** `___________________` SOL - -### Critical Addresses (SAVE THESE!) -``` -Program ID: GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv -Pool: ___________________ -Custom Token Mint: ___________________ -Custom Token Vault: ___________________ -Custom Token Vault Token Account: ___________________ -USDC Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v -USDC Vault: ___________________ -USDC Vault Token Account: ___________________ -``` - -### Next Steps -- [ ] Set up monitoring -- [ ] Update frontend with new addresses -- [ ] Communicate pool address to users -- [ ] Set up alerts for vault balances - ---- - -## Emergency Contacts & Commands - -### Pause Swaps -```bash -ts-node scripts/emergency-pause-swaps.ts -``` - -### Pause Liquidity Operations -```bash -ts-node scripts/emergency-pause-liquidity.ts -``` - -### Withdraw Emergency Liquidity -```bash -ts-node scripts/emergency-withdraw.ts -``` - ---- - -## APPENDIX: Upgrading an Existing Program - -If you need to upgrade an already-deployed program (bug fixes, new features, etc.), follow these steps: - -### Step U1: Obtain the Original Program Keypair - -**Critical:** You MUST have the original keypair from the initial deployment. - -```bash -# Get scaas_liquidity-keypair.json from whoever did the original deployment -# Place it at: target/deploy/scaas_liquidity-keypair.json -``` - -**Verify the keypair:** -```bash -solana address -k target/deploy/scaas_liquidity-keypair.json -# Should output your deployed program ID -``` - ---- - -### Step U2: Make Your Code Changes - -Edit the program code as needed in `programs/scaas-liquidity/src/` - -**Important:** If you're adding new instructions or changing account structures, test thoroughly on devnet first! - ---- - -### Step U3: Build the Updated Program - -```bash -anchor build -``` - -**Result:** -- [ ] Completed -- Build successful: `___________________` -- Program size: `___________________` -- Notes: `___________________` - ---- - -### Step U4: Verify Program ID Match - -```bash -# Check keypair address -solana address -k target/deploy/scaas_liquidity-keypair.json - -# Check Anchor.toml -cat Anchor.toml | grep "scaas_liquidity =" - -# Check lib.rs declare_id! -grep "declare_id!" programs/scaas-liquidity/src/lib.rs -``` - -**All three MUST show the same program ID!** - -**Result:** -- [ ] Completed -- Keypair ID: `___________________` -- Anchor.toml ID: `___________________` -- lib.rs ID: `___________________` -- All match: `___________________` - ---- - -### Step U5: Upgrade the Program - -**Command:** -```bash -# Use 'upgrade' not 'deploy' for existing programs -anchor upgrade --program-id GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv target/deploy/scaas_liquidity.so --provider.cluster mainnet -``` - -**What this does:** -- Replaces the program code at the existing address -- Maintains the same program ID -- Preserves all existing accounts (pool, vaults, etc.) -- Requires upgrade authority (your wallet) - -**Result:** -- [ ] Completed -- Transaction Signature: `___________________` -- Upgrade successful: `___________________` -- Timestamp: `___________________` -- Notes: `___________________` - ---- - -### Step U6: Verify the Upgrade - -```bash -# Check program info -solana program show GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv - -# Look for "Last Deployed In Slot" - should be recent -``` - -**Result:** -- [ ] Completed -- Last Deployed Slot: `___________________` -- Data Length: `___________________` -- Authority: `___________________` - ---- - -### Step U7: Update the IDL Onchain - -```bash -# Update the onchain IDL -anchor idl upgrade --filepath target/idl/scaas_liquidity.json GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv --provider.cluster mainnet - -# Verify -anchor idl fetch GadmXgM1J4NhkbqbpnAbEQxHssZAavWxG5uV6AHiLMHv --provider.cluster mainnet -``` - -**Result:** -- [ ] Completed -- IDL updated: `___________________` -- Transaction Signature: `___________________` - ---- - -### Step U8: Test the Upgrade - -Run your test scripts to verify the upgrade works: - -```bash -# Verify pool state -yarn ts-node scripts/verify-pool.ts - -# Test swap (if applicable) -yarn ts-node scripts/04-test-swap.ts -``` - -**Result:** -- [ ] Completed -- Pool state verified: `___________________` -- Test swap successful: `___________________` -- All functionality working: `___________________` -- Notes: `___________________` - ---- - -### Upgrade Summary - -**Upgrade Date:** `___________________` -**Upgraded By:** `___________________` -**Changes Made:** -- `___________________` -- `___________________` -- `___________________` - -**Upgrade Transaction:** `___________________` -**IDL Update Transaction:** `___________________` - -**Testing Results:** -- `___________________` - ---- - -## Notes & Issues Encountered - -### Issue Log -1. `___________________` -2. `___________________` -3. `___________________` - -### Resolutions -1. `___________________` -2. `___________________` -3. `___________________` - ---- - -**Deployment Status:** ⏳ In Progress - -**Completion Date:** `___________________` - -**Deployed By:** `___________________` - -**Verified By:** `___________________` diff --git a/solana/scripts/01-initialize-pool.ts b/solana/scripts/01-initialize-pool.ts deleted file mode 100644 index 4355027..0000000 --- a/solana/scripts/01-initialize-pool.ts +++ /dev/null @@ -1,170 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { Program } from "@coral-xyz/anchor"; -import { ScaasLiquidity } from "../target/types/scaas_liquidity"; -import { PublicKey, SystemProgram } from "@solana/web3.js"; - -async function main() { - // Parse command line arguments - const args = process.argv.slice(2); - - if (args.length === 0 || args[0] === "--help" || args[0] === "-h") { - console.log( - "Usage: yarn ts-node scripts/01-initialize-pool.ts " - ); - console.log(); - console.log("Arguments:"); - console.log(" FEE_RATE_BPS Fee rate in basis points (0-1000)"); - console.log(" Examples:"); - console.log(" 0 = 0% fee (1:1 swaps)"); - console.log(" 10 = 0.1% fee"); - console.log(" 100 = 1% fee"); - console.log(" 1000 = 10% fee (maximum)"); - console.log(); - console.log("Example:"); - console.log(" yarn ts-node scripts/01-initialize-pool.ts 0"); - console.log(); - console.log( - "Note: Make sure Anchor.toml [provider] cluster is set to mainnet-beta" - ); - process.exit(args.length === 0 ? 1 : 0); - } - - const feeRateBps = parseInt(args[0], 10); - - if (isNaN(feeRateBps) || feeRateBps < 0 || feeRateBps > 1000) { - console.error( - "❌ Error: Fee rate must be a number between 0 and 1000 basis points" - ); - console.error(" (0 = 0%, 100 = 1%, 1000 = 10%)"); - process.exit(1); - } - - // Set default environment variables if not already set - if (!process.env.ANCHOR_PROVIDER_URL) { - process.env.ANCHOR_PROVIDER_URL = "https://api.mainnet-beta.solana.com"; - } - if (!process.env.ANCHOR_WALLET) { - process.env.ANCHOR_WALLET = - require("os").homedir() + "/.config/solana/id.json"; - } - - // Load provider from environment - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - - const payer = provider.wallet as anchor.Wallet; - const program = anchor.workspace.scaasLiquidity as Program; - - console.log("=".repeat(60)); - console.log("INITIALIZING LIQUIDITY POOL"); - console.log("=".repeat(60)); - console.log(); - - // Derive PDAs - const [pool] = PublicKey.findProgramAddressSync( - [Buffer.from("liquidity_pool")], - program.programId - ); - - console.log("Configuration:"); - console.log("- Program ID:", program.programId.toString()); - console.log("- Pool PDA:", pool.toString()); - console.log("- Deployer/Authority:", payer.publicKey.toString()); - console.log(); - - // Check if pool already exists - try { - const poolAccount = await program.account.liquidityPool.fetch(pool); - console.log("❌ Pool already initialized!"); - console.log("Pool state:"); - console.log( - "- Operations Authority:", - poolAccount.operationsAuthority.toString() - ); - console.log("- Pause Authority:", poolAccount.pauseAuthority.toString()); - console.log("- Fee Recipient:", poolAccount.feeRecipient.toString()); - console.log("- Fee Rate:", poolAccount.feeRate.toNumber(), "bps"); - console.log("- Supported Tokens:", poolAccount.supportedTokens.length); - process.exit(0); - } catch (e) { - // Pool doesn't exist, continue with initialization - console.log("✓ Pool not yet initialized, proceeding..."); - } - - // Use fee rate from command line argument - const feeRate = new anchor.BN(feeRateBps); - - console.log(); - console.log("Initialization Parameters:"); - console.log( - "- Fee Rate:", - feeRate.toNumber(), - `basis points (${feeRate.toNumber() / 100}%)` - ); - console.log("- Operations Authority:", payer.publicKey.toString()); - console.log("- Pause Authority:", payer.publicKey.toString()); - console.log("- Fee Recipient:", payer.publicKey.toString()); - console.log(); - - console.log("Sending transaction..."); - - try { - const tx = await program.methods - .initialize(feeRate) - .accounts({ - pool: pool, - payer: payer.publicKey, - operationsAuthority: payer.publicKey, - pauseAuthority: payer.publicKey, - feeRecipient: payer.publicKey, - systemProgram: SystemProgram.programId, - } as any) - .rpc(); - - console.log("✅ Pool initialized successfully!"); - console.log(); - console.log("Transaction Details:"); - console.log("- Signature:", tx); - console.log("- Explorer:", `https://solscan.io/tx/${tx}`); - console.log(); - - // Fetch and display pool state - const poolAccount = await program.account.liquidityPool.fetch(pool); - - console.log("Pool State:"); - console.log( - "- Operations Authority:", - poolAccount.operationsAuthority.toString() - ); - console.log("- Pause Authority:", poolAccount.pauseAuthority.toString()); - console.log("- Fee Recipient:", poolAccount.feeRecipient.toString()); - console.log("- Fee Rate:", poolAccount.feeRate.toNumber(), "bps"); - console.log("- Swaps Paused:", poolAccount.swapsPaused); - console.log("- Liquidity Paused:", poolAccount.liquidityPaused); - console.log("- Supported Tokens:", poolAccount.supportedTokens.length); - console.log(); - - console.log("=".repeat(60)); - console.log("SAVE THESE ADDRESSES:"); - console.log("=".repeat(60)); - console.log("Pool:", pool.toString()); - console.log("=".repeat(60)); - } catch (error: any) { - console.error("❌ Error initializing pool:"); - console.error(error); - - if (error.logs) { - console.error("\nProgram Logs:"); - error.logs.forEach((log: string) => console.error(log)); - } - - process.exit(1); - } -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); diff --git a/solana/scripts/02-add-token.ts b/solana/scripts/02-add-token.ts deleted file mode 100644 index 01d63b0..0000000 --- a/solana/scripts/02-add-token.ts +++ /dev/null @@ -1,197 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { Program } from "@coral-xyz/anchor"; -import { ScaasLiquidity } from "../target/types/scaas_liquidity"; -import { PublicKey, SystemProgram } from "@solana/web3.js"; -import { - TOKEN_PROGRAM_ID, - ASSOCIATED_TOKEN_PROGRAM_ID, - getAssociatedTokenAddress, -} from "@solana/spl-token"; - -async function main() { - // Parse command line arguments - const args = process.argv.slice(2); - - if (args.length === 0 || args[0] === "--help" || args[0] === "-h") { - console.log( - "Usage: yarn ts-node scripts/02-add-token.ts " - ); - console.log(); - console.log("Arguments:"); - console.log(" TOKEN_MINT_ADDRESS The mint address of the token to add"); - console.log(); - console.log("Examples:"); - console.log(" # Add custom token"); - console.log( - " yarn ts-node scripts/02-add-token.ts 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms" - ); - console.log(); - console.log(" # Add USDC"); - console.log( - " yarn ts-node scripts/02-add-token.ts EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" - ); - process.exit(args.length === 0 ? 1 : 0); - } - - const mintAddress = args[0]; - - // Validate mint address - let mint: PublicKey; - try { - mint = new PublicKey(mintAddress); - } catch (error) { - console.error("❌ Error: Invalid mint address"); - process.exit(1); - } - - // Set default environment variables if not already set - if (!process.env.ANCHOR_PROVIDER_URL) { - process.env.ANCHOR_PROVIDER_URL = "https://api.mainnet-beta.solana.com"; - } - if (!process.env.ANCHOR_WALLET) { - process.env.ANCHOR_WALLET = - require("os").homedir() + "/.config/solana/id.json"; - } - - // Load provider from environment - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - - const payer = provider.wallet as anchor.Wallet; - const program = anchor.workspace.scaasLiquidity as Program; - - console.log("=".repeat(60)); - console.log("ADDING TOKEN TO POOL"); - console.log("=".repeat(60)); - console.log(); - - // Derive PDAs - const [pool] = PublicKey.findProgramAddressSync( - [Buffer.from("liquidity_pool")], - program.programId - ); - - const [vault] = PublicKey.findProgramAddressSync( - [Buffer.from("token_vault"), pool.toBuffer(), mint.toBuffer()], - program.programId - ); - - const [vaultTokenAccount] = PublicKey.findProgramAddressSync( - [Buffer.from("vault_token_account"), vault.toBuffer()], - program.programId - ); - - // Get fee recipient ATA - const feeRecipientTokenAccount = await getAssociatedTokenAddress( - mint, - payer.publicKey - ); - - console.log("Configuration:"); - console.log("- Program ID:", program.programId.toString()); - console.log("- Pool PDA:", pool.toString()); - console.log("- Token Mint:", mint.toString()); - console.log("- Vault PDA:", vault.toString()); - console.log("- Vault Token Account:", vaultTokenAccount.toString()); - console.log( - "- Fee Recipient Token Account:", - feeRecipientTokenAccount.toString() - ); - console.log("- Operations Authority:", payer.publicKey.toString()); - console.log(); - - // Check if token already exists - try { - const poolAccount = await program.account.liquidityPool.fetch(pool); - const tokenExists = poolAccount.supportedTokens.some( - (tokenMint) => tokenMint.toString() === mint.toString() - ); - - if (tokenExists) { - console.log("❌ Token already added to pool!"); - console.log(); - console.log("Supported tokens:"); - poolAccount.supportedTokens.forEach((tokenMint, index) => { - console.log(` ${index + 1}. ${tokenMint.toString()}`); - }); - process.exit(0); - } - - console.log("✓ Token not yet added, proceeding..."); - console.log(); - } catch (e) { - console.error("❌ Error: Pool not initialized"); - process.exit(1); - } - - console.log("Sending transaction..."); - - try { - const tx = await program.methods - .addSupportedToken() - .accounts({ - pool: pool, - vault: vault, - vaultTokenAccount: vaultTokenAccount, - feeRecipientTokenAccount: feeRecipientTokenAccount, - feeRecipient: payer.publicKey, - mint: mint, - operationsAuthority: payer.publicKey, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - systemProgram: SystemProgram.programId, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - } as any) - .rpc(); - - console.log("✅ Token added successfully!"); - console.log(); - console.log("Transaction Details:"); - console.log("- Signature:", tx); - console.log("- Explorer:", `https://solscan.io/tx/${tx}`); - console.log(); - - // Fetch and display updated pool state - const poolAccount = await program.account.liquidityPool.fetch(pool); - - console.log("Updated Pool State:"); - console.log( - "- Supported Tokens Count:", - poolAccount.supportedTokens.length - ); - console.log("- Supported Tokens:"); - poolAccount.supportedTokens.forEach((tokenMint, index) => { - console.log(` ${index + 1}. ${tokenMint.toString()}`); - }); - console.log(); - - console.log("=".repeat(60)); - console.log("SAVE THESE ADDRESSES:"); - console.log("=".repeat(60)); - console.log("Token Mint:", mint.toString()); - console.log("Vault:", vault.toString()); - console.log("Vault Token Account:", vaultTokenAccount.toString()); - console.log( - "Fee Recipient Token Account:", - feeRecipientTokenAccount.toString() - ); - console.log("=".repeat(60)); - } catch (error: any) { - console.error("❌ Error adding token:"); - console.error(error); - - if (error.logs) { - console.error("\nProgram Logs:"); - error.logs.forEach((log: string) => console.error(log)); - } - - process.exit(1); - } -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); diff --git a/solana/scripts/04-test-swap.ts b/solana/scripts/04-test-swap.ts deleted file mode 100644 index 86270d5..0000000 --- a/solana/scripts/04-test-swap.ts +++ /dev/null @@ -1,331 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { Program } from "@coral-xyz/anchor"; -import { ScaasLiquidity } from "../target/types/scaas_liquidity"; -import { PublicKey, SystemProgram } from "@solana/web3.js"; -import { - TOKEN_PROGRAM_ID, - ASSOCIATED_TOKEN_PROGRAM_ID, - getAssociatedTokenAddress, - getAccount, - createAssociatedTokenAccountInstruction, -} from "@solana/spl-token"; - -async function main() { - // Parse command line arguments - const args = process.argv.slice(2); - - if (args.length < 3 || args[0] === "--help" || args[0] === "-h") { - console.log( - "Usage: yarn ts-node scripts/04-test-swap.ts [RECIPIENT]" - ); - console.log(); - console.log("Arguments:"); - console.log(" FROM_MINT The mint address of the token to swap from"); - console.log(" TO_MINT The mint address of the token to swap to"); - console.log(" AMOUNT Amount to swap (in token units)"); - console.log( - " RECIPIENT (Optional) Recipient wallet address. If not provided, swaps to your own account" - ); - console.log(); - console.log("Examples:"); - console.log(" # Swap 1 USDC -> Custom Token (to your own account)"); - console.log( - " yarn ts-node scripts/04-test-swap.ts EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms 1" - ); - console.log(); - console.log(" # Swap 1 Custom Token -> USDC (to another user's account)"); - console.log( - " yarn ts-node scripts/04-test-swap.ts 9gJ94RYM3kUbFyKzzXAaGaRQ4n39XrDCLPZ1XnpZhnms EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v 1 " - ); - process.exit(args.length < 3 ? 1 : 0); - } - - const fromMintAddress = args[0]; - const toMintAddress = args[1]; - const amountTokens = parseFloat(args[2]); - const recipientAddress = args[3]; // Optional - - // Validate mint addresses - let fromMint: PublicKey; - let toMint: PublicKey; - let recipient: PublicKey | undefined; - - try { - fromMint = new PublicKey(fromMintAddress); - toMint = new PublicKey(toMintAddress); - - if (recipientAddress) { - recipient = new PublicKey(recipientAddress); - } - } catch (error) { - console.error("❌ Error: Invalid mint or recipient address"); - process.exit(1); - } - - if (fromMint.equals(toMint)) { - console.error("❌ Error: Cannot swap a token for itself"); - process.exit(1); - } - - // Validate amount - if (isNaN(amountTokens) || amountTokens <= 0) { - console.error("❌ Error: Amount must be a positive number"); - process.exit(1); - } - - // Set default environment variables if not already set - if (!process.env.ANCHOR_PROVIDER_URL) { - process.env.ANCHOR_PROVIDER_URL = "https://api.mainnet-beta.solana.com"; - } - if (!process.env.ANCHOR_WALLET) { - process.env.ANCHOR_WALLET = - require("os").homedir() + "/.config/solana/id.json"; - } - - // Load provider from environment - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - - const payer = provider.wallet as anchor.Wallet; - const program = anchor.workspace.scaasLiquidity as Program; - - console.log("=".repeat(60)); - console.log("TEST SWAP"); - console.log("=".repeat(60)); - console.log(); - - // Derive PDAs - const [pool] = PublicKey.findProgramAddressSync( - [Buffer.from("liquidity_pool")], - program.programId - ); - - const [inVault] = PublicKey.findProgramAddressSync( - [Buffer.from("token_vault"), pool.toBuffer(), fromMint.toBuffer()], - program.programId - ); - - const [outVault] = PublicKey.findProgramAddressSync( - [Buffer.from("token_vault"), pool.toBuffer(), toMint.toBuffer()], - program.programId - ); - - const [inVaultTokenAccount] = PublicKey.findProgramAddressSync( - [Buffer.from("vault_token_account"), inVault.toBuffer()], - program.programId - ); - - const [outVaultTokenAccount] = PublicKey.findProgramAddressSync( - [Buffer.from("vault_token_account"), outVault.toBuffer()], - program.programId - ); - - // Get user token accounts - const userFromTokenAccount = await getAssociatedTokenAddress( - fromMint, - payer.publicKey - ); - - // Determine recipient (use provided recipient or default to user) - const recipientWallet = recipient || payer.publicKey; - const userToTokenAccount = await getAssociatedTokenAddress( - toMint, - recipientWallet - ); - - // Get fee recipient token account - const poolAccount = await program.account.liquidityPool.fetch(pool); - const feeRecipientFromTokenAccount = await getAssociatedTokenAddress( - fromMint, - poolAccount.feeRecipient - ); - - // Fetch mint info to get decimals - const fromMintInfo = await provider.connection.getParsedAccountInfo(fromMint); - const toMintInfo = await provider.connection.getParsedAccountInfo(toMint); - - if ( - !fromMintInfo.value || - !("parsed" in fromMintInfo.value.data) || - !toMintInfo.value || - !("parsed" in toMintInfo.value.data) - ) { - console.error("❌ Error: Could not fetch mint info"); - process.exit(1); - } - - const fromDecimals = (fromMintInfo.value.data as any).parsed.info.decimals; - const toDecimals = (toMintInfo.value.data as any).parsed.info.decimals; - const amountIn = new anchor.BN(amountTokens * Math.pow(10, fromDecimals)); - - // For 1:1 swap with 0% fee, min_amount_out should be the same (adjusted for decimals) - const minAmountOut = new anchor.BN( - amountTokens * Math.pow(10, toDecimals) * 0.99 - ); // 1% slippage tolerance - - console.log("Configuration:"); - console.log("- Program ID:", program.programId.toString()); - console.log("- Pool PDA:", pool.toString()); - console.log("- User (Signer):", payer.publicKey.toString()); - if (recipient) { - console.log("- Recipient (Destination):", recipientWallet.toString()); - } - console.log(); - - console.log("Swap Details:"); - console.log("- From Token:", fromMint.toString()); - console.log("- To Token:", toMint.toString()); - console.log("- Amount In:", amountTokens, "tokens"); - console.log("- Amount In (base units):", amountIn.toString()); - console.log("- Min Amount Out (base units):", minAmountOut.toString()); - console.log("- From Decimals:", fromDecimals); - console.log("- To Decimals:", toDecimals); - console.log(); - - // Check balances before - try { - const fromAccount = await getAccount( - provider.connection, - userFromTokenAccount - ); - const fromBalance = Number(fromAccount.amount) / Math.pow(10, fromDecimals); - console.log("Your Balance (From Token):", fromBalance, "tokens"); - - if (fromBalance < amountTokens) { - console.error("❌ Error: Insufficient balance"); - console.error( - ` You have ${fromBalance} tokens but trying to swap ${amountTokens}` - ); - process.exit(1); - } - } catch (error) { - console.error("❌ Error: You don't have the from token in your account"); - process.exit(1); - } - - // Check if destination token account exists, create if needed - let needsToAccountCreation = false; - try { - const toAccount = await getAccount(provider.connection, userToTokenAccount); - const toBalance = Number(toAccount.amount) / Math.pow(10, toDecimals); - if (recipient) { - console.log("Recipient Balance (To Token):", toBalance, "tokens"); - } else { - console.log("Your Balance (To Token):", toBalance, "tokens"); - } - } catch (error) { - if (recipient) { - console.log( - "Recipient Balance (To Token): 0 tokens (account doesn't exist)" - ); - } else { - console.log("Your Balance (To Token): 0 tokens (account doesn't exist)"); - } - needsToAccountCreation = true; - } - console.log(); - - console.log("Sending transaction..."); - - try { - // Build the swap instruction - const swapIx = await program.methods - .swap(amountIn, minAmountOut) - .accounts({ - pool: pool, - inVault: inVault, - outVault: outVault, - inVaultTokenAccount: inVaultTokenAccount, - outVaultTokenAccount: outVaultTokenAccount, - userFromTokenAccount: userFromTokenAccount, - toTokenAccount: userToTokenAccount, - feeRecipientTokenAccount: feeRecipientFromTokenAccount, - feeRecipient: poolAccount.feeRecipient, - fromMint: fromMint, - toMint: toMint, - user: payer.publicKey, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - systemProgram: SystemProgram.programId, - } as any) - .instruction(); - - // Build transaction with optional ATA creation - const transaction = new anchor.web3.Transaction(); - - if (needsToAccountCreation) { - if (recipient) { - console.log("Creating destination token account for recipient..."); - } else { - console.log("Creating destination token account..."); - } - const createAtaIx = createAssociatedTokenAccountInstruction( - payer.publicKey, // Payer (signer pays for account creation) - userToTokenAccount, - recipientWallet, // Authority (owner of the new account) - toMint - ); - transaction.add(createAtaIx); - } - - transaction.add(swapIx); - - // Send transaction - const tx = await provider.sendAndConfirm(transaction); - - console.log("✅ Swap successful!"); - console.log(); - console.log("Transaction Details:"); - console.log("- Signature:", tx); - console.log("- Explorer:", `https://solscan.io/tx/${tx}`); - console.log(); - - // Check balances after - const fromAccountAfter = await getAccount( - provider.connection, - userFromTokenAccount - ); - const fromBalanceAfter = - Number(fromAccountAfter.amount) / Math.pow(10, fromDecimals); - - const toAccountAfter = await getAccount( - provider.connection, - userToTokenAccount - ); - const toBalanceAfter = - Number(toAccountAfter.amount) / Math.pow(10, toDecimals); - - console.log("Your Balance After (From Token):", fromBalanceAfter, "tokens"); - if (recipient) { - console.log( - "Recipient Balance After (To Token):", - toBalanceAfter, - "tokens" - ); - } else { - console.log("Your Balance After (To Token):", toBalanceAfter, "tokens"); - } - console.log(); - - console.log("=".repeat(60)); - console.log("✅ Swap complete!"); - console.log("=".repeat(60)); - } catch (error: any) { - console.error("❌ Error performing swap:"); - console.error(error); - - if (error.logs) { - console.error("\nProgram Logs:"); - error.logs.forEach((log: string) => console.error(log)); - } - - process.exit(1); - } -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); diff --git a/solana/scripts/emergency-pause-liquidity.ts b/solana/scripts/emergency-pause-liquidity.ts deleted file mode 100644 index a80060e..0000000 --- a/solana/scripts/emergency-pause-liquidity.ts +++ /dev/null @@ -1,152 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { Program } from "@coral-xyz/anchor"; -import { ScaasLiquidity } from "../target/types/scaas_liquidity"; -import { PublicKey } from "@solana/web3.js"; - -async function main() { - // Parse command line arguments - const args = process.argv.slice(2); - - if (args[0] === "--help" || args[0] === "-h") { - console.log( - "Usage: yarn ts-node scripts/emergency-pause-liquidity.ts [PAUSED]" - ); - console.log(); - console.log("Arguments:"); - console.log( - " PAUSED true to pause liquidity operations, false to unpause (optional, defaults to true)" - ); - console.log(); - console.log("Examples:"); - console.log(" # Pause liquidity operations (emergency)"); - console.log(" yarn ts-node scripts/emergency-pause-liquidity.ts"); - console.log(" yarn ts-node scripts/emergency-pause-liquidity.ts true"); - console.log(); - console.log(" # Unpause liquidity operations (restore normal operations)"); - console.log(" yarn ts-node scripts/emergency-pause-liquidity.ts false"); - process.exit(0); - } - - // Default to pausing if no argument provided (emergency mode) - const pausedArg = args[0]?.toLowerCase(); - const paused = - pausedArg === undefined || - pausedArg === "true" || - pausedArg === "1" || - pausedArg === "yes"; - - // Set default environment variables if not already set - if (!process.env.ANCHOR_PROVIDER_URL) { - process.env.ANCHOR_PROVIDER_URL = "https://api.mainnet-beta.solana.com"; - } - if (!process.env.ANCHOR_WALLET) { - process.env.ANCHOR_WALLET = - require("os").homedir() + "/.config/solana/id.json"; - } - - // Load provider from environment - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - - const payer = provider.wallet as anchor.Wallet; - const program = anchor.workspace.scaasLiquidity as Program; - - console.log("=".repeat(60)); - console.log("EMERGENCY PAUSE LIQUIDITY"); - console.log("=".repeat(60)); - console.log(); - - // Derive PDAs - const [pool] = PublicKey.findProgramAddressSync( - [Buffer.from("liquidity_pool")], - program.programId - ); - - // Fetch pool to verify pause authority - const poolAccount = await program.account.liquidityPool.fetch(pool); - - console.log("Configuration:"); - console.log("- Program ID:", program.programId.toString()); - console.log("- Pool PDA:", pool.toString()); - console.log("- Pause Authority:", poolAccount.pauseAuthority.toString()); - console.log("- Your Wallet:", payer.publicKey.toString()); - console.log(); - - // Verify you are the pause authority - if (!poolAccount.pauseAuthority.equals(payer.publicKey)) { - console.error("❌ Error: You are not the pause authority"); - console.error( - ` Pause authority is: ${poolAccount.pauseAuthority.toString()}` - ); - console.error(` Your wallet is: ${payer.publicKey.toString()}`); - process.exit(1); - } - - console.log("Current State:"); - console.log("- Swaps Paused:", poolAccount.swapsPaused); - console.log("- Liquidity Paused:", poolAccount.liquidityPaused); - console.log(); - - if (poolAccount.liquidityPaused === paused) { - console.log( - `ℹ️ Liquidity operations are already ${paused ? "paused" : "unpaused"}` - ); - process.exit(0); - } - - console.log( - `${ - paused - ? "⚠️ PAUSING LIQUIDITY OPERATIONS" - : "✅ UNPAUSING LIQUIDITY OPERATIONS" - }` - ); - console.log("Sending transaction..."); - console.log(); - - try { - const tx = await program.methods - .updatePauseConfig(null, paused) - .accounts({ - pool: pool, - pauseAuthority: payer.publicKey, - } as any) - .rpc(); - - console.log( - `${paused ? "🛑" : "✅"} Liquidity operations ${ - paused ? "paused" : "unpaused" - } successfully!` - ); - console.log(); - console.log("Transaction Details:"); - console.log("- Signature:", tx); - console.log("- Explorer:", `https://solscan.io/tx/${tx}`); - console.log(); - - console.log("New State:"); - console.log("- Liquidity Paused:", paused); - console.log(); - - console.log("=".repeat(60)); - console.log(`${paused ? "🛑 LIQUIDITY PAUSED" : "✅ LIQUIDITY RESTORED"}`); - console.log("=".repeat(60)); - } catch (error: any) { - console.error("❌ Error updating pause config:"); - console.error(error); - - if (error.logs) { - console.error("\nProgram Logs:"); - error.logs.forEach((log: string) => console.error(log)); - } - - process.exit(1); - } -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); diff --git a/solana/scripts/emergency-pause-swaps.ts b/solana/scripts/emergency-pause-swaps.ts deleted file mode 100644 index ed93f8d..0000000 --- a/solana/scripts/emergency-pause-swaps.ts +++ /dev/null @@ -1,144 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { Program } from "@coral-xyz/anchor"; -import { ScaasLiquidity } from "../target/types/scaas_liquidity"; -import { PublicKey } from "@solana/web3.js"; - -async function main() { - // Parse command line arguments - const args = process.argv.slice(2); - - if (args[0] === "--help" || args[0] === "-h") { - console.log( - "Usage: yarn ts-node scripts/emergency-pause-swaps.ts [PAUSED]" - ); - console.log(); - console.log("Arguments:"); - console.log( - " PAUSED true to pause swaps, false to unpause (optional, defaults to true)" - ); - console.log(); - console.log("Examples:"); - console.log(" # Pause swaps (emergency)"); - console.log(" yarn ts-node scripts/emergency-pause-swaps.ts"); - console.log(" yarn ts-node scripts/emergency-pause-swaps.ts true"); - console.log(); - console.log(" # Unpause swaps (restore normal operations)"); - console.log(" yarn ts-node scripts/emergency-pause-swaps.ts false"); - process.exit(0); - } - - // Default to pausing if no argument provided (emergency mode) - const pausedArg = args[0]?.toLowerCase(); - const paused = - pausedArg === undefined || - pausedArg === "true" || - pausedArg === "1" || - pausedArg === "yes"; - - // Set default environment variables if not already set - if (!process.env.ANCHOR_PROVIDER_URL) { - process.env.ANCHOR_PROVIDER_URL = "https://api.mainnet-beta.solana.com"; - } - if (!process.env.ANCHOR_WALLET) { - process.env.ANCHOR_WALLET = - require("os").homedir() + "/.config/solana/id.json"; - } - - // Load provider from environment - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - - const payer = provider.wallet as anchor.Wallet; - const program = anchor.workspace.scaasLiquidity as Program; - - console.log("=".repeat(60)); - console.log("EMERGENCY PAUSE SWAPS"); - console.log("=".repeat(60)); - console.log(); - - // Derive PDAs - const [pool] = PublicKey.findProgramAddressSync( - [Buffer.from("liquidity_pool")], - program.programId - ); - - // Fetch pool to verify pause authority - const poolAccount = await program.account.liquidityPool.fetch(pool); - - console.log("Configuration:"); - console.log("- Program ID:", program.programId.toString()); - console.log("- Pool PDA:", pool.toString()); - console.log("- Pause Authority:", poolAccount.pauseAuthority.toString()); - console.log("- Your Wallet:", payer.publicKey.toString()); - console.log(); - - // Verify you are the pause authority - if (!poolAccount.pauseAuthority.equals(payer.publicKey)) { - console.error("❌ Error: You are not the pause authority"); - console.error( - ` Pause authority is: ${poolAccount.pauseAuthority.toString()}` - ); - console.error(` Your wallet is: ${payer.publicKey.toString()}`); - process.exit(1); - } - - console.log("Current State:"); - console.log("- Swaps Paused:", poolAccount.swapsPaused); - console.log("- Liquidity Paused:", poolAccount.liquidityPaused); - console.log(); - - if (poolAccount.swapsPaused === paused) { - console.log(`ℹ️ Swaps are already ${paused ? "paused" : "unpaused"}`); - process.exit(0); - } - - console.log(`${paused ? "⚠️ PAUSING SWAPS" : "✅ UNPAUSING SWAPS"}`); - console.log("Sending transaction..."); - console.log(); - - try { - const tx = await program.methods - .updatePauseConfig(paused, null) - .accounts({ - pool: pool, - pauseAuthority: payer.publicKey, - } as any) - .rpc(); - - console.log( - `${paused ? "🛑" : "✅"} Swaps ${ - paused ? "paused" : "unpaused" - } successfully!` - ); - console.log(); - console.log("Transaction Details:"); - console.log("- Signature:", tx); - console.log("- Explorer:", `https://solscan.io/tx/${tx}`); - console.log(); - - console.log("New State:"); - console.log("- Swaps Paused:", paused); - console.log(); - - console.log("=".repeat(60)); - console.log(`${paused ? "🛑 SWAPS PAUSED" : "✅ SWAPS RESTORED"}`); - console.log("=".repeat(60)); - } catch (error: any) { - console.error("❌ Error updating pause config:"); - console.error(error); - - if (error.logs) { - console.error("\nProgram Logs:"); - error.logs.forEach((log: string) => console.error(log)); - } - - process.exit(1); - } -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); diff --git a/solana/scripts/emergency-withdraw.ts b/solana/scripts/emergency-withdraw.ts deleted file mode 100644 index 7cca74f..0000000 --- a/solana/scripts/emergency-withdraw.ts +++ /dev/null @@ -1,255 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { Program } from "@coral-xyz/anchor"; -import { ScaasLiquidity } from "../target/types/scaas_liquidity"; -import { PublicKey } from "@solana/web3.js"; -import { getAccount, getAssociatedTokenAddress } from "@solana/spl-token"; - -async function main() { - // Parse command line arguments - const args = process.argv.slice(2); - - if (args.length < 1 || args[0] === "--help" || args[0] === "-h") { - console.log( - "Usage: yarn ts-node scripts/emergency-withdraw.ts [AMOUNT]" - ); - console.log(); - console.log("Arguments:"); - console.log(" TOKEN_MINT Token mint address to withdraw"); - console.log( - " AMOUNT Amount to withdraw in tokens (optional, defaults to 'max' for all available)" - ); - console.log(); - console.log("Examples:"); - console.log(" # Withdraw all available liquidity"); - console.log( - " yarn ts-node scripts/emergency-withdraw.ts EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" - ); - console.log( - " yarn ts-node scripts/emergency-withdraw.ts EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v max" - ); - console.log(); - console.log(" # Withdraw specific amount"); - console.log( - " yarn ts-node scripts/emergency-withdraw.ts EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v 100" - ); - process.exit(args.length < 1 ? 1 : 0); - } - - const mintAddress = args[0]; - const amountArg = args[1] || "max"; - - // Validate mint address - let mint: PublicKey; - try { - mint = new PublicKey(mintAddress); - } catch (error) { - console.error(`❌ Error: Invalid mint address: ${mintAddress}`); - process.exit(1); - } - - // Set default environment variables if not already set - if (!process.env.ANCHOR_PROVIDER_URL) { - process.env.ANCHOR_PROVIDER_URL = "https://api.mainnet-beta.solana.com"; - } - if (!process.env.ANCHOR_WALLET) { - process.env.ANCHOR_WALLET = - require("os").homedir() + "/.config/solana/id.json"; - } - - // Load provider from environment - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - - const payer = provider.wallet as anchor.Wallet; - const program = anchor.workspace.scaasLiquidity as Program; - - console.log("=".repeat(60)); - console.log("EMERGENCY WITHDRAW LIQUIDITY"); - console.log("=".repeat(60)); - console.log(); - - // Derive PDAs - const [pool] = PublicKey.findProgramAddressSync( - [Buffer.from("liquidity_pool")], - program.programId - ); - - const [vault] = PublicKey.findProgramAddressSync( - [Buffer.from("token_vault"), pool.toBuffer(), mint.toBuffer()], - program.programId - ); - - const [vaultTokenAccount] = PublicKey.findProgramAddressSync( - [Buffer.from("vault_token_account"), vault.toBuffer()], - program.programId - ); - - const operationsAuthorityTokenAccount = await getAssociatedTokenAddress( - mint, - payer.publicKey, - false - ); - - // Fetch accounts - const poolAccount = await program.account.liquidityPool.fetch(pool); - const vaultAccount = await program.account.tokenVault.fetch(vault); - const mintAccount = await provider.connection.getAccountInfo(mint); - - if (!mintAccount) { - console.error("❌ Error: Mint account not found"); - process.exit(1); - } - - const mintData = await provider.connection.getParsedAccountInfo(mint); - const decimals = (mintData.value?.data as any).parsed.info.decimals; - - console.log("Configuration:"); - console.log("- Program ID:", program.programId.toString()); - console.log("- Pool PDA:", pool.toString()); - console.log("- Token Mint:", mint.toString()); - console.log("- Token Decimals:", decimals); - console.log("- Vault PDA:", vault.toString()); - console.log("- Vault Token Account:", vaultTokenAccount.toString()); - console.log( - "- Operations Authority:", - poolAccount.operationsAuthority.toString() - ); - console.log( - "- Operations Authority Token Account:", - operationsAuthorityTokenAccount.toString() - ); - console.log("- Your Wallet:", payer.publicKey.toString()); - console.log(); - - // Verify you are the operations authority - if (!poolAccount.operationsAuthority.equals(payer.publicKey)) { - console.error("❌ Error: You are not the operations authority"); - console.error( - ` Operations authority is: ${poolAccount.operationsAuthority.toString()}` - ); - console.error(` Your wallet is: ${payer.publicKey.toString()}`); - process.exit(1); - } - - // Fetch vault balance - const vaultTokenAccountInfo = await getAccount( - provider.connection, - vaultTokenAccount - ); - const vaultBalance = - Number(vaultTokenAccountInfo.amount) / Math.pow(10, decimals); - - console.log("Current State:"); - console.log( - "- Vault Total Balance:", - vaultBalance.toLocaleString(), - "tokens" - ); - console.log("- Token Disabled:", vaultAccount.disabled); - console.log(); - - if (vaultBalance === 0) { - console.log("ℹ️ Vault is already empty, nothing to withdraw"); - process.exit(0); - } - - // Determine amount to withdraw - let withdrawAmount: number; - let withdrawAmountBaseUnits: anchor.BN; - - if (amountArg.toLowerCase() === "max") { - withdrawAmount = vaultBalance; - withdrawAmountBaseUnits = new anchor.BN( - vaultTokenAccountInfo.amount.toString() - ); - console.log("⚠️ WITHDRAWING ALL LIQUIDITY (MAX)"); - } else { - withdrawAmount = parseFloat(amountArg); - if (isNaN(withdrawAmount) || withdrawAmount <= 0) { - console.error(`❌ Error: Invalid amount: ${amountArg}`); - process.exit(1); - } - if (withdrawAmount > vaultBalance) { - console.error( - `❌ Error: Amount ${withdrawAmount} exceeds vault balance ${vaultBalance}` - ); - process.exit(1); - } - withdrawAmountBaseUnits = new anchor.BN( - Math.floor(withdrawAmount * Math.pow(10, decimals)) - ); - console.log(`⚠️ WITHDRAWING ${withdrawAmount.toLocaleString()} TOKENS`); - } - - console.log(); - console.log("Withdrawal Details:"); - console.log("- Amount (tokens):", withdrawAmount.toLocaleString()); - console.log("- Amount (base units):", withdrawAmountBaseUnits.toString()); - console.log(); - console.log("Sending transaction..."); - console.log(); - - try { - const tx = await program.methods - .withdrawLiquidity(withdrawAmountBaseUnits) - .accounts({ - pool: pool, - vault: vault, - vaultTokenAccount: vaultTokenAccount, - recipientTokenAccount: operationsAuthorityTokenAccount, - mint: mint, - operationsAuthority: payer.publicKey, - tokenProgram: anchor.utils.token.TOKEN_PROGRAM_ID, - } as any) - .rpc(); - - console.log("✅ Liquidity withdrawn successfully!"); - console.log(); - console.log("Transaction Details:"); - console.log("- Signature:", tx); - console.log("- Explorer:", `https://solscan.io/tx/${tx}`); - console.log(); - - // Fetch updated balance - const updatedVaultTokenAccountInfo = await getAccount( - provider.connection, - vaultTokenAccount - ); - const updatedVaultBalance = - Number(updatedVaultTokenAccountInfo.amount) / Math.pow(10, decimals); - - console.log("Updated State:"); - console.log( - "- Vault Balance After:", - updatedVaultBalance.toLocaleString(), - "tokens" - ); - console.log( - "- Amount Withdrawn:", - withdrawAmount.toLocaleString(), - "tokens" - ); - console.log(); - - console.log("=".repeat(60)); - console.log("✅ EMERGENCY WITHDRAWAL COMPLETE"); - console.log("=".repeat(60)); - } catch (error: any) { - console.error("❌ Error withdrawing liquidity:"); - console.error(error); - - if (error.logs) { - console.error("\nProgram Logs:"); - error.logs.forEach((log: string) => console.error(log)); - } - - process.exit(1); - } -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); diff --git a/solana/scripts/update-operations-authority.ts b/solana/scripts/update-operations-authority.ts deleted file mode 100644 index f039e0b..0000000 --- a/solana/scripts/update-operations-authority.ts +++ /dev/null @@ -1,159 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { Program } from "@coral-xyz/anchor"; -import { ScaasLiquidity } from "../target/types/scaas_liquidity"; -import { PublicKey } from "@solana/web3.js"; - -async function main() { - // Parse command line arguments - const args = process.argv.slice(2); - - if (args.length < 1 || args[0] === "--help" || args[0] === "-h") { - console.log( - "Usage: yarn ts-node scripts/update-operations-authority.ts " - ); - console.log(); - console.log("Arguments:"); - console.log( - " NEW_OPERATIONS_AUTHORITY New operations authority address" - ); - console.log(); - console.log("Examples:"); - console.log(" # Update operations authority to a new address"); - console.log( - " yarn ts-node scripts/update-operations-authority.ts 9TcjK2ToqoAtCr5jrLdDXNTNbZBbDQB1zy2BNGMr7nQE" - ); - console.log(); - console.log( - "Note: You must be the current operations authority to execute this." - ); - process.exit(args.length < 1 ? 1 : 0); - } - - const newOperationsAuthorityAddress = args[0]; - - // Validate new operations authority address - let newOperationsAuthority: PublicKey; - try { - newOperationsAuthority = new PublicKey(newOperationsAuthorityAddress); - } catch (error) { - console.error( - `❌ Error: Invalid address: ${newOperationsAuthorityAddress}` - ); - process.exit(1); - } - - // Set default environment variables if not already set - if (!process.env.ANCHOR_PROVIDER_URL) { - process.env.ANCHOR_PROVIDER_URL = "https://api.mainnet-beta.solana.com"; - } - if (!process.env.ANCHOR_WALLET) { - process.env.ANCHOR_WALLET = - require("os").homedir() + "/.config/solana/id.json"; - } - - // Load provider from environment - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - - const payer = provider.wallet as anchor.Wallet; - const program = anchor.workspace.scaasLiquidity as Program; - - console.log("=".repeat(60)); - console.log("UPDATE OPERATIONS AUTHORITY"); - console.log("=".repeat(60)); - console.log(); - - // Derive PDAs - const [pool] = PublicKey.findProgramAddressSync( - [Buffer.from("liquidity_pool")], - program.programId - ); - - // Fetch pool to verify current operations authority - const poolAccount = await program.account.liquidityPool.fetch(pool); - - console.log("Configuration:"); - console.log("- Program ID:", program.programId.toString()); - console.log("- Pool PDA:", pool.toString()); - console.log( - "- Current Operations Authority:", - poolAccount.operationsAuthority.toString() - ); - console.log("- Your Wallet:", payer.publicKey.toString()); - console.log("- New Operations Authority:", newOperationsAuthority.toString()); - console.log(); - - // Verify you are the current operations authority - if (!poolAccount.operationsAuthority.equals(payer.publicKey)) { - console.error("❌ Error: You are not the current operations authority"); - console.error( - ` Current operations authority is: ${poolAccount.operationsAuthority.toString()}` - ); - console.error(` Your wallet is: ${payer.publicKey.toString()}`); - process.exit(1); - } - - // Check if already set to new authority - if (poolAccount.operationsAuthority.equals(newOperationsAuthority)) { - console.log("ℹ️ Operations authority is already set to this address"); - process.exit(0); - } - - console.log("⚠️ WARNING: This will transfer operations authority control!"); - console.log(" The new operations authority will be able to:"); - console.log(" - Add and remove tokens"); - console.log(" - Withdraw liquidity"); - console.log(" - Transfer operations authority to another address"); - console.log(); - console.log("Sending transaction..."); - console.log(); - - try { - const tx = await program.methods - .updateOperationsAuthority(newOperationsAuthority) - .accounts({ - pool: pool, - operationsAuthority: payer.publicKey, - } as any) - .rpc(); - - console.log("✅ Operations authority updated successfully!"); - console.log(); - console.log("Transaction Details:"); - console.log("- Signature:", tx); - console.log("- Explorer:", `https://solscan.io/tx/${tx}`); - console.log(); - - console.log("Authority Transfer:"); - console.log( - "- Old Operations Authority:", - poolAccount.operationsAuthority.toString() - ); - console.log( - "- New Operations Authority:", - newOperationsAuthority.toString() - ); - console.log(); - - console.log("=".repeat(60)); - console.log("✅ Authority transfer complete!"); - console.log("=".repeat(60)); - } catch (error: any) { - console.error("❌ Error updating operations authority:"); - console.error(error); - - if (error.logs) { - console.error("\nProgram Logs:"); - error.logs.forEach((log: string) => console.error(log)); - } - - process.exit(1); - } -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); diff --git a/solana/scripts/update-pause-authority.ts b/solana/scripts/update-pause-authority.ts deleted file mode 100644 index d57805d..0000000 --- a/solana/scripts/update-pause-authority.ts +++ /dev/null @@ -1,152 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { Program } from "@coral-xyz/anchor"; -import { ScaasLiquidity } from "../target/types/scaas_liquidity"; -import { PublicKey } from "@solana/web3.js"; - -async function main() { - // Parse command line arguments - const args = process.argv.slice(2); - - if (args.length < 1 || args[0] === "--help" || args[0] === "-h") { - console.log( - "Usage: yarn ts-node scripts/update-pause-authority.ts " - ); - console.log(); - console.log("Arguments:"); - console.log(" NEW_PAUSE_AUTHORITY New pause authority address"); - console.log(); - console.log("Examples:"); - console.log(" # Update pause authority to a new address"); - console.log( - " yarn ts-node scripts/update-pause-authority.ts 9TcjK2ToqoAtCr5jrLdDXNTNbZBbDQB1zy2BNGMr7nQE" - ); - console.log(); - console.log( - "Note: You must be the current pause authority to execute this." - ); - process.exit(args.length < 1 ? 1 : 0); - } - - const newPauseAuthorityAddress = args[0]; - - // Validate new pause authority address - let newPauseAuthority: PublicKey; - try { - newPauseAuthority = new PublicKey(newPauseAuthorityAddress); - } catch (error) { - console.error(`❌ Error: Invalid address: ${newPauseAuthorityAddress}`); - process.exit(1); - } - - // Set default environment variables if not already set - if (!process.env.ANCHOR_PROVIDER_URL) { - process.env.ANCHOR_PROVIDER_URL = "https://api.mainnet-beta.solana.com"; - } - if (!process.env.ANCHOR_WALLET) { - process.env.ANCHOR_WALLET = - require("os").homedir() + "/.config/solana/id.json"; - } - - // Load provider from environment - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - - const payer = provider.wallet as anchor.Wallet; - const program = anchor.workspace.scaasLiquidity as Program; - - console.log("=".repeat(60)); - console.log("UPDATE PAUSE AUTHORITY"); - console.log("=".repeat(60)); - console.log(); - - // Derive PDAs - const [pool] = PublicKey.findProgramAddressSync( - [Buffer.from("liquidity_pool")], - program.programId - ); - - // Fetch pool to verify current pause authority - const poolAccount = await program.account.liquidityPool.fetch(pool); - - console.log("Configuration:"); - console.log("- Program ID:", program.programId.toString()); - console.log("- Pool PDA:", pool.toString()); - console.log( - "- Current Pause Authority:", - poolAccount.pauseAuthority.toString() - ); - console.log("- Your Wallet:", payer.publicKey.toString()); - console.log("- New Pause Authority:", newPauseAuthority.toString()); - console.log(); - - // Verify you are the current pause authority - if (!poolAccount.pauseAuthority.equals(payer.publicKey)) { - console.error("❌ Error: You are not the current pause authority"); - console.error( - ` Current pause authority is: ${poolAccount.pauseAuthority.toString()}` - ); - console.error(` Your wallet is: ${payer.publicKey.toString()}`); - process.exit(1); - } - - // Check if already set to new authority - if (poolAccount.pauseAuthority.equals(newPauseAuthority)) { - console.log("ℹ️ Pause authority is already set to this address"); - process.exit(0); - } - - console.log("⚠️ WARNING: This will transfer pause authority control!"); - console.log(" The new pause authority will be able to:"); - console.log(" - Pause/unpause swaps and liquidity operations"); - console.log(" - Enable/disable tokens"); - console.log(" - Transfer pause authority to another address"); - console.log(); - console.log("Sending transaction..."); - console.log(); - - try { - const tx = await program.methods - .updatePauseAuthority(newPauseAuthority) - .accounts({ - pool: pool, - pauseAuthority: payer.publicKey, - } as any) - .rpc(); - - console.log("✅ Pause authority updated successfully!"); - console.log(); - console.log("Transaction Details:"); - console.log("- Signature:", tx); - console.log("- Explorer:", `https://solscan.io/tx/${tx}`); - console.log(); - - console.log("Authority Transfer:"); - console.log( - "- Old Pause Authority:", - poolAccount.pauseAuthority.toString() - ); - console.log("- New Pause Authority:", newPauseAuthority.toString()); - console.log(); - - console.log("=".repeat(60)); - console.log("✅ Authority transfer complete!"); - console.log("=".repeat(60)); - } catch (error: any) { - console.error("❌ Error updating pause authority:"); - console.error(error); - - if (error.logs) { - console.error("\nProgram Logs:"); - error.logs.forEach((log: string) => console.error(log)); - } - - process.exit(1); - } -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); diff --git a/solana/scripts/update-token-status.ts b/solana/scripts/update-token-status.ts deleted file mode 100644 index 2907719..0000000 --- a/solana/scripts/update-token-status.ts +++ /dev/null @@ -1,167 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { Program } from "@coral-xyz/anchor"; -import { ScaasLiquidity } from "../target/types/scaas_liquidity"; -import { PublicKey } from "@solana/web3.js"; - -async function main() { - // Parse command line arguments - const args = process.argv.slice(2); - - if (args.length < 2 || args[0] === "--help" || args[0] === "-h") { - console.log( - "Usage: yarn ts-node scripts/update-token-status.ts " - ); - console.log(); - console.log("Arguments:"); - console.log(" TOKEN_MINT Token mint address to update"); - console.log(" DISABLED true to disable token, false to enable"); - console.log(); - console.log("Examples:"); - console.log(" # Disable a token (prevent swaps)"); - console.log( - " yarn ts-node scripts/update-token-status.ts EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v true" - ); - console.log(); - console.log(" # Enable a token (allow swaps)"); - console.log( - " yarn ts-node scripts/update-token-status.ts EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v false" - ); - process.exit(args.length < 2 ? 1 : 0); - } - - const mintAddress = args[0]; - const disabledArg = args[1].toLowerCase(); - const disabled = - disabledArg === "true" || disabledArg === "1" || disabledArg === "yes"; - - // Validate mint address - let mint: PublicKey; - try { - mint = new PublicKey(mintAddress); - } catch (error) { - console.error(`❌ Error: Invalid mint address: ${mintAddress}`); - process.exit(1); - } - - // Set default environment variables if not already set - if (!process.env.ANCHOR_PROVIDER_URL) { - process.env.ANCHOR_PROVIDER_URL = "https://api.mainnet-beta.solana.com"; - } - if (!process.env.ANCHOR_WALLET) { - process.env.ANCHOR_WALLET = - require("os").homedir() + "/.config/solana/id.json"; - } - - // Load provider from environment - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - - const payer = provider.wallet as anchor.Wallet; - const program = anchor.workspace.scaasLiquidity as Program; - - console.log("=".repeat(60)); - console.log("UPDATE TOKEN STATUS"); - console.log("=".repeat(60)); - console.log(); - - // Derive PDAs - const [pool] = PublicKey.findProgramAddressSync( - [Buffer.from("liquidity_pool")], - program.programId - ); - - const [vault] = PublicKey.findProgramAddressSync( - [Buffer.from("token_vault"), pool.toBuffer(), mint.toBuffer()], - program.programId - ); - - // Fetch pool to verify pause authority - const poolAccount = await program.account.liquidityPool.fetch(pool); - - console.log("Configuration:"); - console.log("- Program ID:", program.programId.toString()); - console.log("- Pool PDA:", pool.toString()); - console.log("- Token Mint:", mint.toString()); - console.log("- Vault PDA:", vault.toString()); - console.log("- Pause Authority:", poolAccount.pauseAuthority.toString()); - console.log("- Your Wallet:", payer.publicKey.toString()); - console.log(); - - // Verify you are the pause authority - if (!poolAccount.pauseAuthority.equals(payer.publicKey)) { - console.error("❌ Error: You are not the pause authority"); - console.error( - ` Pause authority is: ${poolAccount.pauseAuthority.toString()}` - ); - console.error(` Your wallet is: ${payer.publicKey.toString()}`); - process.exit(1); - } - - // Fetch current vault state - const vaultAccount = await program.account.tokenVault.fetch(vault); - - console.log("Current State:"); - console.log("- Token Disabled:", vaultAccount.disabled); - console.log(); - - if (vaultAccount.disabled === disabled) { - console.log(`ℹ️ Token is already ${disabled ? "disabled" : "enabled"}`); - process.exit(0); - } - - console.log(`Setting token to: ${disabled ? "DISABLED" : "ENABLED"}`); - console.log("Sending transaction..."); - console.log(); - - try { - const tx = await program.methods - .updateTokenStatus(disabled) - .accounts({ - pool: pool, - vault: vault, - mint: mint, - pauseAuthority: payer.publicKey, - } as any) - .rpc(); - - console.log(`${disabled ? "🛑" : "✅"} Token status updated successfully!`); - console.log(); - console.log("Transaction Details:"); - console.log("- Signature:", tx); - console.log("- Explorer:", `https://solscan.io/tx/${tx}`); - console.log(); - - console.log("New State:"); - console.log("- Token Disabled:", disabled); - console.log(); - - console.log("=".repeat(60)); - console.log(`${disabled ? "🛑 TOKEN DISABLED" : "✅ TOKEN ENABLED"}`); - console.log("=".repeat(60)); - - if (disabled) { - console.log(); - console.log("Note: Disabled tokens cannot be used in swaps."); - console.log( - "This is useful for emergency situations or token migrations." - ); - } - } catch (error: any) { - console.error("❌ Error updating token status:"); - console.error(error); - - if (error.logs) { - console.error("\nProgram Logs:"); - error.logs.forEach((log: string) => console.error(log)); - } - - process.exit(1); - } -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); diff --git a/solana/scripts/verify-pool.ts b/solana/scripts/verify-pool.ts deleted file mode 100644 index f6be017..0000000 --- a/solana/scripts/verify-pool.ts +++ /dev/null @@ -1,135 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { Program } from "@coral-xyz/anchor"; -import { ScaasLiquidity } from "../target/types/scaas_liquidity"; -import { PublicKey } from "@solana/web3.js"; -import { getAccount } from "@solana/spl-token"; - -async function main() { - // Set default environment variables if not already set - if (!process.env.ANCHOR_PROVIDER_URL) { - process.env.ANCHOR_PROVIDER_URL = "https://api.mainnet-beta.solana.com"; - } - if (!process.env.ANCHOR_WALLET) { - process.env.ANCHOR_WALLET = - require("os").homedir() + "/.config/solana/id.json"; - } - - // Load provider from environment - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - - const program = anchor.workspace.scaasLiquidity as Program; - - // Derive PDAs - const [pool] = PublicKey.findProgramAddressSync( - [Buffer.from("liquidity_pool")], - program.programId - ); - - console.log("=".repeat(60)); - console.log("POOL STATE VERIFICATION"); - console.log("=".repeat(60)); - console.log(); - - console.log("Addresses:"); - console.log("- Program ID:", program.programId.toString()); - console.log("- Pool PDA:", pool.toString()); - console.log(); - - try { - // Fetch pool state - const poolAccount = await program.account.liquidityPool.fetch(pool); - - console.log("Pool Configuration:"); - console.log( - "- Operations Authority:", - poolAccount.operationsAuthority.toString() - ); - console.log("- Pause Authority:", poolAccount.pauseAuthority.toString()); - console.log("- Fee Recipient:", poolAccount.feeRecipient.toString()); - console.log( - "- Fee Rate:", - poolAccount.feeRate.toNumber(), - "bps (" + poolAccount.feeRate.toNumber() / 100 + "%)" - ); - console.log("- Swaps Paused:", poolAccount.swapsPaused); - console.log("- Liquidity Paused:", poolAccount.liquidityPaused); - console.log(); - - console.log("Supported Tokens:"); - console.log("- Count:", poolAccount.supportedTokens.length); - console.log(); - - if (poolAccount.supportedTokens.length > 0) { - for (let index = 0; index < poolAccount.supportedTokens.length; index++) { - const mint = poolAccount.supportedTokens[index]; - console.log(`Token ${index + 1}:`); - console.log(`- Mint: ${mint.toString()}`); - - // Derive vault PDA - const [vault] = PublicKey.findProgramAddressSync( - [Buffer.from("token_vault"), pool.toBuffer(), mint.toBuffer()], - program.programId - ); - - // Derive vault token account PDA - const [vaultTokenAccount] = PublicKey.findProgramAddressSync( - [Buffer.from("vault_token_account"), vault.toBuffer()], - program.programId - ); - - try { - // Fetch vault state - const vaultAccount = await program.account.tokenVault.fetch(vault); - - // Fetch mint info for decimals - const mintInfo = await provider.connection.getParsedAccountInfo(mint); - let decimals = 0; - if (mintInfo.value && "parsed" in mintInfo.value.data) { - decimals = (mintInfo.value.data as any).parsed.info.decimals; - } - - // Fetch vault token account balance - const vaultTokenAccountInfo = await getAccount( - provider.connection, - vaultTokenAccount - ); - const balance = - Number(vaultTokenAccountInfo.amount) / Math.pow(10, decimals); - - console.log(`- Vault: ${vault.toString()}`); - console.log(`- Vault Token Account: ${vaultTokenAccount.toString()}`); - console.log(`- Decimals: ${decimals}`); - console.log(`- Total Balance: ${balance.toLocaleString()} tokens`); - console.log(`- Disabled: ${vaultAccount.disabled}`); - } catch (error: any) { - console.log(`- Error fetching vault info: ${error.message}`); - } - console.log(); - } - } else { - console.log(); - } - - console.log("=".repeat(60)); - console.log("✅ Pool verification complete!"); - console.log("=".repeat(60)); - } catch (error: any) { - console.error("❌ Error fetching pool state:"); - console.error(error.message); - - if (error.message.includes("Account does not exist")) { - console.error("\nThe pool has not been initialized yet."); - console.error("Run: yarn ts-node scripts/01-initialize-pool.ts 0"); - } - - process.exit(1); - } -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); diff --git a/solana/tests/scaas-liquidity.ts b/solana/tests/stable-swapper.ts similarity index 100% rename from solana/tests/scaas-liquidity.ts rename to solana/tests/stable-swapper.ts From 44e962285f8ac3662430fc32229c512135573f42 Mon Sep 17 00:00:00 2001 From: Braden Thompson Date: Wed, 13 May 2026 18:15:26 -0600 Subject: [PATCH 2/6] Chore: cleanup repo for oss publish. --- .github/workflows/test.yml | 91 ++++- README.md | 4 +- evm/README.md | 34 +- evm/foundry.toml | 2 +- evm/test/lib/StableSwapperBase.sol | 14 +- evm/test/unit/StableSwapper/allowlist.t.sol | 16 +- evm/test/unit/StableSwapper/swap.t.sol | 104 +++--- .../StableSwapper/updateFeeRecipient.t.sol | 8 +- .../StableSwapper/updateTokenStatus.t.sol | 2 +- solana/Anchor.toml | 6 +- solana/Cargo.lock | 16 +- solana/README.md | 243 ++++++------- solana/package.json | 2 +- solana/programs/stable-swapper/Cargo.toml | 6 +- solana/programs/stable-swapper/src/lib.rs | 2 +- solana/programs/stable-swapper/src/state.rs | 4 +- solana/tests/stable-swapper.ts | 338 +++++++++--------- 17 files changed, 453 insertions(+), 439 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7cb3d46..d96c8d2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -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 @@ -29,23 +25,80 @@ 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: 2.2.21 + 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: Install Solana CLI + run: | + sh -c "$(curl -sSfL https://release.anza.xyz/v${SOLANA_VERSION}/install)" + echo "$HOME/.local/share/solana/install/active_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: Install JS dependencies + run: yarn install --frozen-lockfile + working-directory: solana + + - name: Generate ephemeral program keypair and align IDs + working-directory: solana + run: | + 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 + + - name: Run Anchor tests + working-directory: solana + run: anchor test --provider.cluster localnet --skip-build diff --git a/README.md b/README.md index 92b6137..0447f41 100644 --- a/README.md +++ b/README.md @@ -30,19 +30,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 ``` diff --git a/evm/README.md b/evm/README.md index d3144d9..41054c0 100644 --- a/evm/README.md +++ b/evm/README.md @@ -38,35 +38,9 @@ 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= -export DEFAULT_ADMIN=
-export TREASURY_AUTHORITY=
-export CONFIGURE_AUTHORITY=
-export PAUSE_AUTHORITY=
-export FEE_RECIPIENT=
-export FEE_BASIS_POINTS= -export ADMIN_TRANSFER_DELAY= - -forge script script/DeployStableSwapper.s.sol:DeployStableSwapper \ - --rpc-url $RPC_URL \ - --broadcast \ - --verify -``` - -## Verify an Existing Deployment - -```sh -export RPC_URL= -export STABLE_SWAPPER_PROXY= - -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 @@ -74,10 +48,6 @@ forge script script/VerifyDeployment.s.sol:VerifyDeployment \ 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 diff --git a/evm/foundry.toml b/evm/foundry.toml index 495040e..5b2acff 100644 --- a/evm/foundry.toml +++ b/evm/foundry.toml @@ -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 diff --git a/evm/test/lib/StableSwapperBase.sol b/evm/test/lib/StableSwapperBase.sol index 21c517f..3791374 100644 --- a/evm/test/lib/StableSwapperBase.sol +++ b/evm/test/lib/StableSwapperBase.sol @@ -33,7 +33,7 @@ contract StableSwapperBase is Test { StableSwapper public swapper; MockERC20 public usdc; - MockERC20 public appStable; + MockERC20 public customStable; address public defaultAdmin; address public treasuryAuthority; @@ -58,7 +58,7 @@ contract StableSwapperBase is Test { // Deploy tokens usdc = new MockERC20("USD Coin", "USDC", 6); - appStable = new MockERC20("App Stable", "APPSTABLE", 6); + customStable = new MockERC20("Custom Stable", "CSTBL", 6); // Deploy implementation implementation = new StableSwapper(); @@ -80,11 +80,11 @@ contract StableSwapperBase is Test { // Mint tokens to wallet0 usdc.mint(wallet0, 1000 * 10 ** 6); // 1000 USDC - appStable.mint(wallet0, 1000 * 10 ** 6); // 1000 AppStable + customStable.mint(wallet0, 1000 * 10 ** 6); // 1000 CustomStable // Mint tokens to treasury authority for liquidity operations usdc.mint(treasuryAuthority, 1000 * 10 ** 6); - appStable.mint(treasuryAuthority, 1000 * 10 ** 6); + customStable.mint(treasuryAuthority, 1000 * 10 ** 6); } /** @@ -94,19 +94,19 @@ contract StableSwapperBase is Test { // Configure authority lists tokens vm.startPrank(configureAuthority); swapper.updateTokenListing(address(usdc), true); - swapper.updateTokenListing(address(appStable), true); + swapper.updateTokenListing(address(customStable), true); vm.stopPrank(); // Pause authority enables tokens vm.startPrank(pauseAuthority); swapper.updateTokenStatus(address(usdc), true); - swapper.updateTokenStatus(address(appStable), true); + swapper.updateTokenStatus(address(customStable), true); vm.stopPrank(); // Treasury authority deposits liquidity vm.startPrank(treasuryAuthority); usdc.transfer(address(swapper), 500 * 10 ** 6); - appStable.transfer(address(swapper), 500 * 10 ** 6); + customStable.transfer(address(swapper), 500 * 10 ** 6); vm.stopPrank(); } } diff --git a/evm/test/unit/StableSwapper/allowlist.t.sol b/evm/test/unit/StableSwapper/allowlist.t.sol index e1e073b..9d3229d 100644 --- a/evm/test/unit/StableSwapper/allowlist.t.sol +++ b/evm/test/unit/StableSwapper/allowlist.t.sol @@ -93,7 +93,7 @@ contract AllowlistTest is StableSwapperBase { vm.startPrank(wallet2); usdc.approve(address(swapper), swapAmount); vm.expectRevert(abi.encodeWithSelector(StableSwapper.AddressNotInAllowlist.selector, wallet2)); - swapper.swap(address(usdc), address(appStable), swapAmount, swapAmount, wallet1); + swapper.swap(address(usdc), address(customStable), swapAmount, swapAmount, wallet1); vm.stopPrank(); } @@ -115,7 +115,7 @@ contract AllowlistTest is StableSwapperBase { vm.startPrank(wallet1); usdc.approve(address(swapper), swapAmount); vm.expectRevert(abi.encodeWithSelector(StableSwapper.AddressNotInAllowlist.selector, wallet1)); - swapper.swap(address(usdc), address(appStable), swapAmount, swapAmount, wallet1); + swapper.swap(address(usdc), address(customStable), swapAmount, swapAmount, wallet1); vm.stopPrank(); } @@ -165,10 +165,10 @@ contract AllowlistTest is StableSwapperBase { vm.startPrank(wallet2); usdc.approve(address(swapper), swapAmount); - swapper.swap(address(usdc), address(appStable), swapAmount, swapAmount, wallet1); + swapper.swap(address(usdc), address(customStable), swapAmount, swapAmount, wallet1); vm.stopPrank(); - assertEq(appStable.balanceOf(wallet1), swapAmount); + assertEq(customStable.balanceOf(wallet1), swapAmount); } function test_swap_allowsAllowlistedUser() public { @@ -188,10 +188,10 @@ contract AllowlistTest is StableSwapperBase { vm.startPrank(wallet1); usdc.approve(address(swapper), swapAmount); - swapper.swap(address(usdc), address(appStable), swapAmount, swapAmount, wallet1); + swapper.swap(address(usdc), address(customStable), swapAmount, swapAmount, wallet1); vm.stopPrank(); - assertEq(appStable.balanceOf(wallet1), swapAmount); + assertEq(customStable.balanceOf(wallet1), swapAmount); } function test_swap_allowsAnyUser_afterAllowlistDisabled() public { @@ -210,9 +210,9 @@ contract AllowlistTest is StableSwapperBase { vm.startPrank(wallet2); usdc.approve(address(swapper), swapAmount); - swapper.swap(address(usdc), address(appStable), swapAmount, swapAmount, wallet1); + swapper.swap(address(usdc), address(customStable), swapAmount, swapAmount, wallet1); vm.stopPrank(); - assertTrue(appStable.balanceOf(wallet1) > 0); + assertTrue(customStable.balanceOf(wallet1) > 0); } } diff --git a/evm/test/unit/StableSwapper/swap.t.sol b/evm/test/unit/StableSwapper/swap.t.sol index e275767..b00ce0c 100644 --- a/evm/test/unit/StableSwapper/swap.t.sol +++ b/evm/test/unit/StableSwapper/swap.t.sol @@ -25,7 +25,7 @@ contract SwapTest is StableSwapperBase { vm.startPrank(wallet0); usdc.approve(address(swapper), swapAmount); vm.expectRevert(StableSwapper.SwapsCannotBePaused.selector); - swapper.swap(address(usdc), address(appStable), swapAmount, swapAmount, wallet0); + swapper.swap(address(usdc), address(customStable), swapAmount, swapAmount, wallet0); vm.stopPrank(); vm.prank(pauseAuthority); @@ -40,7 +40,7 @@ contract SwapTest is StableSwapperBase { vm.startPrank(wallet0); usdc.approve(address(swapper), swapAmount); vm.expectRevert(StableSwapper.CannotBeZeroAddress.selector); - swapper.swap(address(usdc), address(appStable), swapAmount, swapAmount, address(0)); + swapper.swap(address(usdc), address(customStable), swapAmount, swapAmount, address(0)); vm.stopPrank(); } @@ -75,7 +75,7 @@ contract SwapTest is StableSwapperBase { vm.startPrank(wallet0); usdc.approve(address(swapper), swapAmount); vm.expectRevert(abi.encodeWithSelector(StableSwapper.TokenMustBeSwappable.selector, address(usdc))); - swapper.swap(address(usdc), address(appStable), swapAmount, swapAmount, wallet0); + swapper.swap(address(usdc), address(customStable), swapAmount, swapAmount, wallet0); vm.stopPrank(); } @@ -90,9 +90,9 @@ contract SwapTest is StableSwapperBase { uint256 swapAmount = 10 * 10 ** 6; vm.startPrank(wallet0); - appStable.approve(address(swapper), swapAmount); + customStable.approve(address(swapper), swapAmount); vm.expectRevert(abi.encodeWithSelector(StableSwapper.TokenMustBeSwappable.selector, address(usdc))); - swapper.swap(address(appStable), address(usdc), swapAmount, swapAmount, wallet0); + swapper.swap(address(customStable), address(usdc), swapAmount, swapAmount, wallet0); vm.stopPrank(); } @@ -105,7 +105,7 @@ contract SwapTest is StableSwapperBase { vm.startPrank(wallet0); usdc.approve(address(swapper), swapAmount); vm.expectRevert(StableSwapper.CannotBeZeroAmount.selector); - swapper.swap(address(usdc), address(appStable), 0, minAmountOut, wallet0); + swapper.swap(address(usdc), address(customStable), 0, minAmountOut, wallet0); vm.stopPrank(); } @@ -117,7 +117,7 @@ contract SwapTest is StableSwapperBase { vm.startPrank(wallet0); usdc.approve(address(swapper), swapAmount); vm.expectRevert(StableSwapper.CannotBeZeroAmount.selector); - swapper.swap(address(usdc), address(appStable), swapAmount, 0, wallet0); + swapper.swap(address(usdc), address(customStable), swapAmount, 0, wallet0); vm.stopPrank(); } @@ -134,7 +134,7 @@ contract SwapTest is StableSwapperBase { vm.startPrank(wallet0); usdc.approve(address(swapper), swapAmount); vm.expectRevert(abi.encodeWithSelector(StableSwapper.TokenMustBeSwappable.selector, address(usdc))); - swapper.swap(address(usdc), address(appStable), swapAmount, swapAmount, wallet0); + swapper.swap(address(usdc), address(customStable), swapAmount, swapAmount, wallet0); vm.stopPrank(); } @@ -149,9 +149,9 @@ contract SwapTest is StableSwapperBase { uint256 swapAmount = 10 * 10 ** 6; vm.startPrank(wallet0); - appStable.approve(address(swapper), swapAmount); + customStable.approve(address(swapper), swapAmount); vm.expectRevert(abi.encodeWithSelector(StableSwapper.TokenMustBeSwappable.selector, address(usdc))); - swapper.swap(address(appStable), address(usdc), swapAmount, swapAmount, wallet0); + swapper.swap(address(customStable), address(usdc), swapAmount, swapAmount, wallet0); vm.stopPrank(); } @@ -179,7 +179,7 @@ contract SwapTest is StableSwapperBase { vm.startPrank(wallet0); usdc.approve(address(swapper), amountThatCausesOverflow); vm.expectRevert(abi.encodeWithSignature("Panic(uint256)", 0x11)); - swapper.swap(address(usdc), address(appStable), amountThatCausesOverflow, 1, wallet0); + swapper.swap(address(usdc), address(customStable), amountThatCausesOverflow, 1, wallet0); vm.stopPrank(); } @@ -207,37 +207,37 @@ contract SwapTest is StableSwapperBase { vm.startPrank(configureAuthority); swapper.updateTokenListing(address(usdc), true); - swapper.updateTokenListing(address(appStable), true); + swapper.updateTokenListing(address(customStable), true); vm.stopPrank(); vm.startPrank(pauseAuthority); swapper.updateTokenStatus(address(usdc), true); - swapper.updateTokenStatus(address(appStable), true); + swapper.updateTokenStatus(address(customStable), true); vm.stopPrank(); vm.startPrank(treasuryAuthority); usdc.transfer(address(swapper), initialLiquidity); - appStable.transfer(address(swapper), initialLiquidity); + customStable.transfer(address(swapper), initialLiquidity); - // Set reserved amount on appStable - swapper.updateReservedAmount(address(appStable), reservedAmount); + // Set reserved amount on customStable + swapper.updateReservedAmount(address(customStable), reservedAmount); // Withdraw liquidity below the reserved amount // This leaves balance < reservedAmount, triggering the check at line 364 - swapper.withdrawLiquidity(address(appStable), withdrawAmount, treasuryAuthority); + swapper.withdrawLiquidity(address(customStable), withdrawAmount, treasuryAuthority); vm.stopPrank(); // Verify the balance is now below reserved amount - uint256 currentBalance = appStable.balanceOf(address(swapper)); + uint256 currentBalance = customStable.balanceOf(address(swapper)); assertLt(currentBalance, reservedAmount, "Balance should be less than reserved amount"); - // Try to swap USDC -> APPSTABLE, should revert because balance <= reserved amount + // Try to swap USDC -> CSTBL, should revert because balance <= reserved amount vm.startPrank(wallet0); usdc.approve(address(swapper), swapAmount); vm.expectRevert( - abi.encodeWithSelector(StableSwapper.TokenOutBalanceLessThanReservedAmount.selector, address(appStable)) + abi.encodeWithSelector(StableSwapper.TokenOutBalanceLessThanReservedAmount.selector, address(customStable)) ); - swapper.swap(address(usdc), address(appStable), swapAmount, swapAmount, wallet0); + swapper.swap(address(usdc), address(customStable), swapAmount, swapAmount, wallet0); vm.stopPrank(); } @@ -268,25 +268,25 @@ contract SwapTest is StableSwapperBase { vm.startPrank(configureAuthority); swapper.updateTokenListing(address(usdc), true); - swapper.updateTokenListing(address(appStable), true); + swapper.updateTokenListing(address(customStable), true); swapper.updateFeeBasisPoints(feeRateBps); vm.stopPrank(); vm.startPrank(pauseAuthority); swapper.updateTokenStatus(address(usdc), true); - swapper.updateTokenStatus(address(appStable), true); + swapper.updateTokenStatus(address(customStable), true); vm.stopPrank(); vm.startPrank(treasuryAuthority); usdc.transfer(address(swapper), liquidityAmount); - appStable.transfer(address(swapper), liquidityAmount); + customStable.transfer(address(swapper), liquidityAmount); vm.stopPrank(); vm.startPrank(wallet0); usdc.approve(address(swapper), tinyAmount); // When amountOut is 0, it will fail slippage check since minAmountOut is 1 vm.expectRevert(StableSwapper.SlippageExceeded.selector); - swapper.swap(address(usdc), address(appStable), tinyAmount, 1, wallet0); + swapper.swap(address(usdc), address(customStable), tinyAmount, 1, wallet0); vm.stopPrank(); // Reset fee @@ -319,7 +319,7 @@ contract SwapTest is StableSwapperBase { vm.startPrank(wallet0); usdc.approve(address(swapper), swapAmount); vm.expectRevert(StableSwapper.SlippageExceeded.selector); - swapper.swap(address(usdc), address(appStable), swapAmount, minAmountOut, wallet0); + swapper.swap(address(usdc), address(customStable), swapAmount, minAmountOut, wallet0); vm.stopPrank(); // Reset fee @@ -337,18 +337,18 @@ contract SwapTest is StableSwapperBase { // Add tokens vm.startPrank(configureAuthority); swapper.updateTokenListing(address(usdc), true); - swapper.updateTokenListing(address(appStable), true); + swapper.updateTokenListing(address(customStable), true); vm.stopPrank(); vm.startPrank(pauseAuthority); swapper.updateTokenStatus(address(usdc), true); - swapper.updateTokenStatus(address(appStable), true); + swapper.updateTokenStatus(address(customStable), true); vm.stopPrank(); // Deposit limited liquidity vm.startPrank(treasuryAuthority); usdc.transfer(address(swapper), limitedLiquidity); - appStable.transfer(address(swapper), limitedLiquidity); + customStable.transfer(address(swapper), limitedLiquidity); vm.stopPrank(); // Try to swap more than available @@ -359,7 +359,7 @@ contract SwapTest is StableSwapperBase { StableSwapper.AmountOutExceedsAvailableLiquidity.selector, excessiveSwapAmount, limitedLiquidity ) ); - swapper.swap(address(usdc), address(appStable), excessiveSwapAmount, excessiveSwapAmount, wallet0); + swapper.swap(address(usdc), address(customStable), excessiveSwapAmount, excessiveSwapAmount, wallet0); vm.stopPrank(); } @@ -376,17 +376,17 @@ contract SwapTest is StableSwapperBase { // Add tokens vm.startPrank(configureAuthority); swapper.updateTokenListing(address(usdc), true); - swapper.updateTokenListing(address(appStable), true); + swapper.updateTokenListing(address(customStable), true); vm.stopPrank(); vm.startPrank(pauseAuthority); swapper.updateTokenStatus(address(usdc), true); - swapper.updateTokenStatus(address(appStable), true); + swapper.updateTokenStatus(address(customStable), true); vm.stopPrank(); vm.startPrank(treasuryAuthority); usdc.transfer(address(swapper), depositedLiquidity); - appStable.transfer(address(swapper), depositedLiquidity); + customStable.transfer(address(swapper), depositedLiquidity); // Set reserved amount on USDC swapper.updateReservedAmount(address(usdc), reservedAmount); @@ -394,13 +394,13 @@ contract SwapTest is StableSwapperBase { // Try to swap more than available (balance - reserved) vm.startPrank(wallet0); - appStable.approve(address(swapper), swapAmount); + customStable.approve(address(swapper), swapAmount); vm.expectRevert( abi.encodeWithSelector( StableSwapper.AmountOutExceedsAvailableLiquidity.selector, swapAmount, availableLiquidity ) ); - swapper.swap(address(appStable), address(usdc), swapAmount, swapAmount, wallet0); + swapper.swap(address(customStable), address(usdc), swapAmount, swapAmount, wallet0); vm.stopPrank(); } @@ -408,7 +408,7 @@ contract SwapTest is StableSwapperBase { SUCCESS TESTS //////////////////////////////////////////////////////////////*/ - function testFuzz_swap_transfersTokensCorrectly_usdcToAppStable(uint256 swapAmountSeed, uint256 minAmountOutSeed) + function testFuzz_swap_transfersTokensCorrectly_usdcToCustomStable(uint256 swapAmountSeed, uint256 minAmountOutSeed) public { setupBasicSwapEnvironment(); @@ -417,18 +417,18 @@ contract SwapTest is StableSwapperBase { uint256 minAmountOut = bound(minAmountOutSeed, 1, swapAmount); uint256 initialUserUsdc = usdc.balanceOf(wallet0); - uint256 initialUserAppStable = appStable.balanceOf(wallet0); + uint256 initialUserCustomStable = customStable.balanceOf(wallet0); vm.startPrank(wallet0); usdc.approve(address(swapper), swapAmount); - swapper.swap(address(usdc), address(appStable), swapAmount, minAmountOut, wallet0); + swapper.swap(address(usdc), address(customStable), swapAmount, minAmountOut, wallet0); vm.stopPrank(); assertEq(usdc.balanceOf(wallet0), initialUserUsdc - swapAmount); - assertEq(appStable.balanceOf(wallet0), initialUserAppStable + swapAmount); + assertEq(customStable.balanceOf(wallet0), initialUserCustomStable + swapAmount); } - function testFuzz_swap_transfersTokensCorrectly_appStableToUsdc(uint256 swapAmountSeed, uint256 minAmountOutSeed) + function testFuzz_swap_transfersTokensCorrectly_customStableToUsdc(uint256 swapAmountSeed, uint256 minAmountOutSeed) public { setupBasicSwapEnvironment(); @@ -437,14 +437,14 @@ contract SwapTest is StableSwapperBase { uint256 minAmountOut = bound(minAmountOutSeed, 1, swapAmount); uint256 initialUserUsdc = usdc.balanceOf(wallet0); - uint256 initialUserAppStable = appStable.balanceOf(wallet0); + uint256 initialUserCustomStable = customStable.balanceOf(wallet0); vm.startPrank(wallet0); - appStable.approve(address(swapper), swapAmount); - swapper.swap(address(appStable), address(usdc), swapAmount, minAmountOut, wallet0); + customStable.approve(address(swapper), swapAmount); + swapper.swap(address(customStable), address(usdc), swapAmount, minAmountOut, wallet0); vm.stopPrank(); - assertEq(appStable.balanceOf(wallet0), initialUserAppStable - swapAmount); + assertEq(customStable.balanceOf(wallet0), initialUserCustomStable - swapAmount); assertEq(usdc.balanceOf(wallet0), initialUserUsdc + swapAmount); } @@ -582,15 +582,15 @@ contract SwapTest is StableSwapperBase { swapper.updateFeeBasisPoints(feeRateBps); uint256 initialFeeRecipientBalance = usdc.balanceOf(feeRecipient); - uint256 initialUserAppStable = appStable.balanceOf(wallet0); + uint256 initialUserCustomStable = customStable.balanceOf(wallet0); vm.startPrank(wallet0); usdc.approve(address(swapper), swapAmount); - swapper.swap(address(usdc), address(appStable), swapAmount, minAmountOut, wallet0); + swapper.swap(address(usdc), address(customStable), swapAmount, minAmountOut, wallet0); vm.stopPrank(); assertEq(usdc.balanceOf(feeRecipient), initialFeeRecipientBalance + expectedFee); - assertEq(appStable.balanceOf(wallet0), initialUserAppStable + expectedNetOutput); + assertEq(customStable.balanceOf(wallet0), initialUserCustomStable + expectedNetOutput); // Reset fee vm.prank(configureAuthority); @@ -605,15 +605,15 @@ contract SwapTest is StableSwapperBase { uint256 expectedOutput = 50 * 10 ** 6; uint256 initialUserUsdc = usdc.balanceOf(wallet0); - uint256 initialUserAppStable = appStable.balanceOf(wallet0); + uint256 initialUserCustomStable = customStable.balanceOf(wallet0); vm.startPrank(wallet0); usdc.approve(address(swapper), swapAmount); - swapper.swap(address(usdc), address(appStable), swapAmount, minAmountOut, wallet0); + swapper.swap(address(usdc), address(customStable), swapAmount, minAmountOut, wallet0); vm.stopPrank(); assertEq(usdc.balanceOf(wallet0), initialUserUsdc - swapAmount); - assertEq(appStable.balanceOf(wallet0), initialUserAppStable + expectedOutput); + assertEq(customStable.balanceOf(wallet0), initialUserCustomStable + expectedOutput); } function test_swap_roundsUpFees_whenFractionalAmount() public { @@ -635,7 +635,7 @@ contract SwapTest is StableSwapperBase { vm.startPrank(wallet0); usdc.approve(address(swapper), swapAmount); - swapper.swap(address(usdc), address(appStable), swapAmount, minAmountOut, wallet0); + swapper.swap(address(usdc), address(customStable), swapAmount, minAmountOut, wallet0); vm.stopPrank(); uint256 feeCollected = usdc.balanceOf(feeRecipient) - initialFeeRecipientBalance; @@ -663,7 +663,7 @@ contract SwapTest is StableSwapperBase { vm.startPrank(wallet0); usdc.approve(address(swapper), swapAmount); - swapper.swap(address(usdc), address(appStable), swapAmount, minAmountOut, wallet0); + swapper.swap(address(usdc), address(customStable), swapAmount, minAmountOut, wallet0); vm.stopPrank(); uint256 feeCollected = usdc.balanceOf(feeRecipient) - initialFeeRecipientBalance; diff --git a/evm/test/unit/StableSwapper/updateFeeRecipient.t.sol b/evm/test/unit/StableSwapper/updateFeeRecipient.t.sol index e033b72..bcd8db6 100644 --- a/evm/test/unit/StableSwapper/updateFeeRecipient.t.sol +++ b/evm/test/unit/StableSwapper/updateFeeRecipient.t.sol @@ -38,7 +38,7 @@ contract UpdateFeeRecipientTest is StableSwapperBase { vm.startPrank(configureAuthority); swapper.updateTokenListing(address(usdc), true); - swapper.updateTokenListing(address(appStable), true); + swapper.updateTokenListing(address(customStable), true); // Update fee recipient and set 1% fee swapper.updateFeeRecipient(newFeeRecipient); @@ -47,12 +47,12 @@ contract UpdateFeeRecipientTest is StableSwapperBase { vm.startPrank(pauseAuthority); swapper.updateTokenStatus(address(usdc), true); - swapper.updateTokenStatus(address(appStable), true); + swapper.updateTokenStatus(address(customStable), true); vm.stopPrank(); vm.startPrank(treasuryAuthority); usdc.transfer(address(swapper), liquidityAmount); - appStable.transfer(address(swapper), liquidityAmount); + customStable.transfer(address(swapper), liquidityAmount); vm.stopPrank(); uint64 swapAmount = 100 * 10 ** 6; @@ -61,7 +61,7 @@ contract UpdateFeeRecipientTest is StableSwapperBase { vm.startPrank(wallet0); usdc.approve(address(swapper), swapAmount); - swapper.swap(address(usdc), address(appStable), swapAmount, minAmountOut, wallet0); + swapper.swap(address(usdc), address(customStable), swapAmount, minAmountOut, wallet0); vm.stopPrank(); assertEq(usdc.balanceOf(newFeeRecipient), expectedFee); diff --git a/evm/test/unit/StableSwapper/updateTokenStatus.t.sol b/evm/test/unit/StableSwapper/updateTokenStatus.t.sol index 165cf05..b6c546f 100644 --- a/evm/test/unit/StableSwapper/updateTokenStatus.t.sol +++ b/evm/test/unit/StableSwapper/updateTokenStatus.t.sol @@ -76,7 +76,7 @@ contract UpdateTokenStatusTest is StableSwapperBase { vm.startPrank(wallet0); usdc.approve(address(swapper), swapAmount); - swapper.swap(address(usdc), address(appStable), swapAmount, minAmountOut, wallet0); + swapper.swap(address(usdc), address(customStable), swapAmount, minAmountOut, wallet0); vm.stopPrank(); } } diff --git a/solana/Anchor.toml b/solana/Anchor.toml index 9c90b35..1aaa989 100644 --- a/solana/Anchor.toml +++ b/solana/Anchor.toml @@ -6,13 +6,13 @@ resolution = true skip-lint = false [programs.localnet] -scaas_liquidity = "pqgqKahpG1y2wsgxFhzaAnkV1cL9vk8MSg9qm4q646F" +stable_swapper = "pqgqKahpG1y2wsgxFhzaAnkV1cL9vk8MSg9qm4q646F" [programs.devnet] -scaas_liquidity = "9vDwZVJXw5nxymWmUcgmNpemDH5EBcJwLNhtsznrgJDH" +stable_swapper = "9vDwZVJXw5nxymWmUcgmNpemDH5EBcJwLNhtsznrgJDH" [programs.mainnet] -scaas_liquidity = "pqgqKahpG1y2wsgxFhzaAnkV1cL9vk8MSg9qm4q646F" +stable_swapper = "pqgqKahpG1y2wsgxFhzaAnkV1cL9vk8MSg9qm4q646F" [registry] url = "https://api.apr.dev" diff --git a/solana/Cargo.lock b/solana/Cargo.lock index c861f4a..54dd782 100644 --- a/solana/Cargo.lock +++ b/solana/Cargo.lock @@ -1206,14 +1206,6 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" -[[package]] -name = "scaas-liquidity" -version = "0.1.0" -dependencies = [ - "anchor-lang", - "anchor-spl", -] - [[package]] name = "scopeguard" version = "1.2.0" @@ -2554,6 +2546,14 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "stable-swapper" +version = "0.1.0" +dependencies = [ + "anchor-lang", + "anchor-spl", +] + [[package]] name = "subtle" version = "2.6.1" diff --git a/solana/README.md b/solana/README.md index cb7e5a5..5d21b97 100644 --- a/solana/README.md +++ b/solana/README.md @@ -1,99 +1,96 @@ -# SCaaS - Stablecoin-as-a-Service Liquidity Management +# StableSwapper -- Solana -A production-ready Solana-based liquidity management system designed for secure, efficient 1:1 stablecoin swapping with configurable fees and comprehensive admin controls. +Solana / Anchor implementation of StableSwapper: a 1:1 stablecoin swap program that lets a custom stablecoin be paired with USDC (or other listed stablecoins) at a fixed rate with configurable fees, slippage protection, and administrative controls. -## 🏗️ Key Features +## Features -- ✅ **1:1 Token Swaps**: Guaranteed parity swapping between supported stablecoins -- ✅ **Dual Authority Model**: Separate operations and pause authorities with multisig support -- ✅ **Slippage Protection**: User-defined minimum output amounts prevent unexpected losses -- ✅ **Granular Pause Controls**: Independent pause flags for swaps and liquidity management -- ✅ **Configurable Fees**: Admin-controlled fee rates (0-10% max) with separate fee recipient -- ✅ **Multi-token Support**: Dynamic token addition with vault creation (up to 50 tokens) -- ✅ **Access Controls**: Comprehensive authority validation and security measures +- **1:1 Token Swaps** -- Fixed-rate swapping between any listed stablecoins +- **Dual Authority Model** -- Separate operations and pause authorities, both compatible with multisigs +- **Slippage Protection** -- User-supplied minimum output amount per swap +- **Granular Pause Controls** -- Independent pause flags for swaps and liquidity management +- **Configurable Fees** -- Admin-controlled fee rate (0--10%) with a dedicated fee recipient +- **Multi-token Support** -- Add up to 50 supported tokens, each with its own vault +- **Access Controls** -- Authority validation enforced via PDAs -## 📁 Project Structure +## Project Structure ``` -├── programs/scaas-liquidity/ # Solana program (Rust/Anchor) -│ ├── src/ -│ │ |── lib.rs # Main program logic -│ │ |── state.rs -│ │ |── constants.rs -│ │ └── errors.rs -│ └── Cargo.toml -├── tests/ # Program tests -├── target/ # Build artifacts -├── Anchor.toml # Anchor configuration -└── Cargo.toml # Workspace configuration +solana/ +├── programs/stable-swapper/ # Solana program (Rust/Anchor) +│ └── src/ +│ ├── lib.rs # Main program logic +│ ├── state.rs +│ ├── constants.rs +│ ├── errors.rs +│ └── utils.rs +├── tests/ # Program tests (Anchor / Mocha) +├── Anchor.toml # Anchor configuration +└── Cargo.toml # Workspace configuration ``` -## 🚀 Getting Started +## Prerequisites -### Prerequisites - -- **Rust** 1.70.0+ -- **Node.js** 18.0.0+ +- **Rust** 1.70+ +- **Solana CLI** 2.2.21+ - **Anchor CLI** 0.31.1+ -- **Solana CLI** 1.18.0+ +- **Node.js** 18+ with Yarn -### Installation +## Getting Started 1. **Clone the repository** - ```bash - git clone https://github.com/coinbase/stable-swapper.git - cd stable-swapper/svm - ``` + +```bash +git clone https://github.com/coinbase/stable-swapper.git +cd stable-swapper/solana +``` 2. **Install dependencies** - ```bash - # Install Rust dependencies - cargo build - ``` + +```bash +cargo build +yarn install --frozen-lockfile +``` 3. **Configure Solana for development** - ```bash - # Set to devnet - solana config set --url devnet - # Create a keypair (if needed) - solana-keygen new --outfile ~/.config/solana/id.json +```bash +# Set to devnet +solana config set --url devnet + +# Create a keypair (if needed) +solana-keygen new --outfile ~/.config/solana/id.json - # Airdrop SOL for testing - solana airdrop 2 - ``` +# Airdrop SOL for testing +solana airdrop 2 +``` -## 🔧 Development Workflow +## Development Workflow -### Building the Solana Program +### Building the program ```bash -# Build the program anchor build - -# Deploy to devnet -anchor deploy --provider.cluster devnet ``` -### Running the Test Suite +### Running the test suite The committed `declare_id!` and `[programs.devnet]` / `[programs.mainnet]` -entries point at the real deployed programs. To run the Anchor / Mocha suite -against a local validator, generate a throwaway keypair and align all three -references to it before building: +entries in `Anchor.toml` point at the real deployed programs. To run the +Anchor / Mocha suite against a local validator, generate a throwaway keypair +and align all three references to it before building: ```bash # Mint an ephemeral test keypair and align the program ID everywhere mkdir -p target/deploy solana-keygen new --no-bip39-passphrase --silent --force \ - --outfile target/deploy/scaas_liquidity-keypair.json -TEST_ID=$(solana address -k target/deploy/scaas_liquidity-keypair.json) + --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/scaas-liquidity/src/lib.rs + programs/stable-swapper/src/lib.rs awk -v id="$TEST_ID" ' /^\[/ { in_localnet = ($0 ~ /^\[programs\.localnet\]$/) } - in_localnet && /^scaas_liquidity[[:space:]]*=/ { - print "scaas_liquidity = \"" id "\""; next + in_localnet && /^stable_swapper[[:space:]]*=/ { + print "stable_swapper = \"" id "\""; next } { print } ' Anchor.toml > Anchor.toml.tmp && mv Anchor.toml.tmp Anchor.toml @@ -104,90 +101,86 @@ anchor build anchor test --provider.cluster localnet --skip-build # Restore the committed IDs when done -git checkout -- programs/scaas-liquidity/src/lib.rs Anchor.toml +git checkout -- programs/stable-swapper/src/lib.rs Anchor.toml ``` -CI runs the equivalent of these steps in `.github/workflows/test.yml`. +### Deploying -### Network Configuration +```bash +# Build verifiably +anchor build --verifiable -The system is configured for **Solana Devnet** by default. To change networks: +# Deploy +anchor deploy --provider.cluster -2. Update your Solana CLI configuration: - ```bash - solana config set --url mainnet-beta # or devnet - ``` +# Verify deployment +solana program show +``` -## 🏛️ Program Architecture +Deployments, pool initialization, and authority management are performed via +out-of-repo tooling; the contract under `programs/` is the source of truth +for the on-chain behavior. -### Design Philosophy +## Program Architecture -**SCaaS uses a single centralized pool** for all users and tokens: -- Pool PDA: `[b"liquidity_pool"]` (no authority in seeds) -- Only ONE pool exists per program deployment -- All users interact with the same global pool -- Authority controls the pool but doesn't "own" separate instances +### Pool design -**Fee Model**: -- Fees are charged on the **input token** (the token being swapped FROM) -- User provides the full swap amount, which is split: - - **Net amount** (after fee) → goes to vault as liquidity - - **Fee amount** → goes to fee_recipient as protocol revenue -- Example: Swap 100 USDC → AppStable with 1% fee: - - User transfers: 100 USDC total - - Vault receives: 99 USDC (liquidity) - - Fee recipient receives: 1 USDC (protocol fee) - - User receives: 99 AppStable (1:1 with net amount) +The program uses a single centralized pool for all users and tokens: +- Pool PDA: `[b"liquidity_pool"]` (no authority in the seeds) +- Exactly one pool exists per program deployment +- All users interact with the same pool +- The operations authority controls the pool but does not "own" separate instances -**Swap Account Model**: -- Swaps are permissionless when `swaps_paused` is false and both tokens are enabled -- `user_from_token_account` does not need to be owned by `user`; the SPL Token program enforces that `user` is either the owner or a valid delegate -- `to_token_account` may be any valid token account for the output mint, so delegated swaps can route output to a recipient chosen by the delegate +### Fee model -### Core Instructions +Fees are charged on the **input token** (the token being swapped from). The user provides the full swap amount, which is split: -- **`initialize_pool`**: Creates pool with operations & pause authorities, fee configuration -- **`add_supported_token`**: Adds token with dedicated vault (operations authority) -- **`swap`**: Executes 1:1 swaps with slippage protection (`min_amount_out`) -- **`withdraw_liquidity`**: Removes liquidity from a vault (operations authority, checks `liquidity_paused`) -- **`update_fee_config`**: Updates fee rate and recipient (operations authority) -- **`update_pause_config`**: Controls `swaps_paused` and `liquidity_paused` (pause authority) -- **`update_operations_authority`**: Self-updates operations authority (operations authority only) -- **`update_pause_authority`**: Self-updates pause authority (pause authority only) +- **Net amount** (after fee) → goes to the destination vault as liquidity +- **Fee amount** → goes to the fee recipient as protocol revenue -Liquidity is seeded by sending tokens directly to the vault token account via an SPL Token transfer; there is no dedicated deposit instruction. +Example: swap 100 USDC → custom stablecoin with a 1% fee: +- User transfers: 100 USDC total +- Vault receives: 99 USDC (liquidity) +- Fee recipient receives: 1 USDC (protocol fee) +- User receives: 99 custom-stablecoin (1:1 with the net amount) -## 🔐 Security Features +### Swap permissions -### Access Controls -- **Dual authority model**: Separate operations and pause authorities (multisig-ready) -- **Self-updating authorities**: Each authority can only update itself -- **Authority validation**: Operations enforce constraints via PDAs -- **Granular pause controls**: Independent `swaps_paused` and `liquidity_paused` flags -- **Fee rate cap**: Maximum 10% (1000 basis points) enforced at program level +- Swaps are permissionless when `swaps_paused` is `false` and both tokens are enabled. +- `user_from_token_account` does not need to be owned by `user`; the SPL Token program enforces that `user` is either the owner or a valid delegate. +- `to_token_account` may be any valid token account for the output mint, so delegated swaps can route output to a recipient chosen by the delegate. -### Liquidity Safety -- **Slippage protection**: Users specify `min_amount_out` to prevent TOCTOU attacks -- **PDA-based validation**: Accounts validated using program-derived addresses -- **Balance validation**: Ensures sufficient vault balance before swaps and withdrawals -- **Overflow protection**: Checked arithmetic throughout +### Core instructions -### Error Handling -- **Comprehensive error codes**: Detailed error messages for debugging -- **Input validation**: All parameters validated at program level -- **Account ownership verification**: Fee recipient token accounts verified to match pool configuration +- **`initialize_pool`** — Creates the pool with operations + pause authorities and fee configuration +- **`add_supported_token`** — Adds a token with its dedicated vault (operations authority) +- **`swap`** — Executes a 1:1 swap with slippage protection (`min_amount_out`) +- **`withdraw_liquidity`** — Removes liquidity from a vault (operations authority, gated by `liquidity_paused`) +- **`update_fee_config`** — Updates the fee rate and recipient (operations authority) +- **`update_pause_config`** — Controls `swaps_paused` and `liquidity_paused` (pause authority) +- **`update_operations_authority`** — Operations authority updates itself +- **`update_pause_authority`** — Pause authority updates itself -## 🚀 Deployment +Liquidity is seeded by sending tokens directly to the vault token account via an SPL Token transfer; there is no dedicated deposit instruction. -### Program Deployment +## Security -```bash -# Build for production -anchor build --verifiable +### Access controls +- **Dual authority model**: separate operations and pause authorities (both multisig-ready) +- **Self-updating authorities**: each authority can only update itself +- **Authority validation**: enforced via program-derived addresses +- **Granular pause controls**: independent `swaps_paused` and `liquidity_paused` flags +- **Fee rate cap**: maximum 10% (1000 basis points) enforced at program level -# Deploy -anchor deploy --provider.cluster +### Liquidity safety +- **Slippage protection**: users supply `min_amount_out` to prevent TOCTOU attacks +- **PDA-based validation**: accounts validated using program-derived addresses +- **Balance validation**: ensures sufficient vault balance before swaps and withdrawals +- **Overflow protection**: checked arithmetic throughout -# Verify deployment -solana program show -``` +### Error handling +- **Comprehensive error codes**: detailed messages for debugging +- **Input validation**: all parameters validated at the program level +- **Account ownership verification**: fee recipient token accounts verified against the pool configuration + +For vulnerability disclosure, see the repository-root [`SECURITY.md`](../SECURITY.md). diff --git a/solana/package.json b/solana/package.json index b3e4a4a..5297855 100644 --- a/solana/package.json +++ b/solana/package.json @@ -1,5 +1,5 @@ { - "license": "ISC", + "license": "Apache-2.0", "scripts": { "lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w", "lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check" diff --git a/solana/programs/stable-swapper/Cargo.toml b/solana/programs/stable-swapper/Cargo.toml index ae7ddd1..03725a0 100644 --- a/solana/programs/stable-swapper/Cargo.toml +++ b/solana/programs/stable-swapper/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "scaas-liquidity" +name = "stable-swapper" version = "0.1.0" -description = "Created with Anchor" +description = "StableSwapper liquidity pool program" edition = "2021" [lib] crate-type = ["cdylib", "lib"] -name = "scaas_liquidity" +name = "stable_swapper" [features] default = [] diff --git a/solana/programs/stable-swapper/src/lib.rs b/solana/programs/stable-swapper/src/lib.rs index 0245b2a..6d1c558 100644 --- a/solana/programs/stable-swapper/src/lib.rs +++ b/solana/programs/stable-swapper/src/lib.rs @@ -19,7 +19,7 @@ declare_id!("9vDwZVJXw5nxymWmUcgmNpemDH5EBcJwLNhtsznrgJDH"); // close_whitelist instruction is not worth the complexity for the small amount involved. #[program] -pub mod scaas_liquidity { +pub mod stable_swapper { use super::*; pub fn initialize(ctx: Context, fee_rate: u64) -> Result<()> { diff --git a/solana/programs/stable-swapper/src/state.rs b/solana/programs/stable-swapper/src/state.rs index abc9866..3b17a5f 100644 --- a/solana/programs/stable-swapper/src/state.rs +++ b/solana/programs/stable-swapper/src/state.rs @@ -22,9 +22,9 @@ impl LiquidityPool { #[account] pub struct TokenVault { pub mint: Pubkey, - /// Deprecated: liquidity reservation was removed in STBLE-2811. + /// Deprecated: liquidity reservation was removed. #[deprecated( - note = "Liquidity reservation was removed in STBLE-2811; field retained for layout compatibility and is always zero on new vaults." + note = "Liquidity reservation was removed; field retained for layout compatibility and is always zero on new vaults." )] pub reserved_amount: u64, pub disabled: bool, // If true, this token cannot be used in swaps diff --git a/solana/tests/stable-swapper.ts b/solana/tests/stable-swapper.ts index de1bf85..f036215 100644 --- a/solana/tests/stable-swapper.ts +++ b/solana/tests/stable-swapper.ts @@ -1,6 +1,6 @@ import * as anchor from "@coral-xyz/anchor"; import { Program } from "@coral-xyz/anchor"; -import { ScaasLiquidity } from "../target/types/scaas_liquidity"; +import { StableSwapper } from "../target/types/stable_swapper"; import { PublicKey, SystemProgram } from "@solana/web3.js"; import { TOKEN_PROGRAM_ID, @@ -15,34 +15,34 @@ import { } from "@solana/spl-token"; import { assert } from "chai"; -describe("scaas-liquidity", () => { +describe("stable-swapper", () => { const provider = anchor.AnchorProvider.env(); anchor.setProvider(provider); - const program = anchor.workspace.scaasLiquidity as Program; + const program = anchor.workspace.stableSwapper as Program; const payer = provider.wallet as anchor.Wallet; const operationsAuthority = payer; // In tests, same as payer const pauseAuthority = payer; // In tests, same as payer // Test keypairs let usdcMint: PublicKey; - let appStableMint: PublicKey; + let customStableMint: PublicKey; let pool: PublicKey; let usdcVault: PublicKey; - let appStableVault: PublicKey; + let customStableVault: PublicKey; let usdcVaultTokenAccount: PublicKey; - let appStableVaultTokenAccount: PublicKey; + let customStableVaultTokenAccount: PublicKey; // User accounts (also used for fee collection since authority is the fee recipient in tests) let userUsdcAccount: PublicKey; - let userAppStableAccount: PublicKey; + let userCustomStableAccount: PublicKey; // Fee recipient token accounts (created when tokens are added) let feeRecipientUsdcAccount: PublicKey; - let feeRecipientAppStableAccount: PublicKey; + let feeRecipientCustomStableAccount: PublicKey; before(async () => { - // Create USDC and AppStable mints + // Create USDC and CustomStable mints usdcMint = await createMint( provider.connection, payer.payer, @@ -51,12 +51,12 @@ describe("scaas-liquidity", () => { 6 // USDC decimals ); - appStableMint = await createMint( + customStableMint = await createMint( provider.connection, payer.payer, payer.publicKey, null, - 6 // AppStable decimals + 6 // CustomStable decimals ); // Derive PDAs (pool is now a single centralized pool, no authority in seed) @@ -70,8 +70,8 @@ describe("scaas-liquidity", () => { program.programId ); - [appStableVault] = PublicKey.findProgramAddressSync( - [Buffer.from("token_vault"), pool.toBuffer(), appStableMint.toBuffer()], + [customStableVault] = PublicKey.findProgramAddressSync( + [Buffer.from("token_vault"), pool.toBuffer(), customStableMint.toBuffer()], program.programId ); @@ -80,8 +80,8 @@ describe("scaas-liquidity", () => { program.programId ); - [appStableVaultTokenAccount] = PublicKey.findProgramAddressSync( - [Buffer.from("vault_token_account"), appStableVault.toBuffer()], + [customStableVaultTokenAccount] = PublicKey.findProgramAddressSync( + [Buffer.from("vault_token_account"), customStableVault.toBuffer()], program.programId ); @@ -90,8 +90,8 @@ describe("scaas-liquidity", () => { usdcMint, payer.publicKey ); - feeRecipientAppStableAccount = await getAssociatedTokenAddress( - appStableMint, + feeRecipientCustomStableAccount = await getAssociatedTokenAddress( + customStableMint, payer.publicKey ); @@ -103,10 +103,10 @@ describe("scaas-liquidity", () => { payer.publicKey ); - userAppStableAccount = await createAccount( + userCustomStableAccount = await createAccount( provider.connection, payer.payer, - appStableMint, + customStableMint, payer.publicKey ); @@ -123,10 +123,10 @@ describe("scaas-liquidity", () => { await mintTo( provider.connection, payer.payer, - appStableMint, - userAppStableAccount, + customStableMint, + userCustomStableAccount, payer.payer, - 1000 * 10 ** 6 // 1000 AppStable + 1000 * 10 ** 6 // 1000 CustomStable ); }); @@ -195,16 +195,16 @@ describe("scaas-liquidity", () => { ); }); - it("Adds AppStable as supported token", async () => { + it("Adds CustomStable as supported token", async () => { await program.methods .addSupportedToken() .accounts({ pool, - vault: appStableVault, - vaultTokenAccount: appStableVaultTokenAccount, - feeRecipientTokenAccount: feeRecipientAppStableAccount, + vault: customStableVault, + vaultTokenAccount: customStableVaultTokenAccount, + feeRecipientTokenAccount: feeRecipientCustomStableAccount, feeRecipient: payer.publicKey, - mint: appStableMint, + mint: customStableMint, operationsAuthority: operationsAuthority.publicKey, tokenProgram: TOKEN_PROGRAM_ID, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, @@ -238,8 +238,8 @@ describe("scaas-liquidity", () => { await transfer( provider.connection, payer.payer, - userAppStableAccount, - appStableVaultTokenAccount, + userCustomStableAccount, + customStableVaultTokenAccount, payer.payer, seedAmount ); @@ -340,18 +340,18 @@ describe("scaas-liquidity", () => { }); describe("Swapping", () => { - it("Swaps USDC for AppStable (1:1)", async () => { + it("Swaps USDC for CustomStable (1:1)", async () => { const swapAmount = new anchor.BN(100 * 10 ** 6); // 100 USDC - const minAmountOut = new anchor.BN(100 * 10 ** 6); // Expect 100 AppStable (0% fee) + const minAmountOut = new anchor.BN(100 * 10 ** 6); // Expect 100 CustomStable (0% fee) // Get initial balances const initialUserUsdcBalance = await getAccount( provider.connection, userUsdcAccount ); - const initialUserAppStableBalance = await getAccount( + const initialUserCustomStableBalance = await getAccount( provider.connection, - userAppStableAccount + userCustomStableAccount ); await program.methods @@ -359,15 +359,15 @@ describe("scaas-liquidity", () => { .accounts({ pool, inVault: usdcVault, - outVault: appStableVault, + outVault: customStableVault, inVaultTokenAccount: usdcVaultTokenAccount, - outVaultTokenAccount: appStableVaultTokenAccount, + outVaultTokenAccount: customStableVaultTokenAccount, userFromTokenAccount: userUsdcAccount, - toTokenAccount: userAppStableAccount, + toTokenAccount: userCustomStableAccount, feeRecipientTokenAccount: userUsdcAccount, // Fee collected in input token (USDC) feeRecipient: payer.publicKey, // Fee recipient authority fromMint: usdcMint, - toMint: appStableMint, + toMint: customStableMint, user: payer.publicKey, tokenProgram: TOKEN_PROGRAM_ID, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, @@ -381,23 +381,23 @@ describe("scaas-liquidity", () => { provider.connection, userUsdcAccount ); - const finalUserAppStableBalance = await getAccount( + const finalUserCustomStableBalance = await getAccount( provider.connection, - userAppStableAccount + userCustomStableAccount ); // Verify balances changed correctly (1:1 swap, 0% fee) const usdcDiff = initialUserUsdcBalance.amount - finalUserUsdcBalance.amount; - const appStableDiff = - finalUserAppStableBalance.amount - initialUserAppStableBalance.amount; + const customStableDiff = + finalUserCustomStableBalance.amount - initialUserCustomStableBalance.amount; assert.equal(usdcDiff.toString(), swapAmount.toString()); - assert.equal(appStableDiff.toString(), swapAmount.toString()); // 1:1 with 0% fee + assert.equal(customStableDiff.toString(), swapAmount.toString()); // 1:1 with 0% fee }); - it("Swaps AppStable for USDC (1:1)", async () => { - const swapAmount = new anchor.BN(50 * 10 ** 6); // 50 AppStable + it("Swaps CustomStable for USDC (1:1)", async () => { + const swapAmount = new anchor.BN(50 * 10 ** 6); // 50 CustomStable const minAmountOut = new anchor.BN(50 * 10 ** 6); // Expect 50 USDC (0% fee) // Get initial balances @@ -405,24 +405,24 @@ describe("scaas-liquidity", () => { provider.connection, userUsdcAccount ); - const initialUserAppStableBalance = await getAccount( + const initialUserCustomStableBalance = await getAccount( provider.connection, - userAppStableAccount + userCustomStableAccount ); await program.methods .swap(swapAmount, minAmountOut) .accounts({ pool, - inVault: appStableVault, + inVault: customStableVault, outVault: usdcVault, - inVaultTokenAccount: appStableVaultTokenAccount, + inVaultTokenAccount: customStableVaultTokenAccount, outVaultTokenAccount: usdcVaultTokenAccount, - userFromTokenAccount: userAppStableAccount, + userFromTokenAccount: userCustomStableAccount, toTokenAccount: userUsdcAccount, - feeRecipientTokenAccount: userAppStableAccount, // Fee collected in input token (AppStable) + feeRecipientTokenAccount: userCustomStableAccount, // Fee collected in input token (CustomStable) feeRecipient: payer.publicKey, // Fee recipient authority - fromMint: appStableMint, + fromMint: customStableMint, toMint: usdcMint, user: payer.publicKey, tokenProgram: TOKEN_PROGRAM_ID, @@ -437,18 +437,18 @@ describe("scaas-liquidity", () => { provider.connection, userUsdcAccount ); - const finalUserAppStableBalance = await getAccount( + const finalUserCustomStableBalance = await getAccount( provider.connection, - userAppStableAccount + userCustomStableAccount ); // Verify balances changed correctly (1:1 swap, 0% fee) const usdcDiff = finalUserUsdcBalance.amount - initialUserUsdcBalance.amount; - const appStableDiff = - initialUserAppStableBalance.amount - finalUserAppStableBalance.amount; + const customStableDiff = + initialUserCustomStableBalance.amount - finalUserCustomStableBalance.amount; - assert.equal(appStableDiff.toString(), swapAmount.toString()); + assert.equal(customStableDiff.toString(), swapAmount.toString()); assert.equal(usdcDiff.toString(), swapAmount.toString()); // 1:1 with 0% fee }); @@ -460,10 +460,10 @@ describe("scaas-liquidity", () => { usdcMint, swapper.publicKey ); - const swapperAppStableAccount = await createAccount( + const swapperCustomStableAccount = await createAccount( provider.connection, payer.payer, - appStableMint, + customStableMint, swapper.publicKey ); @@ -480,7 +480,7 @@ describe("scaas-liquidity", () => { const minAmountOut = new anchor.BN(10 * 10 ** 6); const beforeBalance = await getAccount( provider.connection, - swapperAppStableAccount + swapperCustomStableAccount ); await program.methods @@ -488,15 +488,15 @@ describe("scaas-liquidity", () => { .accounts({ pool, inVault: usdcVault, - outVault: appStableVault, + outVault: customStableVault, inVaultTokenAccount: usdcVaultTokenAccount, - outVaultTokenAccount: appStableVaultTokenAccount, + outVaultTokenAccount: customStableVaultTokenAccount, userFromTokenAccount: swapperUsdcAccount, - toTokenAccount: swapperAppStableAccount, + toTokenAccount: swapperCustomStableAccount, feeRecipientTokenAccount: feeRecipientUsdcAccount, feeRecipient: payer.publicKey, fromMint: usdcMint, - toMint: appStableMint, + toMint: customStableMint, user: swapper.publicKey, tokenProgram: TOKEN_PROGRAM_ID, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, @@ -507,7 +507,7 @@ describe("scaas-liquidity", () => { const afterBalance = await getAccount( provider.connection, - swapperAppStableAccount + swapperCustomStableAccount ); assert.equal( (afterBalance.amount - beforeBalance.amount).toString(), @@ -527,15 +527,15 @@ describe("scaas-liquidity", () => { .accounts({ pool, inVault: usdcVault, - outVault: appStableVault, + outVault: customStableVault, inVaultTokenAccount: usdcVaultTokenAccount, - outVaultTokenAccount: appStableVaultTokenAccount, + outVaultTokenAccount: customStableVaultTokenAccount, userFromTokenAccount: userUsdcAccount, - toTokenAccount: userAppStableAccount, + toTokenAccount: userCustomStableAccount, feeRecipientTokenAccount: feeRecipientUsdcAccount, feeRecipient: payer.publicKey, fromMint: usdcMint, - toMint: appStableMint, + toMint: customStableMint, user: unauthorizedUser.publicKey, tokenProgram: TOKEN_PROGRAM_ID, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, @@ -567,10 +567,10 @@ describe("scaas-liquidity", () => { usdcMint, owner.publicKey ); - const delegateAppStableAccount = await createAccount( + const delegateCustomStableAccount = await createAccount( provider.connection, payer.payer, - appStableMint, + customStableMint, delegate.publicKey ); @@ -594,7 +594,7 @@ describe("scaas-liquidity", () => { const beforeBalance = await getAccount( provider.connection, - delegateAppStableAccount + delegateCustomStableAccount ); await program.methods @@ -602,15 +602,15 @@ describe("scaas-liquidity", () => { .accounts({ pool, inVault: usdcVault, - outVault: appStableVault, + outVault: customStableVault, inVaultTokenAccount: usdcVaultTokenAccount, - outVaultTokenAccount: appStableVaultTokenAccount, + outVaultTokenAccount: customStableVaultTokenAccount, userFromTokenAccount: ownerUsdcAccount, - toTokenAccount: delegateAppStableAccount, + toTokenAccount: delegateCustomStableAccount, feeRecipientTokenAccount: feeRecipientUsdcAccount, feeRecipient: payer.publicKey, fromMint: usdcMint, - toMint: appStableMint, + toMint: customStableMint, user: delegate.publicKey, tokenProgram: TOKEN_PROGRAM_ID, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, @@ -621,7 +621,7 @@ describe("scaas-liquidity", () => { const afterBalance = await getAccount( provider.connection, - delegateAppStableAccount + delegateCustomStableAccount ); assert.equal( (afterBalance.amount - beforeBalance.amount).toString(), @@ -632,7 +632,7 @@ describe("scaas-liquidity", () => { it("Allows swapping exactly the full destination vault balance", async () => { const destinationVaultBefore = await getAccount( provider.connection, - appStableVaultTokenAccount + customStableVaultTokenAccount ); const fullDrainAmount = new anchor.BN( destinationVaultBefore.amount.toString() @@ -652,15 +652,15 @@ describe("scaas-liquidity", () => { .accounts({ pool, inVault: usdcVault, - outVault: appStableVault, + outVault: customStableVault, inVaultTokenAccount: usdcVaultTokenAccount, - outVaultTokenAccount: appStableVaultTokenAccount, + outVaultTokenAccount: customStableVaultTokenAccount, userFromTokenAccount: userUsdcAccount, - toTokenAccount: userAppStableAccount, + toTokenAccount: userCustomStableAccount, feeRecipientTokenAccount: feeRecipientUsdcAccount, feeRecipient: payer.publicKey, fromMint: usdcMint, - toMint: appStableMint, + toMint: customStableMint, user: payer.publicKey, tokenProgram: TOKEN_PROGRAM_ID, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, @@ -671,15 +671,15 @@ describe("scaas-liquidity", () => { const drainedVault = await getAccount( provider.connection, - appStableVaultTokenAccount + customStableVaultTokenAccount ); assert.equal(drainedVault.amount.toString(), "0"); await transfer( provider.connection, payer.payer, - userAppStableAccount, - appStableVaultTokenAccount, + userCustomStableAccount, + customStableVaultTokenAccount, payer.payer, BigInt(fullDrainAmount.toString()) ); @@ -695,15 +695,15 @@ describe("scaas-liquidity", () => { .accounts({ pool, inVault: usdcVault, - outVault: appStableVault, + outVault: customStableVault, inVaultTokenAccount: usdcVaultTokenAccount, - outVaultTokenAccount: appStableVaultTokenAccount, + outVaultTokenAccount: customStableVaultTokenAccount, userFromTokenAccount: userUsdcAccount, - toTokenAccount: userAppStableAccount, + toTokenAccount: userCustomStableAccount, feeRecipientTokenAccount: userUsdcAccount, feeRecipient: payer.publicKey, fromMint: usdcMint, - toMint: appStableMint, + toMint: customStableMint, user: payer.publicKey, tokenProgram: TOKEN_PROGRAM_ID, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, @@ -740,15 +740,15 @@ describe("scaas-liquidity", () => { .accounts({ pool, inVault: usdcVault, - outVault: appStableVault, + outVault: customStableVault, inVaultTokenAccount: usdcVaultTokenAccount, - outVaultTokenAccount: appStableVaultTokenAccount, + outVaultTokenAccount: customStableVaultTokenAccount, userFromTokenAccount: userUsdcAccount, - toTokenAccount: userAppStableAccount, + toTokenAccount: userCustomStableAccount, feeRecipientTokenAccount: userUsdcAccount, feeRecipient: payer.publicKey, fromMint: usdcMint, - toMint: appStableMint, + toMint: customStableMint, user: payer.publicKey, tokenProgram: TOKEN_PROGRAM_ID, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, @@ -797,15 +797,15 @@ describe("scaas-liquidity", () => { .accounts({ pool, inVault: usdcVault, - outVault: appStableVault, + outVault: customStableVault, inVaultTokenAccount: usdcVaultTokenAccount, - outVaultTokenAccount: appStableVaultTokenAccount, + outVaultTokenAccount: customStableVaultTokenAccount, userFromTokenAccount: userUsdcAccount, - toTokenAccount: userAppStableAccount, + toTokenAccount: userCustomStableAccount, feeRecipientTokenAccount: userUsdcAccount, feeRecipient: payer.publicKey, fromMint: usdcMint, - toMint: appStableMint, + toMint: customStableMint, user: payer.publicKey, tokenProgram: TOKEN_PROGRAM_ID, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, @@ -851,7 +851,7 @@ describe("scaas-liquidity", () => { const vaultAccount = await program.account.tokenVault.fetch(usdcVault); assert.equal(vaultAccount.disabled, true, "Vault should be disabled"); - // Try to swap USDC for AppStable (should fail) + // Try to swap USDC for CustomStable (should fail) const swapAmount = new anchor.BN(10 * 10 ** 6); const minAmountOut = new anchor.BN(10 * 10 ** 6); @@ -861,15 +861,15 @@ describe("scaas-liquidity", () => { .accounts({ pool, inVault: usdcVault, - outVault: appStableVault, + outVault: customStableVault, inVaultTokenAccount: usdcVaultTokenAccount, - outVaultTokenAccount: appStableVaultTokenAccount, + outVaultTokenAccount: customStableVaultTokenAccount, userFromTokenAccount: userUsdcAccount, - toTokenAccount: userAppStableAccount, + toTokenAccount: userCustomStableAccount, feeRecipientTokenAccount: userUsdcAccount, feeRecipient: payer.publicKey, fromMint: usdcMint, - toMint: appStableMint, + toMint: customStableMint, user: payer.publicKey, tokenProgram: TOKEN_PROGRAM_ID, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, @@ -885,7 +885,7 @@ describe("scaas-liquidity", () => { }); it("Prevents swaps when output token is disabled", async () => { - // Try to swap AppStable for USDC (USDC is disabled from previous test) + // Try to swap CustomStable for USDC (USDC is disabled from previous test) const swapAmount = new anchor.BN(10 * 10 ** 6); const minAmountOut = new anchor.BN(10 * 10 ** 6); @@ -894,15 +894,15 @@ describe("scaas-liquidity", () => { .swap(swapAmount, minAmountOut) .accounts({ pool, - inVault: appStableVault, + inVault: customStableVault, outVault: usdcVault, - inVaultTokenAccount: appStableVaultTokenAccount, + inVaultTokenAccount: customStableVaultTokenAccount, outVaultTokenAccount: usdcVaultTokenAccount, - userFromTokenAccount: userAppStableAccount, + userFromTokenAccount: userCustomStableAccount, toTokenAccount: userUsdcAccount, - feeRecipientTokenAccount: userAppStableAccount, + feeRecipientTokenAccount: userCustomStableAccount, feeRecipient: payer.publicKey, - fromMint: appStableMint, + fromMint: customStableMint, toMint: usdcMint, user: payer.publicKey, tokenProgram: TOKEN_PROGRAM_ID, @@ -944,15 +944,15 @@ describe("scaas-liquidity", () => { .accounts({ pool, inVault: usdcVault, - outVault: appStableVault, + outVault: customStableVault, inVaultTokenAccount: usdcVaultTokenAccount, - outVaultTokenAccount: appStableVaultTokenAccount, + outVaultTokenAccount: customStableVaultTokenAccount, userFromTokenAccount: userUsdcAccount, - toTokenAccount: userAppStableAccount, + toTokenAccount: userCustomStableAccount, feeRecipientTokenAccount: userUsdcAccount, feeRecipient: payer.publicKey, fromMint: usdcMint, - toMint: appStableMint, + toMint: customStableMint, user: payer.publicKey, tokenProgram: TOKEN_PROGRAM_ID, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, @@ -1379,15 +1379,15 @@ describe("scaas-liquidity", () => { .accounts({ pool, inVault: usdcVault, - outVault: appStableVault, + outVault: customStableVault, inVaultTokenAccount: usdcVaultTokenAccount, - outVaultTokenAccount: appStableVaultTokenAccount, + outVaultTokenAccount: customStableVaultTokenAccount, userFromTokenAccount: userUsdcAccount, - toTokenAccount: userAppStableAccount, + toTokenAccount: userCustomStableAccount, feeRecipientTokenAccount: userUsdcAccount, feeRecipient: payer.publicKey, fromMint: usdcMint, - toMint: appStableMint, + toMint: customStableMint, user: payer.publicKey, tokenProgram: TOKEN_PROGRAM_ID, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, @@ -1435,15 +1435,15 @@ describe("scaas-liquidity", () => { .swap(excessiveAmount, minAmountOut) .accounts({ pool, - inVault: appStableVault, + inVault: customStableVault, outVault: usdcVault, - inVaultTokenAccount: appStableVaultTokenAccount, + inVaultTokenAccount: customStableVaultTokenAccount, outVaultTokenAccount: usdcVaultTokenAccount, - userFromTokenAccount: userAppStableAccount, + userFromTokenAccount: userCustomStableAccount, toTokenAccount: userUsdcAccount, - feeRecipientTokenAccount: userAppStableAccount, + feeRecipientTokenAccount: userCustomStableAccount, feeRecipient: payer.publicKey, - fromMint: appStableMint, + fromMint: customStableMint, toMint: usdcMint, user: payer.publicKey, tokenProgram: TOKEN_PROGRAM_ID, @@ -1491,9 +1491,9 @@ describe("scaas-liquidity", () => { provider.connection, userUsdcAccount ); - const initialUserAppStableBalance = await getAccount( + const initialUserCustomStableBalance = await getAccount( provider.connection, - userAppStableAccount + userCustomStableAccount ); const initialVaultUsdcBalance = await getAccount( provider.connection, @@ -1509,15 +1509,15 @@ describe("scaas-liquidity", () => { .accounts({ pool, inVault: usdcVault, - outVault: appStableVault, + outVault: customStableVault, inVaultTokenAccount: usdcVaultTokenAccount, - outVaultTokenAccount: appStableVaultTokenAccount, + outVaultTokenAccount: customStableVaultTokenAccount, userFromTokenAccount: userUsdcAccount, - toTokenAccount: userAppStableAccount, + toTokenAccount: userCustomStableAccount, feeRecipientTokenAccount: feeRecipientUsdcAccount, feeRecipient: feeRecipient.publicKey, fromMint: usdcMint, - toMint: appStableMint, + toMint: customStableMint, user: payer.publicKey, tokenProgram: TOKEN_PROGRAM_ID, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, @@ -1531,9 +1531,9 @@ describe("scaas-liquidity", () => { provider.connection, userUsdcAccount ); - const finalUserAppStableBalance = await getAccount( + const finalUserCustomStableBalance = await getAccount( provider.connection, - userAppStableAccount + userCustomStableAccount ); const finalVaultUsdcBalance = await getAccount( provider.connection, @@ -1554,10 +1554,10 @@ describe("scaas-liquidity", () => { ); // Verify user received net amount (after fee deduction) - const userAppStableReceived = - finalUserAppStableBalance.amount - initialUserAppStableBalance.amount; + const userCustomStableReceived = + finalUserCustomStableBalance.amount - initialUserCustomStableBalance.amount; assert.equal( - userAppStableReceived.toString(), + userCustomStableReceived.toString(), expectedNetAmount.toString(), "User should receive net amount after fees" ); @@ -1628,15 +1628,15 @@ describe("scaas-liquidity", () => { .accounts({ pool, inVault: usdcVault, - outVault: appStableVault, + outVault: customStableVault, inVaultTokenAccount: usdcVaultTokenAccount, - outVaultTokenAccount: appStableVaultTokenAccount, + outVaultTokenAccount: customStableVaultTokenAccount, userFromTokenAccount: userUsdcAccount, - toTokenAccount: userAppStableAccount, + toTokenAccount: userCustomStableAccount, feeRecipientTokenAccount: newFeeRecipientUsdcAccount, // New fee recipient feeRecipient: newFeeRecipient.publicKey, fromMint: usdcMint, - toMint: appStableMint, + toMint: customStableMint, user: payer.publicKey, tokenProgram: TOKEN_PROGRAM_ID, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, @@ -1680,9 +1680,9 @@ describe("scaas-liquidity", () => { provider.connection, userUsdcAccount ); - const initialUserAppStableBalance = await getAccount( + const initialUserCustomStableBalance = await getAccount( provider.connection, - userAppStableAccount + userCustomStableAccount ); await program.methods @@ -1690,15 +1690,15 @@ describe("scaas-liquidity", () => { .accounts({ pool, inVault: usdcVault, - outVault: appStableVault, + outVault: customStableVault, inVaultTokenAccount: usdcVaultTokenAccount, - outVaultTokenAccount: appStableVaultTokenAccount, + outVaultTokenAccount: customStableVaultTokenAccount, userFromTokenAccount: userUsdcAccount, - toTokenAccount: userAppStableAccount, + toTokenAccount: userCustomStableAccount, feeRecipientTokenAccount: userUsdcAccount, feeRecipient: payer.publicKey, fromMint: usdcMint, - toMint: appStableMint, + toMint: customStableMint, user: payer.publicKey, tokenProgram: TOKEN_PROGRAM_ID, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, @@ -1712,16 +1712,16 @@ describe("scaas-liquidity", () => { provider.connection, userUsdcAccount ); - const finalUserAppStableBalance = await getAccount( + const finalUserCustomStableBalance = await getAccount( provider.connection, - userAppStableAccount + userCustomStableAccount ); // Verify 1:1 swap with no fees const usdcSpent = initialUserUsdcBalance.amount - finalUserUsdcBalance.amount; - const appStableReceived = - finalUserAppStableBalance.amount - initialUserAppStableBalance.amount; + const customStableReceived = + finalUserCustomStableBalance.amount - initialUserCustomStableBalance.amount; assert.equal( usdcSpent.toString(), @@ -1729,7 +1729,7 @@ describe("scaas-liquidity", () => { "Should spend exact swap amount" ); assert.equal( - appStableReceived.toString(), + customStableReceived.toString(), swapAmount.toString(), "Should receive exact swap amount (1:1, no fees)" ); @@ -1784,15 +1784,15 @@ describe("scaas-liquidity", () => { .accounts({ pool, inVault: usdcVault, - outVault: appStableVault, + outVault: customStableVault, inVaultTokenAccount: usdcVaultTokenAccount, - outVaultTokenAccount: appStableVaultTokenAccount, + outVaultTokenAccount: customStableVaultTokenAccount, userFromTokenAccount: userUsdcAccount, - toTokenAccount: userAppStableAccount, + toTokenAccount: userCustomStableAccount, feeRecipientTokenAccount: feeRecipientUsdcAccountForTest, feeRecipient: feeRecipient.publicKey, fromMint: usdcMint, - toMint: appStableMint, + toMint: customStableMint, user: payer.publicKey, tokenProgram: TOKEN_PROGRAM_ID, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, @@ -1832,15 +1832,15 @@ describe("scaas-liquidity", () => { .accounts({ pool, inVault: usdcVault, - outVault: appStableVault, + outVault: customStableVault, inVaultTokenAccount: usdcVaultTokenAccount, - outVaultTokenAccount: appStableVaultTokenAccount, + outVaultTokenAccount: customStableVaultTokenAccount, userFromTokenAccount: userUsdcAccount, - toTokenAccount: userAppStableAccount, + toTokenAccount: userCustomStableAccount, feeRecipientTokenAccount: feeRecipientUsdcAccountForTest, feeRecipient: feeRecipient.publicKey, fromMint: usdcMint, - toMint: appStableMint, + toMint: customStableMint, user: payer.publicKey, tokenProgram: TOKEN_PROGRAM_ID, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, @@ -1918,15 +1918,15 @@ describe("scaas-liquidity", () => { .accounts({ pool, inVault: usdcVault, - outVault: appStableVault, + outVault: customStableVault, inVaultTokenAccount: usdcVaultTokenAccount, - outVaultTokenAccount: appStableVaultTokenAccount, + outVaultTokenAccount: customStableVaultTokenAccount, userFromTokenAccount: userUsdcAccount, - toTokenAccount: userAppStableAccount, + toTokenAccount: userCustomStableAccount, feeRecipientTokenAccount: feeRecipientUsdcAccountForTest, feeRecipient: feeRecipient.publicKey, fromMint: usdcMint, - toMint: appStableMint, + toMint: customStableMint, user: payer.publicKey, tokenProgram: TOKEN_PROGRAM_ID, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, @@ -2358,15 +2358,15 @@ describe("scaas-liquidity", () => { it("Swaps with same decimals (6 to 6) work", async () => { // This tests backward compatibility - swaps between tokens with same decimals const swapAmount = new anchor.BN(50 * 10 ** 6); // 50 USDC - const minAmountOut = new anchor.BN(50 * 10 ** 6); // Expect 50 AppStable + const minAmountOut = new anchor.BN(50 * 10 ** 6); // Expect 50 CustomStable const userUsdcBefore = await getAccount( provider.connection, userUsdcAccount ); - const userAppStableBefore = await getAccount( + const userCustomStableBefore = await getAccount( provider.connection, - userAppStableAccount + userCustomStableAccount ); await program.methods @@ -2374,15 +2374,15 @@ describe("scaas-liquidity", () => { .accounts({ pool, inVault: usdcVault, - outVault: appStableVault, + outVault: customStableVault, inVaultTokenAccount: usdcVaultTokenAccount, - outVaultTokenAccount: appStableVaultTokenAccount, + outVaultTokenAccount: customStableVaultTokenAccount, userFromTokenAccount: userUsdcAccount, - toTokenAccount: userAppStableAccount, + toTokenAccount: userCustomStableAccount, feeRecipient: payer.publicKey, feeRecipientTokenAccount: userUsdcAccount, fromMint: usdcMint, - toMint: appStableMint, + toMint: customStableMint, user: payer.publicKey, tokenProgram: TOKEN_PROGRAM_ID, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, @@ -2395,9 +2395,9 @@ describe("scaas-liquidity", () => { provider.connection, userUsdcAccount ); - const userAppStableAfter = await getAccount( + const userCustomStableAfter = await getAccount( provider.connection, - userAppStableAccount + userCustomStableAccount ); // Should still be 1:1 when decimals are the same @@ -2407,9 +2407,9 @@ describe("scaas-liquidity", () => { "USDC deducted incorrectly" ); assert.equal( - userAppStableAfter.amount - userAppStableBefore.amount, + userCustomStableAfter.amount - userCustomStableBefore.amount, BigInt(50 * 10 ** 6), - "AppStable received incorrectly" + "CustomStable received incorrectly" ); }); }); From 8f7df20ec9bfa917cdcf0d6db837a279a41f0110 Mon Sep 17 00:00:00 2001 From: Braden Thompson Date: Wed, 13 May 2026 18:22:10 -0600 Subject: [PATCH 3/6] cleanup --- CONTRIBUTING.md | 2 +- README.md | 9 ++++++--- evm/README.md | 9 ++++++++- solana/README.md | 20 +++++++++++--------- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4551160..da3fc1c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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 diff --git a/README.md b/README.md index 0447f41..1ec3c6b 100644 --- a/README.md +++ b/README.md @@ -8,13 +8,16 @@ StableSwapper enables swapping between stablecoins at a 1:1 ratio, automatically ### Features +Shared across both implementations: + - **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 +Implementation-specific features (see each README for details): + ## Implementations | Chain | Directory | Details | diff --git a/evm/README.md b/evm/README.md index 41054c0..2635d22 100644 --- a/evm/README.md +++ b/evm/README.md @@ -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 diff --git a/solana/README.md b/solana/README.md index 5d21b97..f1b46f5 100644 --- a/solana/README.md +++ b/solana/README.md @@ -4,13 +4,13 @@ Solana / Anchor implementation of StableSwapper: a 1:1 stablecoin swap program t ## Features -- **1:1 Token Swaps** -- Fixed-rate swapping between any listed stablecoins +- **1:1 Token Swaps** -- Fixed-rate swapping between any listed stablecoins, with automatic decimal normalization - **Dual Authority Model** -- Separate operations and pause authorities, both compatible with multisigs - **Slippage Protection** -- User-supplied minimum output amount per swap - **Granular Pause Controls** -- Independent pause flags for swaps and liquidity management -- **Configurable Fees** -- Admin-controlled fee rate (0--10%) with a dedicated fee recipient -- **Multi-token Support** -- Add up to 50 supported tokens, each with its own vault -- **Access Controls** -- Authority validation enforced via PDAs +- **Configurable Fees** -- Admin-controlled fee rate (0--10%, capped at 1000 basis points) with a dedicated fee recipient +- **Multi-token Support** -- Add up to 50 supported tokens, each with its own vault. Token mints must have between 6 and 9 decimals. +- **Access Controls** -- Authority validation enforced via `has_one` constraints; the pool itself is a PDA ## Project Structure @@ -152,14 +152,16 @@ Example: swap 100 USDC → custom stablecoin with a 1% fee: ### Core instructions -- **`initialize_pool`** — Creates the pool with operations + pause authorities and fee configuration -- **`add_supported_token`** — Adds a token with its dedicated vault (operations authority) +- **`initialize`** — Creates the pool with operations + pause authorities and fee configuration +- **`add_supported_token`** — Adds a token with its dedicated vault (operations authority). Mints with fewer than 6 or more than 9 decimals are rejected. +- **`remove_supported_token`** — Removes a token from the pool, closes its vault and vault token account, and refunds rent to the operations authority. Requires the token to be disabled and the vault to be empty. - **`swap`** — Executes a 1:1 swap with slippage protection (`min_amount_out`) - **`withdraw_liquidity`** — Removes liquidity from a vault (operations authority, gated by `liquidity_paused`) -- **`update_fee_config`** — Updates the fee rate and recipient (operations authority) +- **`update_fee_config`** — Updates the fee rate and/or recipient (operations authority) - **`update_pause_config`** — Controls `swaps_paused` and `liquidity_paused` (pause authority) -- **`update_operations_authority`** — Operations authority updates itself -- **`update_pause_authority`** — Pause authority updates itself +- **`update_token_status`** — Disables or enables a specific token for swaps (pause authority) +- **`update_operations_authority`** — Operations authority rotates itself +- **`update_pause_authority`** — Pause authority rotates itself Liquidity is seeded by sending tokens directly to the vault token account via an SPL Token transfer; there is no dedicated deposit instruction. From c6df72d20a3f2ff09569af89004d1baf720f8f8b Mon Sep 17 00:00:00 2001 From: Braden Thompson Date: Wed, 13 May 2026 18:35:46 -0600 Subject: [PATCH 4/6] cleanup --- .github/workflows/test.yml | 9 ++++++--- solana/README.md | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d96c8d2..59f4499 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -43,7 +43,7 @@ jobs: name: Solana (Anchor) runs-on: ubuntu-latest env: - SOLANA_VERSION: 2.2.21 + SOLANA_VERSION: 3.0.13 ANCHOR_VERSION: 0.31.1 steps: - name: Harden the runner (Audit all outbound calls) @@ -58,8 +58,11 @@ jobs: - name: Install Solana CLI run: | - sh -c "$(curl -sSfL https://release.anza.xyz/v${SOLANA_VERSION}/install)" - echo "$HOME/.local/share/solana/install/active_release/bin" >> "$GITHUB_PATH" + 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: | diff --git a/solana/README.md b/solana/README.md index f1b46f5..a7d20cc 100644 --- a/solana/README.md +++ b/solana/README.md @@ -31,7 +31,7 @@ solana/ ## Prerequisites - **Rust** 1.70+ -- **Solana CLI** 2.2.21+ +- **Solana CLI** (Agave) 3.0+ - **Anchor CLI** 0.31.1+ - **Node.js** 18+ with Yarn From d96e09d438a95e73785e119cf74c2499e7a16882 Mon Sep 17 00:00:00 2001 From: Braden Thompson Date: Thu, 14 May 2026 13:15:12 -0600 Subject: [PATCH 5/6] update --- .github/workflows/test.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 59f4499..d8e653c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -81,9 +81,18 @@ jobs: run: yarn install --frozen-lockfile working-directory: solana + - 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 From 9dbd98205c99aeefe75aa0a0346d0835c08d66da Mon Sep 17 00:00:00 2001 From: Braden Thompson Date: Fri, 15 May 2026 16:41:10 -0600 Subject: [PATCH 6/6] address review comments --- .github/workflows/test.yml | 16 ++++++++++++---- README.md | 4 ---- solana/tests/stable-swapper.ts | 18 +++++++++++++----- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d8e653c..e4c8e01 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -56,6 +56,18 @@ jobs: - 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 @@ -77,10 +89,6 @@ jobs: - name: Verify Anchor CLI run: anchor --version - - name: Install JS dependencies - run: yarn install --frozen-lockfile - working-directory: solana - - name: Generate wallet keypair run: | set -euo pipefail diff --git a/README.md b/README.md index 1ec3c6b..fadc4e9 100644 --- a/README.md +++ b/README.md @@ -8,16 +8,12 @@ StableSwapper enables swapping between stablecoins at a 1:1 ratio, automatically ### Features -Shared across both implementations: - - **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 authorities with separated concerns - **Pause Controls** -- Independent toggles for swap and liquidity operations - **Slippage Protection** -- Users specify a minimum output amount per swap -Implementation-specific features (see each README for details): - ## Implementations | Chain | Directory | Details | diff --git a/solana/tests/stable-swapper.ts b/solana/tests/stable-swapper.ts index f036215..d9decc6 100644 --- a/solana/tests/stable-swapper.ts +++ b/solana/tests/stable-swapper.ts @@ -71,7 +71,11 @@ describe("stable-swapper", () => { ); [customStableVault] = PublicKey.findProgramAddressSync( - [Buffer.from("token_vault"), pool.toBuffer(), customStableMint.toBuffer()], + [ + Buffer.from("token_vault"), + pool.toBuffer(), + customStableMint.toBuffer(), + ], program.programId ); @@ -390,7 +394,8 @@ describe("stable-swapper", () => { const usdcDiff = initialUserUsdcBalance.amount - finalUserUsdcBalance.amount; const customStableDiff = - finalUserCustomStableBalance.amount - initialUserCustomStableBalance.amount; + finalUserCustomStableBalance.amount - + initialUserCustomStableBalance.amount; assert.equal(usdcDiff.toString(), swapAmount.toString()); assert.equal(customStableDiff.toString(), swapAmount.toString()); // 1:1 with 0% fee @@ -446,7 +451,8 @@ describe("stable-swapper", () => { const usdcDiff = finalUserUsdcBalance.amount - initialUserUsdcBalance.amount; const customStableDiff = - initialUserCustomStableBalance.amount - finalUserCustomStableBalance.amount; + initialUserCustomStableBalance.amount - + finalUserCustomStableBalance.amount; assert.equal(customStableDiff.toString(), swapAmount.toString()); assert.equal(usdcDiff.toString(), swapAmount.toString()); // 1:1 with 0% fee @@ -1555,7 +1561,8 @@ describe("stable-swapper", () => { // Verify user received net amount (after fee deduction) const userCustomStableReceived = - finalUserCustomStableBalance.amount - initialUserCustomStableBalance.amount; + finalUserCustomStableBalance.amount - + initialUserCustomStableBalance.amount; assert.equal( userCustomStableReceived.toString(), expectedNetAmount.toString(), @@ -1721,7 +1728,8 @@ describe("stable-swapper", () => { const usdcSpent = initialUserUsdcBalance.amount - finalUserUsdcBalance.amount; const customStableReceived = - finalUserCustomStableBalance.amount - initialUserCustomStableBalance.amount; + finalUserCustomStableBalance.amount - + initialUserCustomStableBalance.amount; assert.equal( usdcSpent.toString(),