Skip to content

Commit a63c3ea

Browse files
wphan0xbigz
andauthored
add reference offset test example (#1683)
* add reference offset test example * fix which mkt used to calculate bidask * fix bankrun update slot when advancing time * program: fix amm.bid_ask_price for ref price offset * linter, fix ref offset tests * check vamm mid increased on neg inventory and ref price offset * fix sdk vs program bug, add more test cases * add ref price offset test with fill, fix build * remove dlog * fix tests * revert logs * fix test timeout * try fix timeout again * fix fuel tests * try fix bankrun test race conditions again * fix another racey test * fix another race * linter, allow interrupting test script --------- Co-authored-by: 0xbigz <83473873+0xbigz@users.noreply.github.com>
1 parent f126965 commit a63c3ea

File tree

14 files changed

+690
-51
lines changed

14 files changed

+690
-51
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ sdk/src/**/*.js.map
1111
.DS_STORE
1212
.vscode
1313
migrations
14-
/**/*.env
14+
/**/*.env
15+
vendor

programs/drift/src/controller/amm.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,8 @@ pub fn update_spreads(market: &mut PerpMarket, reserve_price: u64) -> DriftResul
184184
let liquidity_ratio = amm_spread::calculate_inventory_liquidity_ratio(
185185
market.amm.base_asset_amount_with_amm,
186186
market.amm.base_asset_reserve,
187-
market.amm.max_base_asset_reserve,
188187
market.amm.min_base_asset_reserve,
188+
market.amm.max_base_asset_reserve,
189189
)?;
190190

191191
let signed_liquidity_ratio =

programs/drift/src/controller/position/tests.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -609,8 +609,9 @@ fn amm_perp_ref_offset() {
609609

610610
let reserve_price = perp_market.amm.reserve_price().unwrap();
611611
let (b1, a1) = perp_market.amm.bid_ask_price(reserve_price).unwrap();
612-
assert_eq!(b1, 7098048);
613-
assert_eq!(a1, 7105178);
612+
assert_eq!(reserve_price, 7101599);
613+
assert_eq!(b1, 7225876);
614+
assert_eq!(a1, 7233006);
614615
assert_eq!(
615616
perp_market.amm.historical_oracle_data.last_oracle_price,
616617
7101600
@@ -690,18 +691,18 @@ fn amm_perp_ref_offset() {
690691

691692
let r = perp_market.amm.reserve_price().unwrap();
692693
let (b, a) = perp_market.amm.bid_ask_price(r).unwrap();
693-
assert_eq!(b, 7098048);
694-
assert_eq!(a, 7105178);
694+
assert_eq!(b, 7098999);
695+
assert_eq!(a, 7106129);
695696
assert_eq!(
696697
perp_market.amm.historical_oracle_data.last_oracle_price,
697698
7101600
698699
);
699-
assert_eq!(perp_market.amm.reference_price_offset, 45000);
700+
assert_eq!(perp_market.amm.reference_price_offset, 134);
700701
assert_eq!(perp_market.amm.max_spread, 90000);
701702

702703
assert_eq!(r, 7101599);
703-
assert_eq!(perp_market.amm.bid_base_asset_reserve, 4570430670410018);
704-
assert_eq!(perp_market.amm.ask_base_asset_reserve, 4568069910766211);
704+
assert_eq!(perp_market.amm.bid_base_asset_reserve, 4675159724262455);
705+
assert_eq!(perp_market.amm.ask_base_asset_reserve, 4672813088646692);
705706

706707
crate::validation::perp_market::validate_perp_market(&perp_market).unwrap();
707708
}

programs/drift/src/state/perp_market.rs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ use crate::math::constants::{
1414
};
1515
use crate::math::constants::{
1616
AMM_RESERVE_PRECISION_I128, AMM_TO_QUOTE_PRECISION_RATIO, BID_ASK_SPREAD_PRECISION,
17-
BID_ASK_SPREAD_PRECISION_U128, DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT,
18-
LIQUIDATION_FEE_PRECISION, LIQUIDATION_FEE_TO_MARGIN_PRECISION_RATIO, LP_FEE_SLICE_DENOMINATOR,
19-
LP_FEE_SLICE_NUMERATOR, MARGIN_PRECISION, MARGIN_PRECISION_U128, MAX_LIQUIDATION_MULTIPLIER,
20-
PEG_PRECISION, PERCENTAGE_PRECISION, PERCENTAGE_PRECISION_I128, PERCENTAGE_PRECISION_I64,
17+
BID_ASK_SPREAD_PRECISION_I128, BID_ASK_SPREAD_PRECISION_U128,
18+
DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT, LIQUIDATION_FEE_PRECISION,
19+
LIQUIDATION_FEE_TO_MARGIN_PRECISION_RATIO, LP_FEE_SLICE_DENOMINATOR, LP_FEE_SLICE_NUMERATOR,
20+
MARGIN_PRECISION, MARGIN_PRECISION_U128, MAX_LIQUIDATION_MULTIPLIER, PEG_PRECISION,
21+
PERCENTAGE_PRECISION, PERCENTAGE_PRECISION_I128, PERCENTAGE_PRECISION_I64,
2122
PERCENTAGE_PRECISION_U64, PRICE_PRECISION, SPOT_WEIGHT_PRECISION, TWENTY_FOUR_HOUR,
2223
};
2324
use crate::math::helpers::get_proportion_i128;
@@ -1413,19 +1414,31 @@ impl AMM {
14131414
}
14141415

14151416
pub fn bid_price(&self, reserve_price: u64) -> DriftResult<u64> {
1417+
let adjusted_spread =
1418+
(-(self.short_spread.cast::<i32>()?)).safe_add(self.reference_price_offset)?;
1419+
1420+
let multiplier = BID_ASK_SPREAD_PRECISION_I128.safe_add(adjusted_spread.cast::<i128>()?)?;
1421+
14161422
reserve_price
14171423
.cast::<u128>()?
1418-
.safe_mul(BID_ASK_SPREAD_PRECISION_U128.safe_sub(self.short_spread.cast()?)?)?
1424+
.safe_mul(multiplier.cast::<u128>()?)?
14191425
.safe_div(BID_ASK_SPREAD_PRECISION_U128)?
14201426
.cast()
14211427
}
14221428

14231429
pub fn ask_price(&self, reserve_price: u64) -> DriftResult<u64> {
1430+
let adjusted_spread = self
1431+
.long_spread
1432+
.cast::<i32>()?
1433+
.safe_add(self.reference_price_offset)?;
1434+
1435+
let multiplier = BID_ASK_SPREAD_PRECISION_I128.safe_add(adjusted_spread.cast::<i128>()?)?;
1436+
14241437
reserve_price
14251438
.cast::<u128>()?
1426-
.safe_mul(BID_ASK_SPREAD_PRECISION_U128.safe_add(self.long_spread.cast()?)?)?
1439+
.safe_mul(multiplier.cast::<u128>()?)?
14271440
.safe_div(BID_ASK_SPREAD_PRECISION_U128)?
1428-
.cast::<u64>()
1441+
.cast()
14291442
}
14301443

14311444
pub fn bid_ask_price(&self, reserve_price: u64) -> DriftResult<(u64, u64)> {

sdk/src/bankrun/bankrunConnection.ts

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ export class BankrunContextWrapper {
121121
}
122122

123123
async moveTimeForward(increment: number): Promise<void> {
124+
const approxSlots = increment / 0.4;
125+
const slot = await this.connection.getSlot();
126+
this.context.warpToSlot(BigInt(Math.floor(Number(slot) + approxSlots)));
127+
124128
const currentClock = await this.context.banksClient.getClock();
125129
const newUnixTimestamp = currentClock.unixTimestamp + BigInt(increment);
126130
const newClock = new Clock(
@@ -130,6 +134,7 @@ export class BankrunContextWrapper {
130134
currentClock.leaderScheduleEpoch,
131135
newUnixTimestamp
132136
);
137+
133138
await this.context.setClock(newClock);
134139
}
135140

@@ -241,13 +246,31 @@ export class BankrunConnection {
241246
: await internal.tryProcessLegacyTransaction(serialized);
242247
const banksTransactionMeta = new BanksTransactionResultWithMeta(inner);
243248

244-
if (banksTransactionMeta.result) {
245-
throw new Error(banksTransactionMeta.result);
246-
}
247249
const signature = isVersioned
248250
? bs58.encode((tx as VersionedTransaction).signatures[0])
249251
: bs58.encode((tx as Transaction).signatures[0].signature);
250-
this.transactionToMeta.set(signature, banksTransactionMeta);
252+
253+
if (banksTransactionMeta.result) {
254+
if (
255+
!banksTransactionMeta.result
256+
.toString()
257+
.includes('This transaction has already been processed')
258+
) {
259+
throw new Error(banksTransactionMeta.result);
260+
} else {
261+
console.log(
262+
`Tx already processed (sig: ${signature}): ${JSON.stringify(
263+
banksTransactionMeta
264+
)}`,
265+
banksTransactionMeta,
266+
banksTransactionMeta.result
267+
);
268+
console.log(tx);
269+
}
270+
}
271+
if (!this.transactionToMeta.has(signature)) {
272+
this.transactionToMeta.set(signature, banksTransactionMeta);
273+
}
251274
let finalizedCount = 0;
252275
while (finalizedCount < 10) {
253276
const signatureStatus = (await this.getSignatureStatus(signature)).value
@@ -290,7 +313,7 @@ export class BankrunConnection {
290313
return signature;
291314
}
292315

293-
private async updateSlotAndClock() {
316+
async updateSlotAndClock() {
294317
const currentSlot = await this.getSlot();
295318
const nextSlot = currentSlot + BigInt(1);
296319
this.context.warpToSlot(nextSlot);
@@ -404,6 +427,9 @@ export class BankrunConnection {
404427
const transactionStatus = await this._banksClient.getTransactionStatus(
405428
signature
406429
);
430+
if (txMeta.meta === null) {
431+
throw new Error(`tx has no meta: ${JSON.stringify(txMeta)}`);
432+
}
407433
const meta: BankrunTransactionMetaNormalized = {
408434
logMessages: txMeta.meta.logMessages,
409435
err: txMeta.result,

sdk/src/math/amm.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -997,7 +997,7 @@ export function calculateSpreadReserves(
997997
amm.pegMultiplier
998998
);
999999

1000-
// always allow 10 bps of price offset, up to a fifth of the market's max_spread
1000+
// always allow 10 bps of price offset, up to a half of the market's max_spread
10011001
let maxOffset = 0;
10021002
let referencePriceOffset = ZERO;
10031003
if (amm.curveUpdateIntensity > 100) {

test-scripts/run-anchor-tests.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
#!/bin/bash
2+
3+
set -e
4+
trap 'echo -e "\nStopped by signal $? (SIGINT)"; exit 0' INT
5+
16
if [ "$1" != "--skip-build" ]; then
27
anchor build -- --features anchor-test && anchor test --skip-build &&
38
cp target/idl/drift.json sdk/src/idl/

test-scripts/single-anchor-test.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ fi
66

77
export ANCHOR_WALLET=~/.config/solana/id.json
88

9-
test_files=(overwritePerpAccounts.ts)
9+
test_files=(referencePriceOffset.ts)
1010

1111
for test_file in ${test_files[@]}; do
1212
ts-mocha -t 300000 ./tests/${test_file}

tests/fuel.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -246,23 +246,14 @@ describe("fuelin'", () => {
246246
assert(takerUserStats.ifStakedQuoteAssetAmount.gt(ZERO));
247247

248248
await fillerDriftClient.updateSpotMarketFuel(0, 100, 200, 200, 0, 250);
249-
const currentClockInit =
250-
await bankrunContextWrapper.context.banksClient.getClock();
251249

252250
await takerDriftClient.fetchAccounts();
253251
assert(takerDriftClient.getSpotMarketAccount(0).fuelBoostInsurance > 0);
254252

255-
const fuelDictInit = takerDriftClientUser.getFuelBonus(
256-
new BN(currentClockInit.unixTimestamp.toString()).addn(36000),
257-
true,
258-
true
259-
);
260-
261-
console.log(fuelDictInit);
262-
assert(fuelDictInit['insuranceFuel'].gt(ZERO));
263-
264-
const timeProgress = 36000; // 30 days in seconds
253+
const currentClockInit =
254+
await bankrunContextWrapper.context.banksClient.getClock();
265255

256+
const timeProgress = 36000; // 30 hours in seconds
266257
await bankrunContextWrapper.moveTimeForward(timeProgress);
267258

268259
const _ = await takerDriftClient.requestRemoveInsuranceFundStake(
@@ -273,6 +264,15 @@ describe("fuelin'", () => {
273264
const currentClockInit2 =
274265
await bankrunContextWrapper.context.banksClient.getClock();
275266

267+
const timeDiff =
268+
currentClockInit2.unixTimestamp - currentClockInit.unixTimestamp;
269+
const fuelDictInit = takerDriftClientUser.getFuelBonus(
270+
new BN(currentClockInit.unixTimestamp.toString()).addn(Number(timeDiff)),
271+
true,
272+
true
273+
);
274+
assert(fuelDictInit['insuranceFuel'].gt(ZERO));
275+
276276
const fuelDictInit2 = takerDriftClientUser.getFuelBonus(
277277
new BN(currentClockInit2.unixTimestamp.toString()),
278278
true,
@@ -316,7 +316,7 @@ describe("fuelin'", () => {
316316
);
317317

318318
console.log(fuelDictRmStake);
319-
const totalFuelIfNoUnstake = 119791;
319+
const totalFuelIfNoUnstake = 119802;
320320
const expectedFuel =
321321
(totalFuelIfNoUnstake - fuelDictInit2['insuranceFuel'].toNumber()) / 3 +
322322
fuelDictInit2['insuranceFuel'].toNumber();

tests/fuelSweep.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ describe('fuel sweep', () => {
152152
it('cannot init fuel sweep with low fuel amount', async () => {
153153
let success = false;
154154
try {
155+
userDriftClient.txParams.computeUnits = 600_000;
155156
await userDriftClient.initializeFuelOverflow(
156157
userDriftClient.wallet.publicKey
157158
);
@@ -187,7 +188,8 @@ describe('fuel sweep', () => {
187188
assert.equal(userStatsAfter.data.fuelTaker, 4_000_000_001);
188189
assert.equal(userStatsAfter.data.fuelMaker, 4_000_000_000);
189190

190-
// initialize FuelSweep account
191+
// use a different CU limit to prevent race condition of sending two identical transactions
192+
userDriftClient.txParams.computeUnits = 600_001;
191193
await userDriftClient.initializeFuelOverflow(
192194
userDriftClient.wallet.publicKey
193195
);

0 commit comments

Comments
 (0)