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
2 changes: 1 addition & 1 deletion .github/workflows/ci-pre-commit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
sudo apt-get install -y libudev-dev pkg-config
- name: Install Anchor CLI
run: |
cargo install --git https://github.com/coral-xyz/anchor avm
cargo install --locked --git https://github.com/coral-xyz/anchor avm
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.

needed to lock to the local lockfile to prevent issues with our toolchain rust version

avm install 0.31.0
avm use 0.31.0
echo "$HOME/.avm/bin" >> $GITHUB_PATH
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-and-release-svm-contract.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- uses: actions/checkout@v2
- name: Install Solana Verify CLI
run: |
cargo install solana-verify --git https://github.com/Ellipsis-Labs/solana-verifiable-build --rev 5ff03e0
cargo install --locked solana-verify --git https://github.com/solana-foundation/solana-verifiable-build --rev 5ff03e0
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.

solana-verifiable-build seems to have moved to the solana-foundation org

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚩 CI: --rev 5ff03e0 kept the same after changing git repo URL

In .github/workflows/test-and-release-svm-contract.yml:26, the git URL changed from Ellipsis-Labs/solana-verifiable-build to solana-foundation/solana-verifiable-build but the --rev 5ff03e0 was kept unchanged. This is valid if the solana-foundation repo is a transfer/fork of the original (same git history preserves commit hashes). However, if the repo is a new/separate repository, this commit hash may not exist and CI would fail. Worth confirming the repo transfer preserved history.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

- name: Build
run: solana-verify build
- name: Run tests
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion contracts/svm/Cargo.lock

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

2 changes: 1 addition & 1 deletion contracts/svm/programs/express_relay/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "express-relay"
version = "0.8.0"
version = "0.9.0"
description = "Pyth Express Relay program for handling permissioning and bid distribution"
repository = "https://github.com/pyth-network/per"
license = "Apache-2.0"
Expand Down
61 changes: 60 additions & 1 deletion contracts/svm/programs/express_relay/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@ use {
system_program::System,
},
anchor_spl::token_interface::{
transfer_checked,
Mint,
TokenAccount,
TokenInterface,
TransferChecked,
},
};

Expand Down Expand Up @@ -182,6 +184,35 @@ pub mod express_relay {
)
}

pub fn withdraw_spl_fees(ctx: Context<WithdrawSplFees>) -> Result<()> {
let amount = ctx.accounts.express_relay_fee_receiver_ata.amount;
if amount == 0 {
return Ok(());
}

let metadata_bump = ctx.bumps.express_relay_metadata;
let signer_seeds: &[&[u8]] = &[SEED_METADATA, &[metadata_bump]];
let signer = &[signer_seeds];
let cpi_accounts = TransferChecked {
from: ctx
.accounts
.express_relay_fee_receiver_ata
.to_account_info(),
to: ctx.accounts.fee_receiver_admin_ta.to_account_info(),
mint: ctx.accounts.mint_fee.to_account_info(),
authority: ctx.accounts.express_relay_metadata.to_account_info(),
};
transfer_checked(
CpiContext::new_with_signer(
ctx.accounts.token_program_fee.to_account_info(),
cpi_accounts,
signer,
),
amount,
ctx.accounts.mint_fee.decimals,
)
}

pub fn swap_internal(ctx: Context<Swap>, data: SwapV2Args) -> Result<()> {
ctx.accounts.check_raw_constraints(data.fee_token)?;
check_deadline(data.deadline)?;
Expand Down Expand Up @@ -224,7 +255,6 @@ pub mod express_relay {
amount_user_after_fees,
)?;


Ok(())
}

Expand Down Expand Up @@ -412,6 +442,35 @@ pub struct WithdrawFees<'info> {
pub express_relay_metadata: Account<'info, ExpressRelayMetadata>,
}

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

#[account(mut, seeds = [SEED_METADATA], bump, has_one = admin)]
pub express_relay_metadata: Account<'info, ExpressRelayMetadata>,

#[account(
mut,
associated_token::mint = mint_fee,
associated_token::authority = express_relay_metadata,
associated_token::token_program = token_program_fee,
)]
pub express_relay_fee_receiver_ata: Box<InterfaceAccount<'info, TokenAccount>>,

/// this can just be any token account for this mint
#[account(
mut,
token::mint = mint_fee,
token::token_program = token_program_fee,
)]
pub fee_receiver_admin_ta: Box<InterfaceAccount<'info, TokenAccount>>,

#[account(mint::token_program = token_program_fee)]
pub mint_fee: Box<InterfaceAccount<'info, Mint>>,

pub token_program_fee: Interface<'info, TokenInterface>,
}

#[derive(AnchorSerialize, AnchorDeserialize, Eq, PartialEq, Clone, Copy, Debug)]
pub enum FeeToken {
Searcher,
Expand Down
1 change: 1 addition & 0 deletions contracts/svm/testing/src/express_relay/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ pub mod set_swap_platform_fee;
pub mod submit_bid;
pub mod swap;
pub mod withdraw_fees;
pub mod withdraw_spl_fees;
38 changes: 38 additions & 0 deletions contracts/svm/testing/src/express_relay/withdraw_spl_fees.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use {
super::helpers::get_express_relay_metadata_key,
anchor_lang::{
InstructionData,
ToAccountMetas,
},
express_relay::accounts::WithdrawSplFees,
solana_sdk::{
instruction::Instruction,
pubkey::Pubkey,
signature::Keypair,
signer::Signer,
},
};

pub fn withdraw_spl_fees_instruction(
admin: &Keypair,
express_relay_fee_receiver_ata: Pubkey,
fee_receiver_admin_ta: Pubkey,
mint_fee: Pubkey,
token_program_fee: Pubkey,
) -> Instruction {
let express_relay_metadata = get_express_relay_metadata_key();

Instruction {
program_id: express_relay::id(),
data: express_relay::instruction::WithdrawSplFees {}.data(),
accounts: WithdrawSplFees {
admin: admin.pubkey(),
express_relay_metadata,
express_relay_fee_receiver_ata,
fee_receiver_admin_ta,
mint_fee,
token_program_fee,
}
.to_account_metas(None),
}
}
135 changes: 135 additions & 0 deletions contracts/svm/testing/tests/withdraw_spl_fees.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
use {
anchor_lang::error::ErrorCode as AnchorErrorCode,
anchor_spl::{
associated_token::{
get_associated_token_address_with_program_id,
spl_associated_token_account::instruction::create_associated_token_account_idempotent,
},
token::spl_token,
},
solana_sdk::{
instruction::InstructionError,
signature::Keypair,
signer::Signer,
},
testing::{
express_relay::{
helpers::get_express_relay_metadata_key,
withdraw_spl_fees::withdraw_spl_fees_instruction,
},
helpers::{
assert_custom_error,
generate_and_fund_key,
submit_transaction,
},
setup::{
setup,
SetupResult,
},
token::Token,
},
};

#[test]
fn test_withdraw_spl_fees() {
let SetupResult { mut svm, admin, .. } = setup(None).expect("setup failed");

let express_relay_metadata = get_express_relay_metadata_key();
let fee_receiver_admin = generate_and_fund_key(&mut svm);
let fee_token = Token::create_mint(&mut svm, spl_token::ID, 6);

let express_relay_fee_receiver_ata = get_associated_token_address_with_program_id(
&express_relay_metadata,
&fee_token.mint,
&fee_token.token_program,
);
let fee_receiver_admin_ata = get_associated_token_address_with_program_id(
&fee_receiver_admin.pubkey(),
&fee_token.mint,
&fee_token.token_program,
);

fee_token.airdrop(&mut svm, &express_relay_metadata, 3.5);

let create_admin_ata_ix = create_associated_token_account_idempotent(
&admin.pubkey(),
&fee_receiver_admin.pubkey(),
&fee_token.mint,
&fee_token.token_program,
);
let withdraw_ix = withdraw_spl_fees_instruction(
&admin,
express_relay_fee_receiver_ata,
fee_receiver_admin_ata,
fee_token.mint,
fee_token.token_program,
);
submit_transaction(
&mut svm,
&[create_admin_ata_ix, withdraw_ix],
&admin,
&[&admin],
)
.unwrap();

assert!(Token::token_balance_matches(
&mut svm,
&express_relay_fee_receiver_ata,
0,
));
assert!(Token::token_balance_matches(
&mut svm,
&fee_receiver_admin_ata,
fee_token.get_amount_with_decimals(3.5),
));
}

#[test]
fn test_withdraw_spl_fees_fail_wrong_admin() {
let SetupResult { mut svm, .. } = setup(None).expect("setup failed");
let wrong_admin = generate_and_fund_key(&mut svm);

let express_relay_metadata = get_express_relay_metadata_key();
let fee_receiver_admin = Keypair::new();
let fee_token = Token::create_mint(&mut svm, spl_token::ID, 6);
let express_relay_fee_receiver_ata = get_associated_token_address_with_program_id(
&express_relay_metadata,
&fee_token.mint,
&fee_token.token_program,
);
let fee_receiver_admin_ata = get_associated_token_address_with_program_id(
&fee_receiver_admin.pubkey(),
&fee_token.mint,
&fee_token.token_program,
);
fee_token.airdrop(&mut svm, &express_relay_metadata, 1.0);
let create_admin_ata_ix = create_associated_token_account_idempotent(
&wrong_admin.pubkey(),
&fee_receiver_admin.pubkey(),
&fee_token.mint,
&fee_token.token_program,
);
submit_transaction(
&mut svm,
&[create_admin_ata_ix],
&wrong_admin,
&[&wrong_admin],
)
.unwrap();

let withdraw_ix = withdraw_spl_fees_instruction(
&wrong_admin,
express_relay_fee_receiver_ata,
fee_receiver_admin_ata,
fee_token.mint,
fee_token.token_program,
);
let tx_result = submit_transaction(&mut svm, &[withdraw_ix], &wrong_admin, &[&wrong_admin])
.expect_err("Transaction should have failed");

assert_custom_error(
tx_result.err,
0,
InstructionError::Custom(AnchorErrorCode::ConstraintHasOne.into()),
);
}
2 changes: 1 addition & 1 deletion sdk/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ solana-rpc-client = { workspace = true }
borsh = { workspace = true }
spl-associated-token-account = { workspace = true }
spl-token = { workspace = true }
express-relay = { version = "0.8.0", path = "../../contracts/svm/programs/express_relay" }
express-relay = { version = "0.9.0", path = "../../contracts/svm/programs/express_relay" }
spl-memo-client = { workspace = true }
Loading