Skip to content

Commit 8dfa34d

Browse files
authored
Merge pull request #69 from metaDAOproject/auto_checks
Autocrat Changes
2 parents 9c08d5e + 2ce39bc commit 8dfa34d

8 files changed

Lines changed: 1523 additions & 984 deletions

File tree

Cargo.lock

Lines changed: 21 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

programs/autocrat_v0/src/lib.rs

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,20 @@ declare_id!("metaRK9dUBnrAdZN6uUDKvxBVKW5pyCbPVmLtUZwtBp");
2929
pub const SLOTS_PER_10_SECS: u64 = 25;
3030
pub const THREE_DAYS_IN_SLOTS: u64 = 3 * 24 * 60 * 6 * SLOTS_PER_10_SECS;
3131

32-
// by default, the pass price needs to be 5% higher than the fail price
33-
pub const DEFAULT_PASS_THRESHOLD_BPS: u16 = 500;
32+
pub const TEN_DAYS_IN_SECONDS: i64 = 10 * 24 * 60 * 60;
33+
34+
// by default, the pass price needs to be 3% higher than the fail price
35+
pub const DEFAULT_PASS_THRESHOLD_BPS: u16 = 300;
3436

3537
// start at 10 SOL ($600 at current prices), decay by ~5 SOL per day
3638
pub const DEFAULT_BASE_BURN_LAMPORTS: u64 = 10 * solana_program::native_token::LAMPORTS_PER_SOL;
3739
pub const DEFAULT_BURN_DECAY_PER_SLOT_LAMPORTS: u64 = 23_150;
3840

3941
pub const MAX_BPS: u16 = 10_000;
4042

43+
// TWAP can only move by $5 per slot
44+
pub const DEFAULT_MAX_OBSERVATION_CHANGE_PER_UPDATE_LOTS: u64 = 5_000;
45+
4146
#[account]
4247
pub struct DAO {
4348
// treasury needed even though DAO is PDA for this reason: https://solana.stackexchange.com/questions/7667/a-peculiar-problem-with-cpis
@@ -59,6 +64,7 @@ pub struct DAO {
5964
pub slots_per_proposal: u64,
6065
pub market_taker_fee: i64,
6166
pub twap_expected_value: u64,
67+
pub max_observation_change_per_update_lots: u64,
6268
}
6369

6470
#[derive(Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Eq)]
@@ -115,7 +121,9 @@ pub mod autocrat_v0 {
115121
dao.burn_decay_per_slot_lamports = DEFAULT_BURN_DECAY_PER_SLOT_LAMPORTS;
116122
dao.slots_per_proposal = THREE_DAYS_IN_SLOTS;
117123
dao.market_taker_fee = 0;
118-
dao.twap_expected_value = 10_000; // 1 USDC per META
124+
// 100_000 price lots * quote lot size of 100 = 10_000_000 or $10 per quote lot size of meta, which is 0.1 meta
125+
dao.twap_expected_value = 100_000; // $100 USDC per 1 META
126+
dao.max_observation_change_per_update_lots = DEFAULT_MAX_OBSERVATION_CHANGE_PER_UPDATE_LOTS;
119127

120128
let (treasury_pubkey, treasury_bump) =
121129
Pubkey::find_program_address(&[dao.key().as_ref()], ctx.program_id);
@@ -145,10 +153,13 @@ pub mod autocrat_v0 {
145153
AutocratError::InvalidMarket
146154
);
147155

148-
// this should also be checked by `openbook_twap`, but why not take the
149-
// precaution?
156+
let current_time = Clock::get().unwrap().unix_timestamp as i64;
157+
158+
// The market expires a minimum of 7 days after the end of a 3 day proposal.
159+
// Make sure to do final TWAP crank after the proposal period has ended
160+
// and before the market expires, or else! Allows for rent retrieval from openbook
150161
require!(
151-
openbook_pass_market.time_expiry == 0,
162+
openbook_pass_market.time_expiry > current_time + TEN_DAYS_IN_SECONDS,
152163
AutocratError::InvalidMarket
153164
);
154165
require!(
@@ -164,7 +175,7 @@ pub mod autocrat_v0 {
164175
AutocratError::InvalidMarket
165176
);
166177
require!(
167-
openbook_pass_market.base_lot_size == 1_000_000_000, // minimum tradeable = 1 META
178+
openbook_pass_market.base_lot_size == 100_000_000, // minimum tradeable = 0.1 META
168179
AutocratError::InvalidMarket
169180
);
170181
require!(
@@ -187,7 +198,7 @@ pub mod autocrat_v0 {
187198
AutocratError::InvalidMarket
188199
);
189200
require!(
190-
openbook_fail_market.time_expiry == 0,
201+
openbook_fail_market.time_expiry > current_time + TEN_DAYS_IN_SECONDS,
191202
AutocratError::InvalidMarket
192203
);
193204
require!(
@@ -203,11 +214,11 @@ pub mod autocrat_v0 {
203214
AutocratError::InvalidMarket
204215
);
205216
require!(
206-
openbook_fail_market.base_lot_size == 1_000_000_000,
217+
openbook_fail_market.base_lot_size == 100_000_000, // minimum tradeable = 0.1 META
207218
AutocratError::InvalidMarket
208219
);
209220
require!(
210-
openbook_pass_market.quote_lot_size == 100,
221+
openbook_fail_market.quote_lot_size == 100,
211222
AutocratError::InvalidMarket
212223
);
213224
require!(
@@ -227,6 +238,20 @@ pub mod autocrat_v0 {
227238
openbook_twap_fail_market.twap_oracle.initial_slot + 50 >= clock.slot,
228239
AutocratError::TWAPMarketTooOld
229240
);
241+
require_eq!(
242+
openbook_twap_pass_market
243+
.twap_oracle
244+
.max_observation_change_per_update_lots,
245+
dao.max_observation_change_per_update_lots,
246+
AutocratError::TWAPOracleWrongChangeLots
247+
);
248+
require_eq!(
249+
openbook_twap_fail_market
250+
.twap_oracle
251+
.max_observation_change_per_update_lots,
252+
dao.max_observation_change_per_update_lots,
253+
AutocratError::TWAPOracleWrongChangeLots
254+
);
230255
require!(
231256
openbook_twap_pass_market.twap_oracle.expected_value == dao.twap_expected_value,
232257
AutocratError::TWAPMarketInvalidExpectedValue
@@ -390,10 +415,7 @@ pub mod autocrat_v0 {
390415
Ok(())
391416
}
392417

393-
pub fn update_dao(
394-
ctx: Context<UpdateDao>,
395-
dao_params: UpdateDaoParams,
396-
) -> Result<()> {
418+
pub fn update_dao(ctx: Context<UpdateDao>, dao_params: UpdateDaoParams) -> Result<()> {
397419
let dao = &mut ctx.accounts.dao;
398420

399421
if let Some(pass_threshold_bps) = dao_params.pass_threshold_bps {
@@ -420,6 +442,12 @@ pub mod autocrat_v0 {
420442
dao.twap_expected_value = twap_expected_value;
421443
}
422444

445+
if let Some(max_observation_change_per_update_lots) =
446+
dao_params.max_observation_change_per_update_lots
447+
{
448+
dao.max_observation_change_per_update_lots = max_observation_change_per_update_lots;
449+
}
450+
423451
Ok(())
424452
}
425453
}
@@ -514,6 +542,7 @@ pub struct UpdateDaoParams {
514542
pub slots_per_proposal: Option<u64>,
515543
pub market_taker_fee: Option<i64>,
516544
pub twap_expected_value: Option<u64>,
545+
pub max_observation_change_per_update_lots: Option<u64>,
517546
}
518547

519548
#[derive(Accounts)]
@@ -556,6 +585,8 @@ pub enum AutocratError {
556585
InvalidMarket,
557586
#[msg("`TWAPMarket` must have an `initial_slot` within 50 slots of the proposal's `slot_enqueued`")]
558587
TWAPMarketTooOld,
588+
#[msg("`TWAPOracle` has an incorrect max_observation_change_per_update_lots value")]
589+
TWAPOracleWrongChangeLots,
559590
#[msg("`TWAPMarket` has the wrong `expected_value`")]
560591
TWAPMarketInvalidExpectedValue,
561592
#[msg("One of the vaults has an invalid `settlement_authority`")]

scripts/main.ts

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ const CONDITIONAL_VAULT_PROGRAM_ID = new PublicKey(
4444
"vAuLTQjV5AZx5f3UgE75wcnkxnQowWxThn1hGjfCVwP"
4545
);
4646
const OPENBOOK_TWAP_PROGRAM_ID = new PublicKey(
47-
"TWAPrdhADy2aTKN5iFZtNnkQYXERD9NvKjPFVPMSCNN"
47+
"twAP5sArq2vDS1mZCT7f4qRLwzTfHvf5Ay5R5Q5df1m"
4848
);
4949
export const OPENBOOK_PROGRAM_ID = new PublicKey(
5050
"opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb"
@@ -390,17 +390,24 @@ export async function initializeProposal(
390390
openbookTwap.programId
391391
);
392392

393+
const currentTimeInSeconds = Math.floor(Date.now() / 1000);
394+
const elevenDaysInSeconds = 11 * 24 * 60 * 60;
395+
const expiryTime = new BN(currentTimeInSeconds + elevenDaysInSeconds);
396+
const quoteLotSize = new BN(100);
397+
const baseLotSize = new BN(1e8);
398+
const maxObservationChangePerUpdateLots = new BN(5_000);
399+
393400
let [passMarketInstructions, passMarketSigners] =
394401
await openbook.createMarketIx(
395402
payer.publicKey,
396403
`${baseNonce}pMETA/pUSDC`,
397404
passQuoteMint,
398405
passBaseMint,
399-
new BN(100),
400-
new BN(1e9),
401-
new BN(0),
406+
quoteLotSize,
407+
baseLotSize,
402408
new BN(0),
403409
new BN(0),
410+
expiryTime,
404411
null,
405412
null,
406413
openbookTwapPassMarket,
@@ -446,11 +453,11 @@ export async function initializeProposal(
446453
`${baseNonce}fMETA/fUSDC`,
447454
failQuoteMint,
448455
failBaseMint,
449-
new BN(100),
450-
new BN(1e9),
451-
new BN(0),
456+
quoteLotSize,
457+
baseLotSize,
452458
new BN(0),
453459
new BN(0),
460+
expiryTime,
454461
null,
455462
null,
456463
openbookTwapFailMarket,
@@ -480,14 +487,14 @@ export async function initializeProposal(
480487
1000
481488
),
482489
await openbookTwap.methods
483-
.createTwapMarket(new BN(10_000))
490+
.createTwapMarket(new BN(10_000), maxObservationChangePerUpdateLots)
484491
.accounts({
485492
market: openbookPassMarketKP.publicKey,
486493
twapMarket: openbookTwapPassMarket,
487494
})
488495
.instruction(),
489496
await openbookTwap.methods
490-
.createTwapMarket(new BN(10_000))
497+
.createTwapMarket(new BN(10_000), maxObservationChangePerUpdateLots)
491498
.accounts({
492499
market: openbookFailMarketKP.publicKey,
493500
twapMarket: openbookTwapFailMarket,

0 commit comments

Comments
 (0)