Skip to content

program: deposit sol#631

Open
2501babe wants to merge 10 commits intosolana-program:mainfrom
2501babe:20260417_depositsol
Open

program: deposit sol#631
2501babe wants to merge 10 commits intosolana-program:mainfrom
2501babe:20260417_depositsol

Conversation

@2501babe
Copy link
Copy Markdown
Member

@2501babe 2501babe commented Apr 17, 2026

the latest. the greatest. it needs no further introduction. god knows ive talked about it too much already. deposit sol

closes #46

@2501babe 2501babe self-assigned this Apr 17, 2026
@2501babe 2501babe force-pushed the 20260417_depositsol branch 3 times, most recently from fa0b257 to 1e0b60b Compare April 18, 2026 08:01
@2501babe 2501babe force-pushed the 20260417_depositsol branch 5 times, most recently from 5ff8680 to 310dc5c Compare April 21, 2026 10:44
@2501babe 2501babe marked this pull request as ready for review April 21, 2026 10:45
@2501babe 2501babe requested a review from joncinque April 21, 2026 10:45
@2501babe 2501babe force-pushed the 20260417_depositsol branch from 310dc5c to 5542960 Compare April 22, 2026 14:51
Copy link
Copy Markdown
Contributor

@joncinque joncinque left a comment

Choose a reason for hiding this comment

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

Looks really great! Just a couple of small things

Comment thread clients/cli/src/cli.rs Outdated
Comment thread clients/cli/src/cli.rs Outdated
Comment thread clients/cli/src/main.rs Outdated
Comment thread program/src/lib.rs Outdated
Comment thread clients/js/src/transactions.ts Outdated
}

interface DepositSolParams {
rpc: Rpc<GetAccountInfoApi & GetMinimumBalanceForRentExemptionApi & GetStakeMinimumDelegationApi>;
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.

nit: I think this only needs GetAccountInfoApi

Suggested change
rpc: Rpc<GetAccountInfoApi & GetMinimumBalanceForRentExemptionApi & GetStakeMinimumDelegationApi>;
rpc: Rpc<GetAccountInfoApi>;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Comment thread program/src/processor.rs
Comment on lines +1651 to +1656
// we round division down and reject deposits too small to generate a fee
// this is to avoid pathological cases where eg someone deposits 2 lamps and pays 50%
let deposit_sol_fee = raw_tokens
.checked_mul(DEPOSIT_SOL_FEE_BPS)
.and_then(|n| n.checked_div(MAX_BPS))
.ok_or(SinglePoolError::UnexpectedMathError)?;
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.

I would recommend a ceil_div on this calculation so that we always take a little bit more.

Because of the 0 check later, it's impossible for someone to deposit less than 100 lamports anyway, which is a slick touch

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Because of the 0 check later, it's impossible for someone to deposit less than 100 lamports anyway

this would no longer be true if we used ceiling division though because the fee would always be at least 1. idk i think its more straightforward doing it this way, you can only game it for 1 lamport which doesnt matter for large deposits, and we can easily reject dust (microdust?) deposits. given 5000 lamps per signature, there isnt any reason to deposit 1/50 of that to play games over 1/5000 of it

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.

I'm mostly suggesting it for consistency with other protocols which typically ceiling fee calculations. We can keep it this way if you prefer

Comment thread program/src/processor.rs
Comment on lines +1682 to +1696
// replenish to delegate the deposit. this safely returns Ok if onramp doesnt meet minimum delegation
invoke(
&svsp_instruction::replenish_pool(program_id, vote_account_info.key),
&[
vote_account_info.clone(),
pool_info.clone(),
pool_stake_info.clone(),
pool_onramp_info.clone(),
pool_stake_authority_info.clone(),
clock_info.clone(),
stake_history_info.clone(),
stake_config_info.clone(),
stake_program_info.clone(),
],
)?;
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.

We don't typically perform re-entrancy in our programs, so just flagging the behavior, but I can't see a reason why it would be a problem

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

do you think its worth making a hard rule that we dont? i considered using instruction introspection to verify a replenish follows, because i hate how many accounts this instruction requires that it only uses for this cpi. i decided it would only add complexity for no benefit since the transaction would need the accounts anyway. but the point that we would avoid a potentially risky self-cpi does mean that there is a real tradeoff beyond aesthetics

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.

I've thought about it a bit more, and maybe it's less risky to self-cpi, since we should get additional checks from the runtime. Let's check with the runtime team and see what they think?

Copy link
Copy Markdown
Member Author

@2501babe 2501babe Apr 23, 2026

Choose a reason for hiding this comment

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

ok how do you feel about this concept d0bfddb i think i prefer ixn introspection now. the thing is if we mess up reentry it could have catastrophic consequences. if we mess up checking for a replenish... it doesnt really matter. it just means the pool is less fresh than we would prefer

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.

I kind of worry about transaction introspection, since it doesn't work with CPI use-cases. This should be a pretty low-level primitive, so easy to compose in defi and whatnot.

I'd lean more towards the re-entrancy or just calling process_replenish directly

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

i reverted the ixn introspection commit

Comment thread program/src/processor.rs
AccountMeta::new_readonly(system_program::id(), false),
AccountMeta::new_readonly(spl_token::id(), false),
AccountMeta::new_readonly(stake::program::id(), false),
AccountMeta::new_readonly(*program_id, false),
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.

Is this actually needed for re-entrancy? Since the JS client seem ok without it, I'm going to guess not

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

hana@laptop:program$ git diff 
diff --git a/program/src/instruction.rs b/program/src/instruction.rs
index fc2a356..b70cc72 100644
--- a/program/src/instruction.rs
+++ b/program/src/instruction.rs
@@ -425,7 +425,7 @@ pub fn deposit_sol(
         AccountMeta::new_readonly(system_program::id(), false),
         AccountMeta::new_readonly(spl_token::id(), false),
         AccountMeta::new_readonly(stake::program::id(), false),
-        AccountMeta::new_readonly(*program_id, false),
+        //AccountMeta::new_readonly(*program_id, false),
     ];
 
     Instruction {
hana@laptop:program$ cargo test-sbf --test deposit_sol success::stakeprogramversion_stable_test_stake_amount_0_expects 2> /dev/null

running 1 test
test success::stakeprogramversion_stable_test_stake_amount_0_expects ... FAILED

failures:

---- success::stakeprogramversion_stable_test_stake_amount_0_expects stdout ----

thread 'success::stakeprogramversion_stable_test_stake_amount_0_expects' (26256) panicked at program/tests/deposit_sol.rs:102:10:
called `Result::unwrap()` on an `Err` value: TransactionError(InstructionError(1, MissingAccount))
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

thats an interesting point with the js though i didnt notice that...

Copy link
Copy Markdown
Member Author

@2501babe 2501babe Apr 23, 2026

Choose a reason for hiding this comment

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

well i did confirm that we get the same failure if we omit the program account and use the spl-single-pool cli against solana-test-validator. i also confirmed that the program does not need the extra account when running the js test and that replenish succeeds

this means it is a) a bug in the js client to not pass the account, which is b) hidden by either a bug in litesvm, or a semantic difference in all svm-based testing frameworks, which causes the cpi to succeed when it should fail

ill wait for resolution on #631 (comment) before addressing it one way or the other

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.

Oh interesting, sounds like it might be a bug in litesvm then!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

5e9dfae added the account to js and added a rust test that will tell us whether this is litesvm or svm in general. ill test out mollusk separately to see if it does this too

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

37ffca0 nevermind i deleted the test, theres no way mollusk has this. i was confused thinking litesvm used a stripped-down real svm that somehow elided program-runtime prechecks and maybe the issue may show up elsewhere, i didnt realize it actually used an entirely fake svm

@2501babe 2501babe force-pushed the 20260417_depositsol branch from d0bfddb to 3f645ec Compare April 24, 2026 07:24
@2501babe 2501babe requested a review from joncinque April 24, 2026 07:37
Copy link
Copy Markdown
Contributor

@joncinque joncinque left a comment

Choose a reason for hiding this comment

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

Looks good to me, let's go with this! There's a merge conflict and a few uses of nev in this PR in case you want to update that -- once it's all good, I'll approve

2501babe added 10 commits April 24, 2026 22:15
`DepositSol` is a new instruction that allows depositing liquid sol directly into the pool onramp for a fee, calculated by underminting tokens to spread the benefit to all existing holders
@2501babe 2501babe force-pushed the 20260417_depositsol branch from 37ffca0 to b8335c6 Compare April 25, 2026 05:17
@2501babe
Copy link
Copy Markdown
Member Author

b8335c6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

program: allow sol deposit

2 participants