Skip to content
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

Merged
merged 121 commits into from
Oct 11, 2024
Merged

v0.4 #246

merged 121 commits into from
Oct 11, 2024

Conversation

metaproph3t
Copy link
Member

see this new PR @0xbigz @Henry-E

@Henry-E Henry-E self-requested a review August 30, 2024 05:15
@Henry-E
Copy link
Collaborator

Henry-E commented Aug 30, 2024

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],
Copy link
Collaborator

@Henry-E Henry-E Aug 30, 2024

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.

Copy link
Member Author

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

Copy link
Member Author

Choose a reason for hiding this comment

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

Addressed by 1b68f4b

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])
Copy link
Collaborator

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
);

Copy link

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
    );

Copy link
Member Author

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

@metaproph3t
Copy link
Member Author

Reviewing it now. It would be useful to get a short high level overview of some of the changes and the new structure.

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.

@Henry-E
Copy link
Collaborator

Henry-E commented Sep 7, 2024

So what's the intention for the multi-outcome markets here?

  • The conditional vaults can be used to create conditional tokens
  • There's an oracle that determines the outcome
  • The AMM still only deals with TWAP proposals
    Where does trading for multi-outcome markets actually happen? Is the UI just going to plug into a bunch of regular meteora AMMs or is there another program still to come?

@metaproph3t
Copy link
Member Author

So what's the intention for the multi-outcome markets here?

* The conditional vaults can be used to create conditional tokens

* There's an oracle that determines the outcome

* The AMM still only deals with TWAP proposals
  Where does trading for multi-outcome markets actually happen? Is the UI just going to plug into a bunch of regular meteora AMMs or is there another program still to come?

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).

@92GC
Copy link

92GC commented Sep 9, 2024

Each outcome will have its own AMM markets, so we don't need to change the amm program to support this.

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

@metaproph3t
Copy link
Member Author

Each outcome will have its own AMM markets, so we don't need to change the amm program to support this.

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.

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?

@92GC
Copy link

92GC commented Sep 9, 2024

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,
Copy link

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?

Copy link

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.

  1. create proposal with question A, and let it pass
  2. create question B with the same proposal as oracle
  3. call finalize_proposal with the proposal, but with question B
  4. question B will now be resolved, and the proposal will be blocked from resolving with question A because the state is now passed/failed

Copy link
Member Author

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(
Copy link

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.

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Member Author

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()?;

Copy link

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
Copy link

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

Copy link
Member Author

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);
Copy link

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

Copy link
Member Author

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
Copy link

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

Copy link
Member Author

Choose a reason for hiding this comment

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

yep, good catch!

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed: 963ee25

Copy link

@robre robre left a 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

@robre
Copy link

robre commented Sep 10, 2024

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;
Copy link
Collaborator

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

Copy link
Member Author

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

@metaproph3t metaproph3t force-pushed the arbitrary-conditional-vault branch 2 times, most recently from 90a4b2e to 40de26a Compare October 11, 2024 16:05
@metaproph3t metaproph3t marked this pull request as ready for review October 11, 2024 16:05
@metaproph3t metaproph3t merged commit b4baaca into develop Oct 11, 2024
3 checks passed
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.

6 participants