Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 60 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,38 @@ jobs:
working-directory: solana
run: cargo fmt --all -- --check

# Guards against the committed source silently building for the wrong network. `declare_id!`
# is baked into the binary and checked at runtime against the deployment address, so a
# mainnet upgrade built from a source whose `declare_id` is not the mainnet program ID would
# reject every instruction with DeclaredProgramIdMismatch. The committed value must be the
# mainnet ID; the ephemeral-align step below rewrites it for localnet tests, and a devnet
# rehearsal patches it to [programs.devnet] (see solana/README.md).
- name: Verify declare_id matches the mainnet program ID
working-directory: solana
run: |
set -euo pipefail
DECLARED=$(sed -nE 's/^declare_id!\("([^"]+)"\).*/\1/p' \
programs/stable-swapper/src/lib.rs)
MAINNET=$(awk '
/^\[programs\.mainnet\]/ { f = 1; next }
/^\[/ { f = 0 }
f && /^stable_swapper/ {
gsub(/["[:space:]]/, "", $0); sub(/stable_swapper=/, "", $0); print; exit
}
' Anchor.toml)
if [ -z "$DECLARED" ] || [ -z "$MAINNET" ]; then
echo "Could not read declare_id ('$DECLARED') or [programs.mainnet] ('$MAINNET')" >&2
exit 1
fi
if [ "$DECLARED" != "$MAINNET" ]; then
echo "declare_id ($DECLARED) != [programs.mainnet] ($MAINNET) in Anchor.toml." >&2
echo "A mainnet build from this source would deploy the wrong program ID and brick" >&2
echo "with DeclaredProgramIdMismatch on every instruction. Set declare_id to the" >&2
echo "mainnet program ID before merging." >&2
exit 1
fi
echo "declare_id matches [programs.mainnet]: $DECLARED"

- name: Install JS dependencies
run: yarn install --frozen-lockfile
working-directory: solana
Expand Down Expand Up @@ -141,4 +173,31 @@ jobs:

- name: Run Anchor tests
working-directory: solana
run: anchor test --provider.cluster localnet --skip-build
run: |
set -euo pipefail

WALLET="$(solana address)"

# `anchor test`'s built-in deploy leaves the program's upgrade authority set to a key
# other than the provider wallet, which trips initialize's NotUpgradeAuthority guard.
# Load the program into a validator we control -- upgradeable, with the provider wallet
# as the upgrade authority -- and have anchor run only the test script against it.
solana-test-validator --reset --quiet \
--upgradeable-program target/deploy/stable_swapper-keypair.json \
target/deploy/stable_swapper.so "$WALLET" &
VALIDATOR_PID=$!
trap 'kill "$VALIDATOR_PID" 2>/dev/null || true' EXIT

# Wait for the validator RPC to accept requests before deploying/airdropping.
for _ in $(seq 1 60); do
if solana cluster-version --url http://127.0.0.1:8899 >/dev/null 2>&1; then
break
fi
sleep 1
done

# Fund the provider wallet so it can pay for test transactions.
solana airdrop 100 "$WALLET" --url http://127.0.0.1:8899 >/dev/null

anchor test --provider.cluster localnet \
--skip-build --skip-deploy --skip-local-validator
Binary file not shown.
1 change: 1 addition & 0 deletions solana/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ test-ledger
**/*-keypair.json
**/id.json
package-lock.json

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

is dropping package-lock intentional?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added back

.migration-verify
296 changes: 168 additions & 128 deletions solana/README.md

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions solana/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
"@types/chai": "^4.3.0",
"@types/mocha": "^9.0.0",
"@types/node": "^25.0.3",
"anchor-bankrun": "^0.5.0",
"chai": "^4.3.4",
"mocha": "^9.0.3",
"prettier": "^2.6.2",
"solana-bankrun": "^0.4.0",
"ts-mocha": "^10.0.0",
"ts-node": "^10.9.2",
"typescript": "^5.9.3"
Expand Down
5 changes: 5 additions & 0 deletions solana/programs/stable-swapper/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ pub const MAX_FEE_RATE: u64 = 1000;
/// Maximum number of supported tokens per pool
pub const MAX_SUPPORTED_TOKENS: usize = 50;

/// Maximum number of allowlisted withdraw recipients per pool.
/// The treasury authority may only withdraw to a token account owned by one of
/// these addresses; only the cold-key configure authority can add or remove them.
pub const MAX_WITHDRAW_RECIPIENTS: usize = 10;

/// Minimum allowed token decimals
pub const MIN_TOKEN_DECIMALS: u8 = 6;

Expand Down
28 changes: 26 additions & 2 deletions solana/programs/stable-swapper/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use anchor_lang::prelude::*;
pub enum LiquidityError {
#[msg("Swaps are paused")]
SwapsPaused,
#[msg("Liquidity management is paused")]
LiquidityPaused,
#[msg("Withdrawals are paused")]
WithdrawalPaused,
#[msg("Invalid amount")]
InvalidAmount,
#[msg("Token not supported")]
Expand Down Expand Up @@ -48,4 +48,28 @@ pub enum LiquidityError {
TokenMustBeDisabled,
#[msg("Vault must be empty before removing token")]
VaultNotEmpty,
#[msg("Pool has already been migrated to the role-based authority layout")]
AlreadyMigrated,
#[msg("Recipient key must not be the default pubkey")]
RecipientNotSet,
#[msg("Withdraw recipient is not on the allowlist")]
WithdrawRecipientNotAllowed,
#[msg("Withdraw recipient is already on the allowlist")]
WithdrawRecipientAlreadyAllowed,
#[msg("Maximum number of withdraw recipients reached")]
MaxWithdrawRecipientsReached,
#[msg("Legacy pool data length does not match the expected pre-migration size")]
LegacySizeMismatch,
Comment thread
OliverCai0 marked this conversation as resolved.
#[msg("Legacy pool discriminator does not match LiquidityPool")]
LegacyDiscriminatorMismatch,
#[msg("Legacy supported_tokens length is invalid")]
LegacyVecLengthInvalid,
#[msg("Failed to serialize the new LiquidityPool layout during migration")]
MigrationSerializeFailed,
#[msg("Authority key must not be the default pubkey")]
AuthorityNotSet,
#[msg("Program data account does not belong to this program")]
InvalidProgramData,
#[msg("Payer is not the program upgrade authority")]
NotUpgradeAuthority,
}
Loading
Loading