-
Notifications
You must be signed in to change notification settings - Fork 40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
v0.4 #246
v0.4 #246
Conversation
b2bff55
to
cdd9812
Compare
Reviewing it now. It would be useful to get a short high level overview of some of the changes and the new structure. |
)] | ||
pub base_vault: Account<'info, ConditionalVaultAccount>, | ||
#[account( | ||
constraint = pass_amm.base_mint == base_vault.conditional_on_finalize_token_mint, | ||
constraint = pass_amm.quote_mint == quote_vault.conditional_on_finalize_token_mint, | ||
constraint = pass_amm.base_mint == base_vault.conditional_token_mints[1], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of using 0 and 1 everywhere i'm wondering if it would be better / possible to use an enum. So it's clear this is referring to pass / fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
100%, this is a great idea
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed by 1b68f4b
programs/conditional_vault/src/instructions/add_metadata_to_conditional_tokens.rs
Show resolved
Hide resolved
let (new_proposal_state, new_vault_state) = if pass_market_twap > threshold { | ||
(ProposalState::Passed, VaultStatus::Finalized) | ||
let (new_proposal_state, payout_numerators) = if pass_market_twap > threshold { | ||
(ProposalState::Passed, vec![0, 1]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, i see now that these are the "numerators" that say the proposal 100% passed or 100% failed. On the surface of it, not super intuitive. Don't have an immediate suggestion though.
question.num_outcomes(), | ||
VaultError::InvalidNumPayoutNumerators | ||
); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we check that the options add up to 100%:
require_eq!(
question.payout_denominator,
args.payout_numerators.iter().sum(),
VaultError::InvalidNumPayoutNumerators
);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is implicit because we're setting question.payout_denominator equal to this sum, so check not needed
High-level: abstractify the conditional vault to allow for multiple options and variable payouts for each option. We do this via having "questions" which have "outcomes," which themselves have "payouts." It's pretty similar to the way that Gnosis' conditional token framework works. |
So what's the intention for the multi-outcome markets here?
|
Each outcome will have its own AMM markets, so we don't need to change the amm program to support this. We will need to change autocrat to handle multiple markets (it'll need to receive all of them and pick the option with the highest TWAP). |
If we ignored constraints on dev time, IMO it would be better if we used FPMMs like Polymarket and Gnosis. Using separate AMMs is leaky. If I buy Pass I am explicitly selling Fail here. Arb bots will be able to scalp across AMMs here. I guess pro traders can use Jito bundles though. Using an FPMM would also get rid of the leak in the proposers' liquidity from arbs with the spot market. Though, these arbs are good for the DAO metrics and revenue. This is audited code for a multi-asset AMM: https://github.com/igneous-labs/s |
The AMM is actually already a FPMM (more commonly referred to as a CPMM). How would using one stop value leakage through spot market arbing? |
Sorry, this doesn't fix arbs with spot. There won't be any arbs with spot for betting markets with PassUSDC/FailUSDC tokens. Though for DAO related decisions a PassDaoToken / FailDAOToken betting market is more capital efficient for holders. |
has_one = base_vault, | ||
has_one = quote_vault, | ||
// has_one = base_vault, | ||
// has_one = quote_vault, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't there be a has_one = question
now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this might be a critical issue.
- create proposal with question A, and let it pass
- create question B with the same proposal as oracle
- call finalize_proposal with the proposal, but with question B
- question B will now be resolved, and the proposal will be blocked from resolving with question A because the state is now passed/failed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep, this was indeed a critical issue. resolved: e616e3a
let signer = &[&seeds[..]]; | ||
let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); | ||
|
||
system_program::create_account( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://github.com/solana-labs/solana/blob/27eff8408b7223bb3c4ab70523f8a8dca3ca6645/programs/system/src/system_processor.rs#L161
when accounts already have lamports, create_account will fail
attackers may send a lamport to your conditional accounts and make initialization impossible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's retarded, okay I guess I will do what anchor does: https://github.com/coral-xyz/anchor/blob/340e9c13f42191a1caa6092d811ed930a64c1f7b/lang/syn/src/codegen/accounts/constraints.rs#L1603-L1627
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed: 166de60
|
||
ctx.accounts.user_underlying_token_account.reload()?; | ||
ctx.accounts.vault_underlying_token_account.reload()?; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice, I like the meticulous checks here
.position(|mint| mint == &conditional_mint.key()) | ||
.unwrap(); | ||
|
||
total_redeemable += ((user_conditional_token_account.amount as u128 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this logic means that we're rounding down (e.g. 1000 * 100/900 -> 111). This means the vault will end up with some dust
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep, I'm okay with that
|
||
for acc in user_conditional_token_accounts.iter_mut() { | ||
acc.reload()?; | ||
require_eq!(acc.amount, 0, VaultError::AssertFailed); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
might as well close the accounts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makes it easier for me to index stuff if they're not closed
.iter() | ||
.enumerate() | ||
.map(|(i, supply)| { | ||
*supply * question.payout_numerators[i] as u64 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in redeem_tokens.rs, you cast to u128 for safe multiplication. I'd recommend doing it here as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep, good catch!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed: 963ee25
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've reviewed the programs, and added some comments. Most importantly you should add a check when resolving amms/questions (has_one = question), as it may be a critical bug.
Otherwise looks mostly quite solid, just added tiny comments
ah please note my comments often refer to the code before/after what's shown by github as the context... |
|
||
total_redeemable += ((user_conditional_token_account.amount as u128 | ||
* question.payout_numerators[payout_index] as u128) | ||
/ question.payout_denominator as u128) as u64; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in addition to dust from round downs (perhaps using div_ceil
for one can mitigate)
doing a mix of account loader Context
and (unsafe) math logic within one function. would be great to split up math and state modification logic from solana account loader stuff. this makes rust tests much easier to write and lets you fuzz more easily
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
div_ceil
could cause liabilities to exceed assets as it rounds up. we'd want to use div_floor
, which is the same as regular division for unsigned integers: https://doc.rust-lang.org/std/primitive.u32.html#method.div_floor
90a4b2e
to
40de26a
Compare
see this new PR @0xbigz @Henry-E