├── .cargo
└── audit.toml
├── .devcontainer
├── Dockerfile
└── devcontainer.json
├── .eslintrc.js
├── .github
├── actions
│ └── setup-solana
│ │ ├── action.yaml
│ │ └── scripts
│ │ └── solana-install-init.sh
└── workflows
│ ├── audit.yml
│ ├── main.yml
│ └── security.yml
├── .gitignore
├── .gitmodules
├── .husky
└── pre-commit
├── .prettierignore
├── .prettierrc.js
├── .verified-build.json
├── AUDIT.md
├── Anchor.toml
├── CHANGELOG.md
├── Cargo.lock
├── Cargo.toml
├── LICENSE
├── README.md
├── SECURITY.md
├── bug-bounty
└── README.md
├── cli
├── .gitignore
├── README.md
├── cli.ts
├── package.json
└── tsconfig.json
├── codecov.yml
├── deploy-scripts
├── build-devnet.sh
├── deploy-devnet.sh
└── verified-build.sh
├── deps
└── configs
│ ├── pyth_lazer_storage.json
│ └── usdc.json
├── package.json
├── programs
├── drift
│ ├── Cargo.toml
│ ├── Xargo.toml
│ └── src
│ │ ├── controller
│ │ ├── amm.rs
│ │ ├── amm
│ │ │ └── tests.rs
│ │ ├── funding.rs
│ │ ├── insurance.rs
│ │ ├── insurance
│ │ │ └── tests.rs
│ │ ├── liquidation.rs
│ │ ├── liquidation
│ │ │ └── tests.rs
│ │ ├── lp.rs
│ │ ├── lp
│ │ │ └── tests.rs
│ │ ├── mod.rs
│ │ ├── orders.rs
│ │ ├── orders
│ │ │ ├── amm_jit_tests.rs
│ │ │ ├── amm_lp_jit_tests.rs
│ │ │ ├── fuel_tests.rs
│ │ │ └── tests.rs
│ │ ├── pda.rs
│ │ ├── pnl.rs
│ │ ├── pnl
│ │ │ ├── delisting.rs
│ │ │ └── tests.rs
│ │ ├── position.rs
│ │ ├── position
│ │ │ └── tests.rs
│ │ ├── repeg.rs
│ │ ├── repeg
│ │ │ └── tests.rs
│ │ ├── spot_balance.rs
│ │ ├── spot_balance
│ │ │ └── tests.rs
│ │ ├── spot_position.rs
│ │ ├── spot_position
│ │ │ └── tests.rs
│ │ └── token.rs
│ │ ├── error.rs
│ │ ├── ids.rs
│ │ ├── instructions
│ │ ├── admin.rs
│ │ ├── constraints.rs
│ │ ├── if_staker.rs
│ │ ├── keeper.rs
│ │ ├── mod.rs
│ │ ├── optional_accounts.rs
│ │ ├── pyth_lazer_oracle.rs
│ │ ├── pyth_pull_oracle.rs
│ │ └── user.rs
│ │ ├── lib.rs
│ │ ├── macros.rs
│ │ ├── math
│ │ ├── amm.rs
│ │ ├── amm
│ │ │ └── tests.rs
│ │ ├── amm_jit.rs
│ │ ├── amm_jit
│ │ │ └── tests.rs
│ │ ├── amm_spread.rs
│ │ ├── amm_spread
│ │ │ └── tests.rs
│ │ ├── auction.rs
│ │ ├── auction
│ │ │ └── tests.rs
│ │ ├── bankruptcy.rs
│ │ ├── bankruptcy
│ │ │ └── tests.rs
│ │ ├── bn.rs
│ │ ├── casting.rs
│ │ ├── ceil_div.rs
│ │ ├── constants.rs
│ │ ├── cp_curve.rs
│ │ ├── cp_curve
│ │ │ └── tests.rs
│ │ ├── fees.rs
│ │ ├── fees
│ │ │ └── tests.rs
│ │ ├── floor_div.rs
│ │ ├── fuel.rs
│ │ ├── fuel
│ │ │ └── tests.rs
│ │ ├── fulfillment.rs
│ │ ├── fulfillment
│ │ │ └── tests.rs
│ │ ├── funding.rs
│ │ ├── funding
│ │ │ └── tests.rs
│ │ ├── helpers.rs
│ │ ├── helpers
│ │ │ └── tests.rs
│ │ ├── insurance.rs
│ │ ├── insurance
│ │ │ └── tests.rs
│ │ ├── liquidation.rs
│ │ ├── liquidation
│ │ │ └── tests.rs
│ │ ├── lp.rs
│ │ ├── lp
│ │ │ └── tests.rs
│ │ ├── margin.rs
│ │ ├── margin
│ │ │ └── tests.rs
│ │ ├── matching.rs
│ │ ├── matching
│ │ │ └── tests.rs
│ │ ├── mod.rs
│ │ ├── oracle.rs
│ │ ├── oracle
│ │ │ └── tests.rs
│ │ ├── orders.rs
│ │ ├── orders
│ │ │ └── tests.rs
│ │ ├── pnl.rs
│ │ ├── position.rs
│ │ ├── quote_asset.rs
│ │ ├── repeg.rs
│ │ ├── repeg
│ │ │ └── tests.rs
│ │ ├── safe_math.rs
│ │ ├── safe_unwrap.rs
│ │ ├── serum.rs
│ │ ├── serum
│ │ │ └── tests.rs
│ │ ├── spot_balance.rs
│ │ ├── spot_balance
│ │ │ └── tests.rs
│ │ ├── spot_swap.rs
│ │ ├── spot_swap
│ │ │ └── tests.rs
│ │ ├── spot_withdraw.rs
│ │ └── stats.rs
│ │ ├── signer.rs
│ │ ├── state
│ │ ├── events.rs
│ │ ├── fill_mode.rs
│ │ ├── fill_mode
│ │ │ └── tests.rs
│ │ ├── fulfillment.rs
│ │ ├── fulfillment_params
│ │ │ ├── drift.rs
│ │ │ ├── mod.rs
│ │ │ ├── openbook_v2.rs
│ │ │ ├── phoenix.rs
│ │ │ └── serum.rs
│ │ ├── high_leverage_mode_config.rs
│ │ ├── insurance_fund_stake.rs
│ │ ├── insurance_fund_stake
│ │ │ └── tests.rs
│ │ ├── load_ref.rs
│ │ ├── margin_calculation.rs
│ │ ├── mod.rs
│ │ ├── oracle.rs
│ │ ├── oracle
│ │ │ └── tests.rs
│ │ ├── oracle_map.rs
│ │ ├── order_params.rs
│ │ ├── order_params
│ │ │ └── tests.rs
│ │ ├── paused_operations.rs
│ │ ├── paused_operations
│ │ │ └── tests.rs
│ │ ├── perp_market.rs
│ │ ├── perp_market
│ │ │ └── tests.rs
│ │ ├── perp_market_map.rs
│ │ ├── protected_maker_mode_config.rs
│ │ ├── pyth_lazer_oracle.rs
│ │ ├── settle_pnl_mode.rs
│ │ ├── settle_pnl_mode
│ │ │ └── tests.rs
│ │ ├── signed_msg_user.rs
│ │ ├── signed_msg_user
│ │ │ └── tests.rs
│ │ ├── spot_fulfillment_params.rs
│ │ ├── spot_market.rs
│ │ ├── spot_market_map.rs
│ │ ├── state.rs
│ │ ├── state
│ │ │ └── tests.rs
│ │ ├── traits.rs
│ │ ├── traits
│ │ │ └── tests.rs
│ │ ├── user.rs
│ │ ├── user
│ │ │ └── tests.rs
│ │ └── user_map.rs
│ │ ├── test_utils.rs
│ │ └── validation
│ │ ├── fee_structure.rs
│ │ ├── fee_structure
│ │ └── tests.rs
│ │ ├── margin.rs
│ │ ├── mod.rs
│ │ ├── order.rs
│ │ ├── order
│ │ └── test.rs
│ │ ├── perp_market.rs
│ │ ├── position.rs
│ │ ├── sig_verification.rs
│ │ ├── spot_market.rs
│ │ ├── user.rs
│ │ └── whitelist.rs
├── openbook_v2
│ ├── Cargo.toml
│ ├── README.md
│ └── src
│ │ ├── account.rs
│ │ ├── constants.rs
│ │ ├── context.rs
│ │ └── lib.rs
├── pyth
│ ├── Cargo.toml
│ ├── Xargo.toml
│ └── src
│ │ ├── lib.rs
│ │ └── pc.rs
├── switchboard-on-demand
│ ├── Cargo.toml
│ └── src
│ │ └── lib.rs
├── switchboard
│ ├── Cargo.toml
│ └── src
│ │ └── lib.rs
└── token_faucet
│ ├── Cargo.toml
│ ├── Xargo.toml
│ └── src
│ └── lib.rs
├── sdk
├── .gitignore
├── README.md
├── VERSION
├── bun.lock
├── get_events.ts
├── package.json
├── scripts
│ ├── postbuild.js
│ └── updateVersion.js
├── src
│ ├── accounts
│ │ ├── basicUserAccountSubscriber.ts
│ │ ├── bulkAccountLoader.ts
│ │ ├── bulkUserStatsSubscription.ts
│ │ ├── bulkUserSubscription.ts
│ │ ├── fetch.ts
│ │ ├── grpcAccountSubscriber.ts
│ │ ├── grpcDriftClientAccountSubscriber.ts
│ │ ├── grpcInsuranceFundStakeAccountSubscriber.ts
│ │ ├── grpcProgramAccountSubscriber.ts
│ │ ├── grpcUserAccountSubscriber.ts
│ │ ├── grpcUserStatsAccountSubscriber.ts
│ │ ├── oneShotUserAccountSubscriber.ts
│ │ ├── pollingDriftClientAccountSubscriber.ts
│ │ ├── pollingHighLeverageModeConfigAccountSubscriber.ts
│ │ ├── pollingInsuranceFundStakeAccountSubscriber.ts
│ │ ├── pollingOracleAccountSubscriber.ts
│ │ ├── pollingTokenAccountSubscriber.ts
│ │ ├── pollingUserAccountSubscriber.ts
│ │ ├── pollingUserStatsAccountSubscriber.ts
│ │ ├── testBulkAccountLoader.ts
│ │ ├── types.ts
│ │ ├── utils.ts
│ │ ├── webSocketAccountSubscriber.ts
│ │ ├── webSocketDriftClientAccountSubscriber.ts
│ │ ├── webSocketHighLeverageModeConfigAccountSubscriber.ts
│ │ ├── webSocketInsuranceFundStakeAccountSubscriber.ts
│ │ ├── webSocketProgramAccountSubscriber.ts
│ │ ├── webSocketUserAccountSubscriber.ts
│ │ └── webSocketUserStatsAccountSubsriber.ts
│ ├── addresses
│ │ ├── marketAddresses.ts
│ │ └── pda.ts
│ ├── adminClient.ts
│ ├── assert
│ │ └── assert.ts
│ ├── auctionSubscriber
│ │ ├── auctionSubscriber.ts
│ │ ├── auctionSubscriberGrpc.ts
│ │ ├── index.ts
│ │ └── types.ts
│ ├── bankrun
│ │ └── bankrunConnection.ts
│ ├── blockhashSubscriber
│ │ ├── BlockhashSubscriber.ts
│ │ ├── index.ts
│ │ └── types.ts
│ ├── clock
│ │ └── clockSubscriber.ts
│ ├── config.ts
│ ├── constants
│ │ ├── numericConstants.ts
│ │ ├── perpMarkets.ts
│ │ ├── spotMarkets.ts
│ │ └── txConstants.ts
│ ├── decode
│ │ ├── phoenix.ts
│ │ └── user.ts
│ ├── dlob
│ │ ├── DLOB.ts
│ │ ├── DLOBNode.ts
│ │ ├── DLOBSubscriber.ts
│ │ ├── NodeList.ts
│ │ ├── orderBookLevels.ts
│ │ └── types.ts
│ ├── driftClient.ts
│ ├── driftClientConfig.ts
│ ├── events
│ │ ├── eventList.ts
│ │ ├── eventSubscriber.ts
│ │ ├── eventsServerLogProvider.ts
│ │ ├── fetchLogs.ts
│ │ ├── parse.ts
│ │ ├── pollingLogProvider.ts
│ │ ├── sort.ts
│ │ ├── txEventCache.ts
│ │ ├── types.ts
│ │ └── webSocketLogProvider.ts
│ ├── factory
│ │ ├── bigNum.ts
│ │ └── oracleClient.ts
│ ├── idl
│ │ ├── drift.json
│ │ ├── openbook.json
│ │ ├── pyth.json
│ │ ├── pyth_solana_receiver.json
│ │ ├── switchboard.json
│ │ ├── switchboard_on_demand_30.json
│ │ └── token_faucet.json
│ ├── index.ts
│ ├── indicative-quotes
│ │ └── indicativeQuotesSender.ts
│ ├── isomorphic
│ │ ├── README.md
│ │ ├── grpc.browser.ts
│ │ ├── grpc.node.ts
│ │ └── grpc.ts
│ ├── jupiter
│ │ └── jupiterClient.ts
│ ├── keypair.ts
│ ├── marinade
│ │ ├── idl
│ │ │ └── idl.json
│ │ ├── index.ts
│ │ └── types.ts
│ ├── math
│ │ ├── amm.ts
│ │ ├── auction.ts
│ │ ├── bankruptcy.ts
│ │ ├── conversion.ts
│ │ ├── exchangeStatus.ts
│ │ ├── fuel.ts
│ │ ├── funding.ts
│ │ ├── insurance.ts
│ │ ├── liquidation.ts
│ │ ├── margin.ts
│ │ ├── market.ts
│ │ ├── oracles.ts
│ │ ├── orders.ts
│ │ ├── position.ts
│ │ ├── protectedMakerParams.ts
│ │ ├── repeg.ts
│ │ ├── spotBalance.ts
│ │ ├── spotMarket.ts
│ │ ├── spotPosition.ts
│ │ ├── state.ts
│ │ ├── superStake.ts
│ │ ├── tiers.ts
│ │ ├── trade.ts
│ │ ├── userStatus.ts
│ │ └── utils.ts
│ ├── memcmp.ts
│ ├── openbook
│ │ ├── openbookV2FulfillmentConfigMap.ts
│ │ └── openbookV2Subscriber.ts
│ ├── oracles
│ │ ├── oracleClientCache.ts
│ │ ├── oracleId.ts
│ │ ├── prelaunchOracleClient.ts
│ │ ├── pythClient.ts
│ │ ├── pythLazerClient.ts
│ │ ├── pythPullClient.ts
│ │ ├── quoteAssetOracleClient.ts
│ │ ├── strictOraclePrice.ts
│ │ ├── switchboardClient.ts
│ │ ├── switchboardOnDemandClient.ts
│ │ └── types.ts
│ ├── orderParams.ts
│ ├── orderSubscriber
│ │ ├── OrderSubscriber.ts
│ │ ├── PollingSubscription.ts
│ │ ├── WebsocketSubscription.ts
│ │ ├── grpcSubscription.ts
│ │ ├── index.ts
│ │ └── types.ts
│ ├── phoenix
│ │ ├── phoenixFulfillmentConfigMap.ts
│ │ └── phoenixSubscriber.ts
│ ├── priorityFee
│ │ ├── averageOverSlotsStrategy.ts
│ │ ├── averageStrategy.ts
│ │ ├── driftPriorityFeeMethod.ts
│ │ ├── ewmaStrategy.ts
│ │ ├── heliusPriorityFeeMethod.ts
│ │ ├── index.ts
│ │ ├── maxOverSlotsStrategy.ts
│ │ ├── maxStrategy.ts
│ │ ├── priorityFeeSubscriber.ts
│ │ ├── priorityFeeSubscriberMap.ts
│ │ ├── solanaPriorityFeeMethod.ts
│ │ └── types.ts
│ ├── serum
│ │ ├── serumFulfillmentConfigMap.ts
│ │ ├── serumSubscriber.ts
│ │ └── types.ts
│ ├── slot
│ │ ├── SlotSubscriber.ts
│ │ └── SlothashSubscriber.ts
│ ├── swift
│ │ ├── grpcSignedMsgUserAccountSubscriber.ts
│ │ ├── index.ts
│ │ ├── signedMsgUserAccountSubscriber.ts
│ │ └── swiftOrderSubscriber.ts
│ ├── testClient.ts
│ ├── token
│ │ └── index.ts
│ ├── tokenFaucet.ts
│ ├── tx
│ │ ├── baseTxSender.ts
│ │ ├── blockhashFetcher
│ │ │ ├── baseBlockhashFetcher.ts
│ │ │ ├── cachedBlockhashFetcher.ts
│ │ │ └── types.ts
│ │ ├── fastSingleTxSender.ts
│ │ ├── forwardOnlyTxSender.ts
│ │ ├── priorityFeeCalculator.ts
│ │ ├── reportTransactionError.ts
│ │ ├── retryTxSender.ts
│ │ ├── txHandler.ts
│ │ ├── txParamProcessor.ts
│ │ ├── types.ts
│ │ ├── utils.ts
│ │ └── whileValidTxSender.ts
│ ├── types.ts
│ ├── user.ts
│ ├── userConfig.ts
│ ├── userMap
│ │ ├── PollingSubscription.ts
│ │ ├── WebsocketSubscription.ts
│ │ ├── grpcSubscription.ts
│ │ ├── referrerMap.ts
│ │ ├── userMap.ts
│ │ ├── userMapConfig.ts
│ │ └── userStatsMap.ts
│ ├── userName.ts
│ ├── userStats.ts
│ ├── userStatsConfig.ts
│ ├── util
│ │ ├── TransactionConfirmationManager.ts
│ │ ├── chainClock.ts
│ │ ├── computeUnits.ts
│ │ ├── digest.ts
│ │ ├── ed25519Utils.ts
│ │ ├── promiseTimeout.ts
│ │ ├── pythOracleUtils.ts
│ │ └── tps.ts
│ └── wallet.ts
├── tests
│ ├── amm
│ │ └── test.ts
│ ├── auctions
│ │ └── test.ts
│ ├── bn
│ │ └── test.ts
│ ├── ci
│ │ └── verifyConstants.ts
│ ├── decode
│ │ ├── phoenix.ts
│ │ ├── test.ts
│ │ └── userAccountBufferStrings.ts
│ ├── dlob
│ │ ├── helpers.ts
│ │ └── test.ts
│ ├── insurance
│ │ └── test.ts
│ ├── spot
│ │ └── test.ts
│ ├── subscriber
│ │ └── openbook.ts
│ ├── tx
│ │ ├── TransactionConfirmationManager.test.ts
│ │ ├── cachedBlockhashFetcher.test.ts
│ │ ├── priorityFeeCalculator.ts
│ │ └── priorityFeeStrategy.ts
│ └── user
│ │ ├── helpers.ts
│ │ └── test.ts
├── tsconfig.browser.json
├── tsconfig.json
└── yarn.lock
├── test-scripts
├── run-anchor-local-validator-tests.sh
├── run-anchor-tests.sh
├── run-ts-mocha
└── single-anchor-test.sh
├── tests
├── admin.ts
├── adminDeposit.ts
├── assetTier.ts
├── cancelAllOrders.ts
├── cappedSymFunding.ts
├── curve.ts
├── decodeUser.ts
├── deleteInitializedSpotMarket.ts
├── delistMarket.ts
├── delistMarketLiq.ts
├── depositIntoSpotMarketVault.ts
├── driftClient.ts
├── fillSpot.ts
├── fixtures
│ ├── openbook.so
│ ├── phoenix_dex.so
│ ├── pyth_solana_receiver.so
│ └── serum_dex.so
├── forceUserDelete.ts
├── fuel.ts
├── fuelSweep.ts
├── govStakeDevnet.ts
├── highLeverageMode.ts
├── imbalancePerpPnl.ts
├── insuranceFundStake.ts
├── ksolver.ts
├── liquidateBorrowForPerpPnl.ts
├── liquidateMaxLps.ts
├── liquidatePerp.ts
├── liquidatePerpAndLp.ts
├── liquidatePerpPnlForDeposit.ts
├── liquidatePerpWithFill.ts
├── liquidateSpot.ts
├── liquidateSpotSocialLoss.ts
├── liquidateSpotWithSwap.ts
├── liquidityBook.ts
├── liquidityProvider.ts
├── marketOrder.ts
├── marketOrderBaseAssetAmount.ts
├── maxDeposit.ts
├── maxLeverageOrderParams.ts
├── modifyOrder.ts
├── multipleMakerOrders.ts
├── multipleSpotMakerOrders.ts
├── openbookHelpers.ts
├── openbookTest.ts
├── oracleDiffSources.ts
├── oracleFillPriceGuardrails.ts
├── oracleOffsetOrders.ts
├── order.ts
├── ordersWithSpread.ts
├── pauseDepositWithdraw.ts
├── pauseExchange.ts
├── perpLpJit.ts
├── perpLpRiskMitigation.ts
├── phoenixTest.ts
├── phoenixTestAccountData.ts
├── placeAndMakePerp.ts
├── placeAndMakeSignedMsg.ts
├── placeAndMakeSignedMsgBankrun.ts
├── placeAndMakeSpotOrder.ts
├── postOnly.ts
├── postOnlyAmmFulfillment.ts
├── prelisting.ts
├── prepegMarketOrderBaseAssetAmount.ts
├── pyth.ts
├── pythLazer.ts
├── pythLazerBankrun.ts
├── pythLazerData.ts
├── pythPull.ts
├── pythPullOracleData.ts
├── referrer.ts
├── repegAndSpread.ts
├── resizeSwiftUserOrderIds.ts
├── roundInFavorBaseAsset.ts
├── serumHelper.ts
├── serumTest.ts
├── signedMsgWsDelegates.ts
├── spotDepositWithdraw.ts
├── spotDepositWithdraw22.ts
├── spotMarketPoolIds.ts
├── spotSwap.ts
├── spotSwap22.ts
├── spotWithdrawUtil100.ts
├── stopLimits.ts
├── subaccounts.ts
├── surgePricing.ts
├── switchOracle.ts
├── switchboardOnDemand.ts
├── switchboardOnDemandData.ts
├── switchboardTxCus.ts
├── testHelpers.ts
├── testHelpersLocalValidator.ts
├── tokenFaucet.ts
├── tradingLP.ts
├── transferPerpPosition.ts
├── transferPools.ts
├── triggerOrders.ts
├── triggerSpotOrder.ts
├── updateAMM.ts
├── updateK.ts
├── userAccount.ts
├── userDelegate.ts
├── userOrderId.ts
└── whitelist.ts
├── tsconfig.json
├── typedoc.json
└── yarn.lock
/.cargo/audit.toml:
--------------------------------------------------------------------------------
1 | # RUSTSEC-2022-0013 ignores as upstream dependency
2 |
3 | [advisories]
4 | ignore = [
5 | "RUSTSEC-2022-0013",
6 | "RUSTSEC-2022-0093", # Double Public Key Signing Function Oracle Attack on `ed25519-dalek`
7 | ]
8 | informational_warnings = ["unmaintained"] # warn for categories of informational advisories
9 | severity_threshold = "high" # CVSS severity ("none", "low", "medium", "high", "critical")
10 |
11 | # Advisory Database Configuration
12 | [database]
13 | path = "~/.cargo/advisory-db" # Path where advisory git repo will be cloned
14 | url = "https://github.com/RustSec/advisory-db.git" # URL to git repo
15 | fetch = true # Perform a `git fetch` before auditing (default: true)
16 | stale = false # Allow stale advisory DB (i.e. no commits for 90 days, default: false)
17 |
18 | # Output Configuration
19 | [output]
20 | deny = [] # exit on error if unmaintained dependencies are found
21 | format = "terminal" # "terminal" (human readable report) or "json"
22 | quiet = false # Only print information on error
23 | show_tree = true # Show inverse dependency trees along with advisories (default: true)
24 |
25 | # Target Configuration
26 | [target]
27 | arch = "x86_64" # Ignore advisories for CPU architectures other than this one
28 | os = "linux" # Ignore advisories for operating systems other than this one
29 |
30 | [yanked]
31 | enabled = false # Warn for yanked crates in Cargo.lock (default: true)
32 | update_index = true # Auto-update the crates.io index (default: true)
33 |
--------------------------------------------------------------------------------
/.devcontainer/Dockerfile:
--------------------------------------------------------------------------------
1 | #
2 | # Docker image to generate deterministic, verifiable builds of Anchor programs.
3 | # This must be run *after* a given ANCHOR_CLI version is published and a git tag
4 | # is released on GitHub.
5 | #
6 |
7 | FROM rust:1.75
8 |
9 | ARG DEBIAN_FRONTEND=noninteractive
10 |
11 | ARG SOLANA_CLI="1.14.7"
12 | ARG ANCHOR_CLI="0.26.0"
13 | ARG NODE_VERSION="v18.16.0"
14 |
15 | ENV HOME="/root"
16 | ENV PATH="${HOME}/.cargo/bin:${PATH}"
17 | ENV PATH="${HOME}/.local/share/solana/install/active_release/bin:${PATH}"
18 | ENV PATH="${HOME}/.nvm/versions/node/${NODE_VERSION}/bin:${PATH}"
19 |
20 | # Install base utilities.
21 | RUN mkdir -p /workdir && mkdir -p /tmp && \
22 | apt-get update -qq && apt-get upgrade -qq && apt-get install -qq \
23 | build-essential git curl wget jq pkg-config python3-pip \
24 | libssl-dev libudev-dev
25 |
26 | RUN wget http://nz2.archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2_amd64.deb
27 | RUN dpkg -i libssl1.1_1.1.1f-1ubuntu2_amd64.deb
28 |
29 | # Install rust.
30 | RUN curl "https://sh.rustup.rs" -sfo rustup.sh && \
31 | sh rustup.sh -y && \
32 | rustup component add rustfmt clippy
33 |
34 | # Install node / npm / yarn.
35 | RUN curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash
36 | ENV NVM_DIR="${HOME}/.nvm"
37 | RUN . $NVM_DIR/nvm.sh && \
38 | nvm install ${NODE_VERSION} && \
39 | nvm use ${NODE_VERSION} && \
40 | nvm alias default node && \
41 | npm install -g yarn && \
42 | yarn add ts-mocha
43 |
44 | # Install Solana tools.
45 | RUN sh -c "$(curl -sSfL https://release.solana.com/v${SOLANA_CLI}/install)"
46 |
47 | # Install anchor.
48 | RUN cargo install --git https://github.com/coral-xyz/anchor avm --locked --force
49 | RUN avm install ${ANCHOR_CLI} && avm use ${ANCHOR_CLI}
50 |
51 | RUN solana-keygen new --no-bip39-passphrase
52 |
53 | WORKDIR /workdir
54 | #be sure to add `/root/.avm/bin` to your PATH to be able to run the installed binaries
55 |
--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | {
2 | "build": { "dockerfile": "Dockerfile" },
3 | }
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "root": true,
3 | "parser": "@typescript-eslint/parser",
4 | "env": {
5 | "browser": true,
6 | "node": true
7 | },
8 | "ignorePatterns": ["**/lib", "**/node_modules", "migrations"],
9 | "plugins": [],
10 | "extends": [
11 | "eslint:recommended",
12 | "plugin:@typescript-eslint/eslint-recommended",
13 | "plugin:@typescript-eslint/recommended"
14 | ],
15 | "rules": {
16 | "@typescript-eslint/explicit-function-return-type": "off",
17 | "@typescript-eslint/ban-ts-ignore": "off",
18 | "@typescript-eslint/ban-ts-comment": "off",
19 | "@typescript-eslint/no-explicit-any": "off",
20 | "@typescript-eslint/no-unused-vars": [
21 | 2,
22 | {
23 | "argsIgnorePattern": "^_",
24 | "varsIgnorePattern": "^_"
25 | }
26 | ],
27 | "@typescript-eslint/no-var-requires": 0,
28 | "@typescript-eslint/no-empty-function": 0,
29 | "no-mixed-spaces-and-tabs": [2, "smart-tabs"],
30 | "no-prototype-builtins": "off",
31 | "semi": 2,
32 | "no-restricted-imports": [
33 | "error",
34 | {
35 | "patterns": [
36 | {
37 | // Restrict importing BN from bn.js
38 | "group": ["bn.js"],
39 | "message": "Import BN from @drift-labs/sdk instead",
40 | }
41 | ],
42 | },
43 | ],
44 | }
45 | };
46 |
--------------------------------------------------------------------------------
/.github/actions/setup-solana/action.yaml:
--------------------------------------------------------------------------------
1 | name: "Setup Solana"
2 | description: "Setup Solana"
3 | runs:
4 | using: "composite"
5 | steps:
6 | - uses: actions/cache@v3
7 | name: Cache Solana Tool Suite
8 | id: cache-solana
9 | with:
10 | path: |
11 | ~/.cache/solana/
12 | ~/.local/share/solana/
13 | key: solana-${{ runner.os }}-v0000-${{ env.SOLANA_VERSION }}
14 | - name: Download Solana
15 | run: |
16 | echo Downloading Solana v${{ env.SOLANA_VERSION }}... 🧬
17 | export SOLANA_RELEASE=v${{ env.SOLANA_VERSION }}
18 | export SOLANA_INSTALL_INIT_ARGS=v${{ env.SOLANA_VERSION }}
19 | ${{ github.workspace }}/.github/actions/setup-solana/scripts/solana-install-init.sh
20 | echo "$HOME/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH
21 | export PATH="$HOME/.local/share/solana/install/active_release/bin:$PATH"
22 | echo "[41,242,37,42,13,160,221,13,242,224,230,17,141,228,35,40,57,231,71,8,239,32,226,165,181,216,231,245,170,229,117,123,39,103,128,179,245,168,230,228,127,219,58,249,69,6,251,148,173,190,191,217,50,67,123,105,121,215,242,41,242,85,71,109]" > $HOME/.config/solana/id.json
23 | shell: bash
--------------------------------------------------------------------------------
/.github/workflows/audit.yml:
--------------------------------------------------------------------------------
1 | name: Soteria
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | branches:
9 | - master
10 |
11 | env:
12 | CARGO_TERM_COLOR: always
13 | SOLANA_VERSION: "1.9.5"
14 | PROGRAM_PATH: "programs/drift/"
15 | RUST_TOOLCHAIN: nightly-2022-02-07
16 |
17 | jobs:
18 | build:
19 | name: Soteria Scan
20 | runs-on: ubicloud
21 | steps:
22 | - name: Checkout changes
23 | uses: actions/checkout@v2
24 |
25 | - name: Cache Solana Version
26 | uses: actions/cache@v2
27 | id: solana-cache
28 | with:
29 | path: |
30 | ~/.rustup
31 | ~/.cache/solana
32 | ~/.local/share/solana
33 | key: solana-v${{ env.SOLANA_VERSION }}
34 |
35 | - name: Cache Soteria Build
36 | uses: Swatinem/rust-cache@v1
37 | with:
38 | target-dir: ${{ env.PROGRAM_PATH }}.coderrect/build # Cache build files for performance
39 |
40 | - name: Install Rust nightly
41 | uses: actions-rs/toolchain@v1
42 | with:
43 | toolchain: ${{ env.RUST_TOOLCHAIN }}
44 | profile: minimal
45 | override: true
46 | components: rustfmt, clippy
47 |
48 | - name: Download Solana
49 | if: steps.solana-cache.outputs.cache-hit != 'true' # Skip this step if matched cached version is available
50 | run: |
51 | echo Downloading Solana v${{ env.SOLANA_VERSION }}... 🧬
52 | sh -c "$(curl -sSfL https://release.solana.com/v${{ env.SOLANA_VERSION }}/install)"
53 | echo "$HOME/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH
54 | export PATH="$HOME/.local/share/solana/install/active_release/bin:$PATH"
55 | echo Configuring bpf toolchain...
56 | (cd $HOME/.local/share/solana/install/active_release/bin/sdk/bpf/scripts; ./install.sh)
57 | shell: bash
58 |
59 | - name: Download Soteria # Always grab the latest version
60 | run: |
61 | echo Downloading Soteria... 🔬
62 | sh -c "$(curl -k https://supercompiler.xyz/install)"
63 | export PATH=$PWD/soteria-linux-develop/bin/:$PATH
64 | echo "$PWD/soteria-linux-develop/bin" >> $GITHUB_PATH
65 | shell: bash
66 |
67 | - name: Run Soteria
68 | run: |
69 | echo Running Soteria... 👾
70 | cd ${{ env.PROGRAM_PATH }}
71 | soteria -analyzeAll .
72 | shell: bash
73 |
--------------------------------------------------------------------------------
/.github/workflows/security.yml:
--------------------------------------------------------------------------------
1 | name: Security Scans
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | branches:
9 | - master
10 |
11 | jobs:
12 | monitor:
13 | name: Sync with snyk.io
14 | runs-on: ubicloud
15 | steps:
16 | - uses: actions/checkout@master
17 | - name: Monitor on snyk.io
18 | uses: snyk/actions/node@master
19 | env:
20 | SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
21 | with:
22 | command: monitor --org=${{ secrets.SNYK_ORG }} --all-projects --project-business-criticality=critical --project-lifecycle=production --project-environment=backend
23 | sast:
24 | needs: monitor
25 | name: Code Scan
26 | runs-on: ubicloud
27 | steps:
28 | - name: Checkout changes
29 | uses: actions/checkout@v2
30 |
31 | - uses: snyk/actions/setup@master
32 | - name: Snyk Code Scan
33 | run: snyk code test --org=${{ secrets.SNYK_ORG }} --severity-threshold=medium --sarif-file-output=snyk-sast.json
34 | env:
35 | SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
36 |
37 | - name: Upload code report
38 | if: always()
39 | uses: github/codeql-action/upload-sarif@v1
40 | with:
41 | sarif_file: snyk-sast.json
42 | sca:
43 | needs: monitor
44 | name: Dependency Scan
45 | runs-on: ubicloud
46 | steps:
47 | - name: Checkout changes
48 | uses: actions/checkout@v2
49 |
50 | - name: Snyk Dependency Scan
51 | uses: snyk/actions/node@master
52 | env:
53 | SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
54 | with:
55 | args: --all-projects --org=${{ secrets.SNYK_ORG }} --severity-threshold=medium --fail-on=upgradable --sarif-file-output=snyk-sca.sarif
56 |
57 | - name: Upload dependency report
58 | if: always()
59 | uses: github/codeql-action/upload-sarif@v1
60 | with:
61 | sarif_file: snyk-sca.sarif
62 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /**/target
2 | /**/test-ledger
3 | /**/node_modules
4 | /package-lock.json
5 | /**/output*
6 | .anchor
7 | .idea
8 | yarn-lock
9 | sdk/src/**/*.js
10 | sdk/src/**/*.js.map
11 | .DS_STORE
12 | .vscode
13 | migrations
14 | /**/*.env
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "deps/serum-dex"]
2 | path = deps/serum-dex
3 | url = https://github.com/project-serum/serum-dex.git
4 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | yarn prettify
5 | yarn lint
6 | cd sdk && yarn run test
7 | yarn build
8 |
9 | if [ -z "$TS_PRECOMMIT_ONLY" ] || [ "$TS_PRECOMMIT_ONLY" = "false" ]; then
10 | cargo fmt -- --check
11 | cargo clippy -p drift -- -D warnings -D clippy::unwrap_used -D clippy::expect_used -D clippy::panic
12 | cargo clippy -p drift --tests -- -D warnings
13 | cargo test --quiet
14 | fi
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | **/node_modules/**
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | semi: true,
3 | trailingComma: 'es5',
4 | singleQuote: true,
5 | printWidth: 80,
6 | tabWidth: 2,
7 | useTabs: true,
8 | bracketSameLine: false,
9 | endOfLine: 'auto',
10 | };
11 |
--------------------------------------------------------------------------------
/.verified-build.json:
--------------------------------------------------------------------------------
1 | {
2 | "dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH": ["--commit-hash", "8d2cd726afdc800f89c841ff3cf1968980719df0", "--library-name", "drift"]
3 | }
4 |
--------------------------------------------------------------------------------
/AUDIT.md:
--------------------------------------------------------------------------------
1 | - [Neodyme Audit (Protocol V2)](https://github.com/drift-labs/audits/blob/master/protocol-v2/neodyme.pdf)
2 | - [Trail of Bits Audit (Protocol V2)](https://github.com/drift-labs/audits/blob/master/protocol-v2/tob.pdf)
3 |
--------------------------------------------------------------------------------
/Anchor.toml:
--------------------------------------------------------------------------------
1 | [provider]
2 | cluster = "localnet"
3 | wallet = "~/.config/solana/id.json"
4 |
5 | [workspace]
6 | exclude = ["programs/openbook_v2"]
7 |
8 | [scripts]
9 | # to run local validator tests, use "./test-scripts/run-ts-mocha" in "test"
10 | test = "echo" # need to call anchor test to update metadata field in idl before running tests, so just do a noop
11 | lint = "yarn prettify:fix && cargo fmt"
12 | fulltest = 'cargo test && bash ./test-scripts/run-anchor-tests.sh'
13 | watch_ts = 'find ./programs/clearing_house/src/* ./tests ./sdk/src | entr -c bash ./test-scripts/single-anchor-test.sh'
14 | watch_build = 'find ./programs/clearing_house/src/* ./tests ./sdk/src | entr -c anchor build'
15 | watch_cargo = 'find ./programs/clearing_house/src/* ./tests ./sdk/src | entr -c cargo test -- --show-output'
16 |
17 | [programs.localnet]
18 | drift = "dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH"
19 | pyth = "FsJ3A3u2vn5cTVofAjvy6y5kwABJAqYWpe4975bi2epH"
20 | token_faucet = "V4v1mQiAdLz4qwckEb45WqHYceYizoib39cDBHSWfaB"
21 |
22 | [[test.validator.account]]
23 | address = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
24 | filename = "./deps/configs/usdc.json"
25 |
26 | [[test.validator.account]]
27 | address = "3rdJbqfnagQ4yx9HXJViD4zc4xpiSqmFsKpPuSCQVyQL"
28 | filename = "./deps/configs/pyth_lazer_storage.json"
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | members = [
3 | "programs/*",
4 | ]
5 | exclude = [
6 | "deps/serum-dex"
7 | ]
8 |
9 | [profile.release]
10 | lto = "fat"
11 | codegen-units = 1
12 |
13 | [profile.release.build-override]
14 | opt-level = 3
15 | incremental = false
16 | codegen-units = 1
17 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
%20(1).png)
3 |
4 |
Drift Protocol v2
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | # Drift Protocol v2
14 |
15 | This repository provides open source access to Drift V2's Typescript SDK, Solana Programs, and more.
16 |
17 | Integrating Drift? [Go here](./sdk/README.md)
18 |
19 | # SDK Guide
20 |
21 | SDK docs can be found [here](./sdk/README.md)
22 |
23 | # Example Bot Implementations
24 |
25 | Example bots (makers, liquidators, fillers, etc) can be found [here](https://github.com/drift-labs/keeper-bots-v2)
26 |
27 | # Building Locally
28 |
29 | Note: If you are running the build on an Apple computer with an M1 chip, please set the default rust toolchain to `stable-x86_64-apple-darwin`
30 |
31 | ```bash
32 | rustup default stable-x86_64-apple-darwin
33 | ```
34 |
35 | ## Compiling Programs
36 |
37 | ```bash
38 | # build v2
39 | anchor build
40 | # install packages
41 | yarn
42 | # build sdk
43 | cd sdk/ && yarn && yarn build && cd ..
44 | ```
45 |
46 | ## Running Rust Test
47 |
48 | ```bash
49 | cargo test
50 | ```
51 |
52 | ## Running Javascript Tests
53 |
54 | ```bash
55 | bash test-scripts/run-anchor-tests.sh
56 | ```
57 |
58 | # Bug Bounty
59 |
60 | Information about the Bug Bounty can be found [here](./bug-bounty/README.md)
61 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Bug Bounty Overview
2 | Drift offers bug bounties for Drift's on-chain program code; UI only bugs are omitted.
3 |
4 | |Severity|Description|Bug Bounty|
5 | |-----------|--------------|-------------|
6 | |Critical|Bugs that freeze user funds or drain the contract's holdings or involve theft of funds without user signatures|10% of the value of the hack up to $500,000|
7 | |High|Bugs that could temporarily freeze user funds or incorrectly assign value to user funds|$10,000 to $50,000 per bug, assessed on a case by case basis|
8 | |Medium/Low|Bugs that don't threaten user funds|$1,000 to $5,000 per bug, assessed on a case by case basis|
9 |
10 | The severity guidelines are based on [Immunefi's classification system](https://immunefi.com/severity-updated/).
11 | Note that these are simply guidelines for the severity of the bugs. Each bug bounty submission will be evaluated on a case-by-case basis.
12 |
13 | ## Submission
14 | Please email hello@drift.trade with a detailed description of the attack vector. For critical and moderate bugs, we require a proof of concept done on a privately deployed mainnet contract. We will reach back out in 1 business day with additional questions or the next steps on the bug bounty.
15 |
16 | ## Bug Bounty Payment
17 | Bug bounties will be paid in USDC. Alternative payment methods can be used on a case-by-case basis.
18 |
19 | ## Invalid Bug Bounties
20 | The following are out of scope for the bug bounty:
21 | 1. Attacks that the reporter has already exploited themselves, leading to damage.
22 | 2. Attacks requiring access to leaked keys/credentials.
23 | 3. Attacks requiring access to privileged addresses (governance, admin).
24 | 4. Incorrect data supplied by third party oracles (this does not exclude oracle manipulation/flash loan attacks).
25 | 5. Lack of liquidity.
26 | 6. Third party, off-chain bot errors (for instance bugs with an arbitrage bot running on the smart contracts).
27 | 7. Best practice critiques.
28 | 8. Sybil attacks.
29 | 9. Attempted phishing or other social engineering attacks involving Drift contributors or users
30 | 10. Denial of service, or automated testing of services that generate significant traffic.
31 | 11. Any submission violating [Immunefi's rules](https://immunefi.com/rules/)
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/bug-bounty/README.md:
--------------------------------------------------------------------------------
1 | # Bug Bounty for v2 (coming soon)
2 |
--------------------------------------------------------------------------------
/cli/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 | node_modules
--------------------------------------------------------------------------------
/cli/README.md:
--------------------------------------------------------------------------------
1 | ## Installation
2 | ```shell
3 | yarn &&
4 | tsc &&
5 | npm link &&
6 | drift config init
7 | ```
--------------------------------------------------------------------------------
/cli/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "drift-v1-cli",
3 | "version": "0.0.0",
4 | "description": "",
5 | "bin": {
6 | "drift": "./build/cli.js"
7 | },
8 | "scripts": {},
9 | "author": "crispheaney",
10 | "license": "ISC",
11 | "dependencies": {
12 | "@drift-labs/sdk": "file:../sdk",
13 | "@project-serum/anchor": "0.24.2",
14 | "@solana/spl-token": "^0.1.8",
15 | "@solana/web3.js": "^1.30.2",
16 | "@types/node": "^16.7.10",
17 | "colors": "^1.4.0",
18 | "commander": "8.1.0",
19 | "loglevel": "^1.7.1",
20 | "promptly": "^3.2.0"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/cli/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "target": "es6",
5 | "strict": true,
6 | "esModuleInterop": true,
7 | "skipLibCheck": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "outDir": "./build",
10 | "resolveJsonModule": true
11 | }
12 | }
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | comment:
2 | layout: "header, diff, flags, components"
3 |
4 | component_management:
5 | individual_components:
6 | - component_id: drift
7 | name: drift
8 | paths:
9 | - programs/drift
--------------------------------------------------------------------------------
/deploy-scripts/build-devnet.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | anchor build -- --no-default-features
--------------------------------------------------------------------------------
/deploy-scripts/deploy-devnet.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | anchor upgrade --program-id dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH --provider.cluster devnet --provider.wallet $SOLANA_PATH/$DEVNET_ADMIN target/deploy/drift.so
--------------------------------------------------------------------------------
/deploy-scripts/verified-build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | solana-verify build --library-name drift
--------------------------------------------------------------------------------
/deps/configs/pyth_lazer_storage.json:
--------------------------------------------------------------------------------
1 | {
2 | "pubkey": "3rdJbqfnagQ4yx9HXJViD4zc4xpiSqmFsKpPuSCQVyQL",
3 | "account": {
4 | "lamports": 1461600,
5 | "data": [
6 | "0XX/ucSvRAkL/td28gTUmmjn6CkzKyvYXJOMcup4pEKu3cXcP7cvDAv+13byBNSaaOfoKTMrK9hck4xy6nikQq7dxdw/ty8MAQAAAAAAAAAB9lIQvuT89bHO4eU3+rz9lQECl2U7lK8E1FT8Rz6Ug0/oNgZ6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
7 | "base64"
8 | ],
9 | "owner": "pytd2yyk641x7ak7mkaasSJVXh6YYZnC7wTmtgAyxPt",
10 | "executable": false,
11 | "rentEpoch": 367
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/deps/configs/usdc.json:
--------------------------------------------------------------------------------
1 | {
2 | "pubkey": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
3 | "account": {
4 | "lamports": 1461600,
5 | "data": [
6 | "AQAAANuZX+JRadFByrm7upK6oB+fLh7OffTLKsBRkPN/zB+dAAAAAAAAAAAGAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
7 | "base64"
8 | ],
9 | "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
10 | "executable": false,
11 | "rentEpoch": 367
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "directories": {
3 | "test": "tests"
4 | },
5 | "author": "",
6 | "license": "ISC",
7 | "devDependencies": {
8 | "@coral-xyz/anchor": "0.29.0",
9 | "@coral-xyz/anchor-30": "npm:@coral-xyz/anchor@0.30.1",
10 | "@project-serum/common": "0.0.1-beta.3",
11 | "@project-serum/serum": "0.13.65",
12 | "@pythnetwork/client": "2.21.0",
13 | "@solana/spl-token": "0.3.7",
14 | "@solana/web3.js": "1.73.2",
15 | "@types/bn.js": "5.1.6",
16 | "@types/chai": "5.0.0",
17 | "@types/mocha": "8.2.3",
18 | "@typescript-eslint/eslint-plugin": "4.33.0",
19 | "@typescript-eslint/parser": "4.33.0",
20 | "chai": "4.4.1",
21 | "eslint": "7.32.0",
22 | "eslint-config-prettier": "8.3.0",
23 | "eslint-plugin-prettier": "3.4.0",
24 | "husky": "7.0.4",
25 | "prettier": "3.0.1",
26 | "typedoc": "0.23.23",
27 | "typescript": "4.9.5",
28 | "@pythnetwork/price-service-client": "1.9.0"
29 | },
30 | "dependencies": {
31 | "@ellipsis-labs/phoenix-sdk": "1.4.2",
32 | "@pythnetwork/pyth-solana-receiver": "0.8.0",
33 | "@switchboard-xyz/on-demand": "2.4.1",
34 | "@switchboard-xyz/common": "3.0.14",
35 | "anchor-bankrun": "0.3.0",
36 | "chai-bn": "0.2.2",
37 | "csvtojson": "2.0.10",
38 | "dotenv": "16.4.5",
39 | "json2csv": "5.0.7",
40 | "nanoid": "3.3.4",
41 | "rpc-websockets": "7.5.1",
42 | "solana-bankrun": "0.3.0",
43 | "zstddec": "0.1.0"
44 | },
45 | "scripts": {
46 | "generate-docs": "typedoc --skipErrorChecking --logLevel Error",
47 | "prepare": "husky install",
48 | "prettify": "prettier --check './sdk/src/**/*.ts' './tests/**.ts' './cli/**.ts'",
49 | "prettify:fix": "prettier --write './sdk/src/**/*.ts' './tests/**.ts' './cli/**.ts'",
50 | "lint": "eslint . --ext ts --quiet",
51 | "lint:fix": "eslint . --ext ts --fix",
52 | "update-idl": "cp target/idl/drift.json sdk/src/idl/drift.json"
53 | },
54 | "engines": {
55 | "node": ">=12"
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/programs/drift/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "drift"
3 | version = "2.122.0"
4 | description = "Created with Anchor"
5 | edition = "2018"
6 |
7 | [lib]
8 | crate-type = ["cdylib", "lib"]
9 | name = "drift"
10 | path = "src/lib.rs"
11 |
12 | [features]
13 | no-entrypoint = []
14 | cpi = ["no-entrypoint"]
15 | mainnet-beta=[]
16 | anchor-test= []
17 | default=["mainnet-beta"]
18 | drift-rs=[]
19 |
20 | [dependencies]
21 | anchor-lang = "0.29.0"
22 | solana-program = "1.16"
23 | anchor-spl = "0.29.0"
24 | pyth-client = "0.2.2"
25 | pyth-lazer-solana-contract = { git = "https://github.com/drift-labs/pyth-crosschain", rev = "d790d1cb4da873a949cf33ff70349b7614b232eb", features = ["no-entrypoint"]}
26 | pythnet-sdk = { git = "https://github.com/drift-labs/pyth-crosschain", rev = "3e8a24ecd0bcf22b787313e2020f4186bb22c729"}
27 | pyth-solana-receiver-sdk = { git = "https://github.com/drift-labs/pyth-crosschain", rev = "3e8a24ecd0bcf22b787313e2020f4186bb22c729"}
28 | bytemuck = { version = "1.4.0" }
29 | borsh = "0.10.3"
30 | hex = "0.4.3"
31 | num-traits = "0.2"
32 | uint = { version = "0.9.1", default-features = false }
33 | num-derive = "0.3"
34 | thiserror = "1.0"
35 | num-integer = "0.1.44"
36 | arrayref = "0.3.6"
37 | base64 = "0.13.0"
38 | serum_dex = { git = "https://github.com/project-serum/serum-dex", rev = "85b4f14", version = "0.5.6", features = ["no-entrypoint"] }
39 | enumflags2 = "0.6.4"
40 | phoenix-v1 = { git = "https://github.com/drift-labs/phoenix-v1", rev = "7703c5", version = "0.2.4", features = ["no-entrypoint"] }
41 | solana-security-txt = "1.1.0"
42 | static_assertions = "1.1.0"
43 | drift-macros = { git = "https://github.com/drift-labs/drift-macros.git", rev = "c57d87" }
44 | switchboard = { path = "../switchboard", features = ["no-entrypoint"] }
45 | openbook-v2-light = { path = "../openbook_v2", features = ["no-entrypoint"] }
46 | ahash = "=0.8.6"
47 | switchboard-on-demand = { path = "../switchboard-on-demand", features = ["no-entrypoint"] }
48 | byteorder = "1.4.3"
49 |
50 | [dev-dependencies]
51 | bytes = "1.2.0"
52 | pyth = { path = "../pyth", features = ["no-entrypoint"]}
53 | base64 = "0.13.0"
54 |
--------------------------------------------------------------------------------
/programs/drift/Xargo.toml:
--------------------------------------------------------------------------------
1 | [target.bpfel-unknown-unknown.dependencies.std]
2 | features = []
--------------------------------------------------------------------------------
/programs/drift/src/controller/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod amm;
2 | pub mod funding;
3 | pub mod insurance;
4 | pub mod liquidation;
5 | pub mod lp;
6 | pub mod orders;
7 | pub mod pda;
8 | pub mod pnl;
9 | pub mod position;
10 | pub mod repeg;
11 | pub mod spot_balance;
12 | pub mod spot_position;
13 | pub mod token;
14 |
--------------------------------------------------------------------------------
/programs/drift/src/instructions/mod.rs:
--------------------------------------------------------------------------------
1 | pub use admin::*;
2 | pub use constraints::*;
3 | pub use if_staker::*;
4 | pub use keeper::*;
5 | pub use pyth_lazer_oracle::*;
6 | pub use pyth_pull_oracle::*;
7 | pub use user::*;
8 |
9 | mod admin;
10 | mod constraints;
11 | mod if_staker;
12 | mod keeper;
13 | pub mod optional_accounts;
14 | mod pyth_lazer_oracle;
15 | mod pyth_pull_oracle;
16 | mod user;
17 |
--------------------------------------------------------------------------------
/programs/drift/src/math/amm_jit/tests.rs:
--------------------------------------------------------------------------------
1 | use crate::math::amm_jit::*;
2 | use crate::state::perp_market::AMM;
3 |
4 | #[test]
5 | fn balanced_market_zero_jit() {
6 | let market = PerpMarket {
7 | amm: AMM {
8 | base_asset_amount_with_amm: 0,
9 | amm_jit_intensity: 100,
10 | ..AMM::default_test()
11 | },
12 | ..PerpMarket::default()
13 | };
14 | let jit_base_asset_amount = 100;
15 |
16 | let jit_amount = calculate_clamped_jit_base_asset_amount(
17 | &market,
18 | AMMLiquiditySplit::ProtocolOwned,
19 | jit_base_asset_amount,
20 | )
21 | .unwrap();
22 | assert_eq!(jit_amount, 0);
23 | }
24 |
25 | #[test]
26 | fn balanced_market_zero_intensity() {
27 | let market = PerpMarket {
28 | amm: AMM {
29 | base_asset_amount_with_amm: 100,
30 | amm_jit_intensity: 0,
31 | ..AMM::default_test()
32 | },
33 | ..PerpMarket::default()
34 | };
35 | let jit_base_asset_amount = 100;
36 |
37 | let jit_amount = calculate_clamped_jit_base_asset_amount(
38 | &market,
39 | AMMLiquiditySplit::ProtocolOwned,
40 | jit_base_asset_amount,
41 | )
42 | .unwrap();
43 | assert_eq!(jit_amount, 0);
44 | }
45 |
46 | #[test]
47 | fn balanced_market_full_intensity() {
48 | let market = PerpMarket {
49 | amm: AMM {
50 | base_asset_amount_with_amm: 100,
51 | amm_jit_intensity: 100,
52 | ..AMM::default_test()
53 | },
54 | ..PerpMarket::default()
55 | };
56 | let jit_base_asset_amount = 100;
57 |
58 | let jit_amount = calculate_clamped_jit_base_asset_amount(
59 | &market,
60 | AMMLiquiditySplit::ProtocolOwned,
61 | jit_base_asset_amount,
62 | )
63 | .unwrap();
64 | assert_eq!(jit_amount, 100);
65 | }
66 |
67 | #[test]
68 | fn balanced_market_half_intensity() {
69 | let market = PerpMarket {
70 | amm: AMM {
71 | base_asset_amount_with_amm: 100,
72 | amm_jit_intensity: 50,
73 | ..AMM::default_test()
74 | },
75 | ..PerpMarket::default()
76 | };
77 | let jit_base_asset_amount = 100;
78 |
79 | let jit_amount = calculate_clamped_jit_base_asset_amount(
80 | &market,
81 | AMMLiquiditySplit::ProtocolOwned,
82 | jit_base_asset_amount,
83 | )
84 | .unwrap();
85 | assert_eq!(jit_amount, 50);
86 | }
87 |
--------------------------------------------------------------------------------
/programs/drift/src/math/bankruptcy.rs:
--------------------------------------------------------------------------------
1 | use crate::state::spot_market::SpotBalanceType;
2 | use crate::state::user::User;
3 |
4 | #[cfg(test)]
5 | mod tests;
6 |
7 | pub fn is_user_bankrupt(user: &User) -> bool {
8 | // user is bankrupt iff they have spot liabilities, no spot assets, and no perp exposure
9 |
10 | let mut has_liability = false;
11 |
12 | for spot_position in user.spot_positions.iter() {
13 | if spot_position.scaled_balance > 0 {
14 | match spot_position.balance_type {
15 | SpotBalanceType::Deposit => return false,
16 | SpotBalanceType::Borrow => has_liability = true,
17 | }
18 | }
19 | }
20 |
21 | for perp_position in user.perp_positions.iter() {
22 | if perp_position.base_asset_amount != 0
23 | || perp_position.quote_asset_amount > 0
24 | || perp_position.has_open_order()
25 | || perp_position.is_lp()
26 | {
27 | return false;
28 | }
29 |
30 | if perp_position.quote_asset_amount < 0 {
31 | has_liability = true;
32 | }
33 | }
34 |
35 | has_liability
36 | }
37 |
--------------------------------------------------------------------------------
/programs/drift/src/math/bankruptcy/tests.rs:
--------------------------------------------------------------------------------
1 | use crate::math::bankruptcy::is_user_bankrupt;
2 | use crate::state::spot_market::SpotBalanceType;
3 | use crate::state::user::{PerpPosition, SpotPosition, User};
4 | use crate::test_utils::{get_positions, get_spot_positions};
5 |
6 | #[test]
7 | fn user_has_position_with_base() {
8 | let user = User {
9 | perp_positions: get_positions(PerpPosition {
10 | base_asset_amount: 1,
11 | ..PerpPosition::default()
12 | }),
13 | ..User::default()
14 | };
15 |
16 | let is_bankrupt = is_user_bankrupt(&user);
17 | assert!(!is_bankrupt);
18 | }
19 |
20 | #[test]
21 | fn user_has_position_with_positive_quote() {
22 | let user = User {
23 | perp_positions: get_positions(PerpPosition {
24 | quote_asset_amount: 1,
25 | ..PerpPosition::default()
26 | }),
27 | ..User::default()
28 | };
29 |
30 | let is_bankrupt = is_user_bankrupt(&user);
31 | assert!(!is_bankrupt);
32 | }
33 |
34 | #[test]
35 | fn user_with_deposit() {
36 | let user = User {
37 | spot_positions: get_spot_positions(SpotPosition {
38 | balance_type: SpotBalanceType::Deposit,
39 | scaled_balance: 1,
40 | ..SpotPosition::default()
41 | }),
42 | ..User::default()
43 | };
44 |
45 | let is_bankrupt = is_user_bankrupt(&user);
46 | assert!(!is_bankrupt);
47 | }
48 |
49 | #[test]
50 | fn user_has_position_with_negative_quote() {
51 | let user = User {
52 | perp_positions: get_positions(PerpPosition {
53 | quote_asset_amount: -1,
54 | ..PerpPosition::default()
55 | }),
56 | ..User::default()
57 | };
58 |
59 | let is_bankrupt = is_user_bankrupt(&user);
60 | assert!(is_bankrupt);
61 | }
62 |
63 | #[test]
64 | fn user_with_borrow() {
65 | let user = User {
66 | spot_positions: get_spot_positions(SpotPosition {
67 | balance_type: SpotBalanceType::Borrow,
68 | scaled_balance: 1,
69 | ..SpotPosition::default()
70 | }),
71 | ..User::default()
72 | };
73 |
74 | let is_bankrupt = is_user_bankrupt(&user);
75 | assert!(is_bankrupt);
76 | }
77 |
78 | #[test]
79 | fn user_with_empty_position_and_balances() {
80 | let user = User::default();
81 | let is_bankrupt = is_user_bankrupt(&user);
82 | assert!(!is_bankrupt);
83 | }
84 |
--------------------------------------------------------------------------------
/programs/drift/src/math/casting.rs:
--------------------------------------------------------------------------------
1 | use crate::error::{DriftResult, ErrorCode};
2 | use crate::math::bn::U192;
3 | use crate::msg;
4 | use std::convert::TryInto;
5 | use std::panic::Location;
6 |
7 | pub trait Cast: Sized {
8 | #[track_caller]
9 | #[inline(always)]
10 | fn cast>(self) -> DriftResult {
11 | match self.try_into() {
12 | Ok(result) => Ok(result),
13 | Err(_) => {
14 | let caller = Location::caller();
15 | msg!(
16 | "Casting error thrown at {}:{}",
17 | caller.file(),
18 | caller.line()
19 | );
20 | Err(ErrorCode::CastingFailure)
21 | }
22 | }
23 | }
24 | }
25 |
26 | impl Cast for U192 {}
27 | impl Cast for u128 {}
28 | impl Cast for u64 {}
29 | impl Cast for u32 {}
30 | impl Cast for u16 {}
31 | impl Cast for u8 {}
32 | impl Cast for usize {}
33 | impl Cast for i128 {}
34 | impl Cast for i64 {}
35 | impl Cast for i32 {}
36 | impl Cast for i16 {}
37 | impl Cast for i8 {}
38 | impl Cast for bool {}
39 |
--------------------------------------------------------------------------------
/programs/drift/src/math/ceil_div.rs:
--------------------------------------------------------------------------------
1 | use crate::math::bn::{U192, U256};
2 | use num_traits::{One, Zero};
3 |
4 | pub trait CheckedCeilDiv: Sized {
5 | /// Perform ceiling division
6 | fn checked_ceil_div(&self, rhs: Self) -> Option;
7 | }
8 |
9 | macro_rules! checked_impl {
10 | ($t:ty) => {
11 | impl CheckedCeilDiv for $t {
12 | #[track_caller]
13 | #[inline]
14 | fn checked_ceil_div(&self, rhs: $t) -> Option<$t> {
15 | let quotient = self.checked_div(rhs)?;
16 |
17 | let remainder = self.checked_rem(rhs)?;
18 |
19 | if remainder > <$t>::zero() {
20 | quotient.checked_add(<$t>::one())
21 | } else {
22 | Some(quotient)
23 | }
24 | }
25 | }
26 | };
27 | }
28 |
29 | checked_impl!(U256);
30 | checked_impl!(U192);
31 | checked_impl!(u128);
32 | checked_impl!(u64);
33 | checked_impl!(u32);
34 | checked_impl!(u16);
35 | checked_impl!(u8);
36 | checked_impl!(i128);
37 | checked_impl!(i64);
38 | checked_impl!(i32);
39 | checked_impl!(i16);
40 | checked_impl!(i8);
41 |
--------------------------------------------------------------------------------
/programs/drift/src/math/floor_div.rs:
--------------------------------------------------------------------------------
1 | use num_traits::{One, Zero};
2 |
3 | pub trait CheckedFloorDiv: Sized {
4 | /// Perform floor division
5 | fn checked_floor_div(&self, rhs: Self) -> Option;
6 | }
7 |
8 | macro_rules! checked_impl {
9 | ($t:ty) => {
10 | impl CheckedFloorDiv for $t {
11 | #[track_caller]
12 | #[inline]
13 | fn checked_floor_div(&self, rhs: $t) -> Option<$t> {
14 | let quotient = self.checked_div(rhs)?;
15 |
16 | let remainder = self.checked_rem(rhs)?;
17 |
18 | if remainder != <$t>::zero() {
19 | quotient.checked_sub(<$t>::one())
20 | } else {
21 | Some(quotient)
22 | }
23 | }
24 | }
25 | };
26 | }
27 |
28 | checked_impl!(i128);
29 | checked_impl!(i64);
30 | checked_impl!(i32);
31 | checked_impl!(i16);
32 | checked_impl!(i8);
33 |
34 | #[cfg(test)]
35 | mod test {
36 | use crate::math::floor_div::CheckedFloorDiv;
37 |
38 | #[test]
39 | fn test() {
40 | let x = -3_i128;
41 |
42 | assert_eq!(x.checked_floor_div(2), Some(-2));
43 | assert_eq!(x.checked_floor_div(0), None);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/programs/drift/src/math/fuel.rs:
--------------------------------------------------------------------------------
1 | use crate::error::DriftResult;
2 | use crate::math::casting::Cast;
3 | use crate::math::safe_math::SafeMath;
4 | use crate::state::perp_market::PerpMarket;
5 | use crate::state::spot_market::SpotMarket;
6 | use crate::{FUEL_WINDOW_U128, QUOTE_PRECISION, QUOTE_PRECISION_U64};
7 |
8 | #[cfg(test)]
9 | mod tests;
10 |
11 | pub fn calculate_perp_fuel_bonus(
12 | perp_market: &PerpMarket,
13 | base_asset_value: i128,
14 | fuel_bonus_numerator: i64,
15 | ) -> DriftResult {
16 | let result: u64 = if base_asset_value.unsigned_abs() < QUOTE_PRECISION {
17 | 0_u64
18 | } else {
19 | base_asset_value
20 | .unsigned_abs()
21 | .safe_mul(fuel_bonus_numerator.cast()?)?
22 | .safe_mul(perp_market.fuel_boost_position.cast()?)?
23 | .safe_div(FUEL_WINDOW_U128)?
24 | .cast::()?
25 | / (QUOTE_PRECISION_U64 / 10)
26 | };
27 |
28 | Ok(result)
29 | }
30 |
31 | pub fn calculate_spot_fuel_bonus(
32 | spot_market: &SpotMarket,
33 | signed_token_value: i128,
34 | fuel_bonus_numerator: i64,
35 | ) -> DriftResult {
36 | let result: u64 = if signed_token_value.unsigned_abs() < QUOTE_PRECISION {
37 | 0_u64
38 | } else if signed_token_value > 0 {
39 | signed_token_value
40 | .unsigned_abs()
41 | .safe_mul(fuel_bonus_numerator.cast()?)?
42 | .safe_mul(spot_market.fuel_boost_deposits.cast()?)?
43 | .safe_div(FUEL_WINDOW_U128)?
44 | .cast::()?
45 | / (QUOTE_PRECISION_U64 / 10)
46 | } else {
47 | signed_token_value
48 | .unsigned_abs()
49 | .safe_mul(fuel_bonus_numerator.cast()?)?
50 | .safe_mul(spot_market.fuel_boost_borrows.cast()?)?
51 | .safe_div(FUEL_WINDOW_U128)?
52 | .cast::()?
53 | / (QUOTE_PRECISION_U64 / 10)
54 | };
55 |
56 | Ok(result)
57 | }
58 |
59 | pub fn calculate_insurance_fuel_bonus(
60 | spot_market: &SpotMarket,
61 | stake_amount: u64,
62 | stake_amount_delta: i64,
63 | fuel_bonus_numerator: u32,
64 | ) -> DriftResult {
65 | Ok(stake_amount
66 | .saturating_sub(stake_amount_delta.unsigned_abs())
67 | .cast::()?
68 | .safe_mul(fuel_bonus_numerator.cast()?)?
69 | .safe_mul(spot_market.fuel_boost_insurance.cast()?)?
70 | .safe_div(FUEL_WINDOW_U128)?
71 | .cast::()?
72 | / (QUOTE_PRECISION_U64 / 10))
73 | }
74 |
--------------------------------------------------------------------------------
/programs/drift/src/math/fuel/tests.rs:
--------------------------------------------------------------------------------
1 | mod calculate_perp_fuel_bonus {
2 | use crate::math::fuel::calculate_perp_fuel_bonus;
3 | use crate::state::perp_market::PerpMarket;
4 | use crate::{FUEL_WINDOW_U128, QUOTE_PRECISION_I128};
5 |
6 | #[test]
7 | fn test() {
8 | let perp_market = PerpMarket {
9 | fuel_boost_position: 1,
10 | ..PerpMarket::default()
11 | };
12 | let bonus =
13 | calculate_perp_fuel_bonus(&perp_market, QUOTE_PRECISION_I128, FUEL_WINDOW_U128 as i64)
14 | .unwrap();
15 | assert_eq!(bonus, 10);
16 | }
17 | }
18 |
19 | mod calculate_spot_fuel_bonus {
20 | use crate::math::fuel::calculate_spot_fuel_bonus;
21 |
22 | use crate::state::spot_market::SpotMarket;
23 | use crate::{FUEL_WINDOW_U128, QUOTE_PRECISION_I128};
24 |
25 | #[test]
26 | fn test() {
27 | let mut spot_market = SpotMarket {
28 | fuel_boost_deposits: 1,
29 | ..SpotMarket::default()
30 | };
31 | let bonus =
32 | calculate_spot_fuel_bonus(&spot_market, QUOTE_PRECISION_I128, FUEL_WINDOW_U128 as i64)
33 | .unwrap();
34 | assert_eq!(bonus, 10);
35 |
36 | spot_market.fuel_boost_borrows = 1;
37 |
38 | let bonus =
39 | calculate_spot_fuel_bonus(&spot_market, -QUOTE_PRECISION_I128, FUEL_WINDOW_U128 as i64)
40 | .unwrap();
41 | assert_eq!(bonus, 10);
42 | }
43 | }
44 |
45 | mod calculate_insurance_fuel_bonus {
46 | use crate::math::fuel::calculate_insurance_fuel_bonus;
47 |
48 | use crate::state::spot_market::SpotMarket;
49 | use crate::{FUEL_WINDOW_U128, QUOTE_PRECISION_U64};
50 |
51 | #[test]
52 | fn test() {
53 | let spot_market = SpotMarket {
54 | fuel_boost_insurance: 1,
55 | ..SpotMarket::default()
56 | };
57 | let bonus = calculate_insurance_fuel_bonus(
58 | &spot_market,
59 | QUOTE_PRECISION_U64,
60 | 0,
61 | FUEL_WINDOW_U128 as u32,
62 | )
63 | .unwrap();
64 | assert_eq!(bonus, 10);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/programs/drift/src/math/helpers/tests.rs:
--------------------------------------------------------------------------------
1 | use crate::math::helpers::*;
2 |
3 | #[test]
4 | pub fn log_test() {
5 | assert_eq!(log10_iter(0), 0);
6 | assert_eq!(log10(0), 0);
7 |
8 | assert_eq!(log10_iter(9), 0);
9 | assert_eq!(log10(9), 0);
10 |
11 | assert_eq!(log10(19), 1);
12 | assert_eq!(log10_iter(19), 1);
13 |
14 | assert_eq!(log10_iter(13432429), 7);
15 |
16 | assert_eq!(log10(100), 2);
17 | assert_eq!(log10_iter(100), 2);
18 |
19 | // no modify check
20 | let n = 1005325523;
21 | assert_eq!(log10_iter(n), 9);
22 | assert_eq!(log10_iter(n), 9);
23 | assert_eq!(log10(n), 9);
24 | assert_eq!(log10_iter(n), 9);
25 | }
26 |
27 | #[test]
28 | fn proportion_tests() {
29 | let result = get_proportion_i128(999999999369, 1000000036297, 1000000042597).unwrap();
30 | assert_eq!(result, 999999993069);
31 | let result = get_proportion_u128(999999999369, 1000000036297, 1000000042597).unwrap();
32 | assert_eq!(result, 999999993069);
33 | let result = get_proportion_u128(1000000036297, 999999999369, 1000000042597).unwrap();
34 | assert_eq!(result, 999999993069);
35 |
36 | let result = get_proportion_u128(999999999369, 1000000042597, 1000000036297).unwrap();
37 | assert_eq!(result, 1000000005668);
38 | let result = get_proportion_u128(1000000042597, 999999999369, 1000000036297).unwrap();
39 | assert_eq!(result, 1000000005668);
40 | }
41 |
--------------------------------------------------------------------------------
/programs/drift/src/math/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod amm;
2 | pub mod amm_jit;
3 | pub mod amm_spread;
4 | pub mod auction;
5 | pub mod bankruptcy;
6 | pub mod bn;
7 | pub mod casting;
8 | pub mod ceil_div;
9 | pub mod constants;
10 | pub mod cp_curve;
11 | pub mod fees;
12 | mod floor_div;
13 | pub mod fuel;
14 | pub mod fulfillment;
15 | pub mod funding;
16 | pub mod helpers;
17 | pub mod insurance;
18 | pub mod liquidation;
19 | pub mod lp;
20 | pub mod margin;
21 | pub mod matching;
22 | pub mod oracle;
23 | pub mod orders;
24 | pub mod pnl;
25 | pub mod position;
26 | pub mod quote_asset;
27 | pub mod repeg;
28 | pub mod safe_math;
29 | pub mod safe_unwrap;
30 | pub mod serum;
31 | pub mod spot_balance;
32 | pub mod spot_swap;
33 | pub mod spot_withdraw;
34 | pub mod stats;
35 |
--------------------------------------------------------------------------------
/programs/drift/src/math/pnl.rs:
--------------------------------------------------------------------------------
1 | use crate::controller::amm::SwapDirection;
2 | use crate::error::DriftResult;
3 | use crate::math::casting::Cast;
4 | use crate::math::safe_math::SafeMath;
5 |
6 | pub fn calculate_pnl(
7 | exit_value: u128,
8 | entry_value: u128,
9 | swap_direction_to_close: SwapDirection,
10 | ) -> DriftResult {
11 | match swap_direction_to_close {
12 | SwapDirection::Add => exit_value.cast::()?.safe_sub(entry_value.cast()?),
13 | SwapDirection::Remove => entry_value.cast::()?.safe_sub(exit_value.cast()?),
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/programs/drift/src/math/quote_asset.rs:
--------------------------------------------------------------------------------
1 | use crate::error::DriftResult;
2 | use crate::math::constants::AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO;
3 | use crate::math::safe_math::SafeMath;
4 |
5 | use std::ops::Div;
6 |
7 | pub fn reserve_to_asset_amount(
8 | quote_asset_reserve: u128,
9 | peg_multiplier: u128,
10 | ) -> DriftResult {
11 | Ok(quote_asset_reserve
12 | .safe_mul(peg_multiplier)?
13 | .div(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO))
14 | }
15 |
16 | pub fn asset_to_reserve_amount(
17 | quote_asset_amount: u128,
18 | peg_multiplier: u128,
19 | ) -> DriftResult {
20 | Ok(quote_asset_amount
21 | .safe_mul(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO)?
22 | .div(peg_multiplier))
23 | }
24 |
--------------------------------------------------------------------------------
/programs/drift/src/math/safe_unwrap.rs:
--------------------------------------------------------------------------------
1 | use crate::error::{DriftResult, ErrorCode};
2 | use crate::msg;
3 | use std::panic::Location;
4 |
5 | pub trait SafeUnwrap {
6 | type Item;
7 |
8 | fn safe_unwrap(self) -> DriftResult;
9 | }
10 |
11 | impl SafeUnwrap for Option {
12 | type Item = T;
13 |
14 | #[track_caller]
15 | #[inline(always)]
16 | fn safe_unwrap(self) -> DriftResult {
17 | match self {
18 | Some(v) => Ok(v),
19 | None => {
20 | let caller = Location::caller();
21 | msg!("Unwrap error thrown at {}:{}", caller.file(), caller.line());
22 | Err(ErrorCode::FailedUnwrap)
23 | }
24 | }
25 | }
26 | }
27 |
28 | impl SafeUnwrap for Result {
29 | type Item = T;
30 |
31 | #[track_caller]
32 | #[inline(always)]
33 | fn safe_unwrap(self) -> DriftResult {
34 | match self {
35 | Ok(v) => Ok(v),
36 | Err(_) => {
37 | let caller = Location::caller();
38 | msg!("Unwrap error thrown at {}:{}", caller.file(), caller.line());
39 | Err(ErrorCode::FailedUnwrap)
40 | }
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/programs/drift/src/math/serum.rs:
--------------------------------------------------------------------------------
1 | #[cfg(test)]
2 | mod tests;
3 |
4 | use crate::controller::position::PositionDirection;
5 | use crate::error::DriftResult;
6 | use crate::math::casting::Cast;
7 | use crate::math::constants::PRICE_TO_QUOTE_PRECISION_RATIO;
8 | use crate::math::safe_math::SafeMath;
9 |
10 | // Max amount of base to put deposit into serum
11 | pub fn calculate_serum_max_coin_qty(
12 | base_asset_amount: u64,
13 | coin_lot_size: u64,
14 | ) -> DriftResult {
15 | base_asset_amount.safe_div(coin_lot_size)
16 | }
17 |
18 | // calculate limit price in serum lot sizes
19 | pub fn calculate_serum_limit_price(
20 | limit_price: u64,
21 | pc_lot_size: u64,
22 | coin_decimals: u32,
23 | coin_lot_size: u64,
24 | direction: PositionDirection,
25 | ) -> DriftResult {
26 | let coin_precision = 10_u128.pow(coin_decimals);
27 |
28 | match direction {
29 | PositionDirection::Long => limit_price
30 | .cast::()?
31 | .safe_div(PRICE_TO_QUOTE_PRECISION_RATIO)?
32 | .safe_mul(coin_lot_size.cast()?)?
33 | .safe_div(pc_lot_size.cast::()?.safe_mul(coin_precision)?)?
34 | .cast(),
35 | PositionDirection::Short => limit_price
36 | .cast::()?
37 | .safe_div(PRICE_TO_QUOTE_PRECISION_RATIO)?
38 | .safe_mul(coin_lot_size.cast()?)?
39 | .safe_div_ceil(pc_lot_size.cast::()?.safe_mul(coin_precision)?)?
40 | .cast(),
41 | }
42 | }
43 |
44 | // Max amount of quote to put deposit into serum
45 | pub fn calculate_serum_max_native_pc_quantity(
46 | serum_limit_price: u64,
47 | serum_coin_qty: u64,
48 | pc_lot_size: u64,
49 | ) -> DriftResult {
50 | pc_lot_size
51 | .safe_add(pc_lot_size / 2500)? // max 4bps
52 | .safe_mul(serum_limit_price)?
53 | .safe_mul(serum_coin_qty)?
54 | .safe_mul(10004)?
55 | .safe_div(10000)
56 | }
57 |
58 | pub fn calculate_price_from_serum_limit_price(
59 | limit_price: u64,
60 | pc_lot_size: u64,
61 | coin_decimals: u32,
62 | coin_lot_size: u64,
63 | ) -> DriftResult {
64 | let coin_precision = 10_u128.pow(coin_decimals);
65 |
66 | limit_price
67 | .cast::()?
68 | .safe_mul(pc_lot_size.cast::()?.safe_mul(coin_precision)?)?
69 | .safe_mul(PRICE_TO_QUOTE_PRECISION_RATIO)?
70 | .safe_div(coin_lot_size.cast()?)?
71 | .cast()
72 | }
73 |
--------------------------------------------------------------------------------
/programs/drift/src/math/serum/tests.rs:
--------------------------------------------------------------------------------
1 | use crate::controller::position::PositionDirection;
2 | use crate::math::constants::{LAMPORTS_PER_SOL_U64, PRICE_PRECISION_U64};
3 | use crate::math::serum::{
4 | calculate_price_from_serum_limit_price, calculate_serum_limit_price,
5 | calculate_serum_max_coin_qty, calculate_serum_max_native_pc_quantity,
6 | };
7 |
8 | #[test]
9 | fn test_calculate_serum_max_coin_qty() {
10 | let base_asset_amount = LAMPORTS_PER_SOL_U64;
11 | let coin_lot_size = 100000000;
12 | let max_coin_qty = calculate_serum_max_coin_qty(base_asset_amount, coin_lot_size).unwrap();
13 | assert_eq!(max_coin_qty, 10)
14 | }
15 |
16 | #[test]
17 | fn test_calculate_serum_limit_price_bid() {
18 | let limit_price = 21359900;
19 | let pc_lot_size = 1_u64;
20 | let coin_lot_size = 1000000;
21 | let coin_decimals = 9;
22 |
23 | let direction = PositionDirection::Long;
24 | let serum_limit_price = calculate_serum_limit_price(
25 | limit_price,
26 | pc_lot_size,
27 | coin_decimals,
28 | coin_lot_size,
29 | direction,
30 | )
31 | .unwrap();
32 |
33 | assert_eq!(serum_limit_price, 21359);
34 | }
35 |
36 | #[test]
37 | fn test_calculate_serum_limit_price_ask() {
38 | let limit_price = 21359900;
39 | let pc_lot_size = 1_u64;
40 | let coin_lot_size = 1000000;
41 | let coin_decimals = 9;
42 |
43 | let direction = PositionDirection::Short;
44 | let serum_limit_price = calculate_serum_limit_price(
45 | limit_price,
46 | pc_lot_size,
47 | coin_decimals,
48 | coin_lot_size,
49 | direction,
50 | )
51 | .unwrap();
52 |
53 | assert_eq!(serum_limit_price, 21360);
54 | }
55 |
56 | #[test]
57 | fn test_calculate_serum_max_native_pc_quantity() {
58 | let serum_limit_price = 100000_u64;
59 | let serum_coin_qty = 10;
60 | let pc_lot_size = 100_u64;
61 |
62 | let max_native_pc_quantity =
63 | calculate_serum_max_native_pc_quantity(serum_limit_price, serum_coin_qty, pc_lot_size)
64 | .unwrap();
65 |
66 | assert_eq!(max_native_pc_quantity, 100040000); // $100.04
67 | }
68 |
69 | #[test]
70 | fn test_calculate_price_from_serum_limit_price() {
71 | let serum_limit_price = 100000_u64;
72 | let pc_lot_size = 100_u64;
73 | let coin_lot_size = 100000000;
74 | let coin_decimals = 9;
75 |
76 | let price = calculate_price_from_serum_limit_price(
77 | serum_limit_price,
78 | pc_lot_size,
79 | coin_decimals,
80 | coin_lot_size,
81 | )
82 | .unwrap();
83 |
84 | assert_eq!(price, 100 * PRICE_PRECISION_U64);
85 | }
86 |
--------------------------------------------------------------------------------
/programs/drift/src/math/spot_balance/tests.rs:
--------------------------------------------------------------------------------
1 | #[cfg(test)]
2 | mod test {
3 | use crate::math::spot_balance::{get_spot_balance, get_token_amount};
4 | use crate::state::spot_market::{SpotBalanceType, SpotMarket};
5 | use crate::SPOT_CUMULATIVE_INTEREST_PRECISION;
6 |
7 | #[test]
8 | fn bonk() {
9 | let spot_market = SpotMarket {
10 | cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION,
11 | decimals: 5,
12 | ..SpotMarket::default_quote_market()
13 | };
14 |
15 | let one_bonk = 10_u128.pow(spot_market.decimals);
16 |
17 | let balance =
18 | get_spot_balance(one_bonk, &spot_market, &SpotBalanceType::Deposit, false).unwrap();
19 |
20 | let token_amount =
21 | get_token_amount(balance, &spot_market, &SpotBalanceType::Deposit).unwrap();
22 | assert_eq!(token_amount, one_bonk);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/programs/drift/src/math/stats.rs:
--------------------------------------------------------------------------------
1 | use crate::error::DriftResult;
2 | use crate::math::casting::Cast;
3 | use crate::math::safe_math::SafeMath;
4 | use std::cmp::max;
5 |
6 | pub fn calculate_rolling_sum(
7 | data1: u64,
8 | data2: u64,
9 | weight1_numer: i64,
10 | weight1_denom: i64,
11 | ) -> DriftResult {
12 | // assumes that missing times are zeros (e.g. handle NaN as 0)
13 | let prev_twap_99 = data1
14 | .cast::()?
15 | .safe_mul(max(0, weight1_denom.safe_sub(weight1_numer)?).cast::()?)?
16 | .safe_div(weight1_denom.cast::()?)?;
17 |
18 | prev_twap_99.cast::()?.safe_add(data2)
19 | }
20 |
21 | pub fn calculate_weighted_average(
22 | data1: i64,
23 | data2: i64,
24 | weight1: i64,
25 | weight2: i64,
26 | ) -> DriftResult {
27 | let denominator = weight1.safe_add(weight2)?.cast::()?;
28 | let prev_twap_99 = data1.cast::()?.safe_mul(weight1.cast()?)?;
29 | let latest_price_01 = data2.cast::()?.safe_mul(weight2.cast()?)?;
30 |
31 | if weight1 == 0 {
32 | return Ok(data2);
33 | }
34 |
35 | if weight2 == 0 {
36 | return Ok(data1);
37 | }
38 |
39 | let bias: i64 = if weight2 > 1 {
40 | if latest_price_01 < prev_twap_99 {
41 | -1
42 | } else if latest_price_01 > prev_twap_99 {
43 | 1
44 | } else {
45 | 0
46 | }
47 | } else {
48 | 0
49 | };
50 |
51 | let twap = prev_twap_99
52 | .safe_add(latest_price_01)?
53 | .safe_div(denominator)?
54 | .cast::()?;
55 |
56 | if twap == 0 && bias < 0 {
57 | return Ok(twap);
58 | }
59 |
60 | twap.safe_add(bias)
61 | }
62 |
63 | pub fn calculate_new_twap(
64 | current_price: i64,
65 | current_ts: i64,
66 | last_twap: i64,
67 | last_ts: i64,
68 | period: i64,
69 | ) -> DriftResult {
70 | let since_last = max(0_i64, current_ts.safe_sub(last_ts)?);
71 | let from_start = max(1_i64, period.safe_sub(since_last)?);
72 |
73 | calculate_weighted_average(current_price, last_twap, since_last, from_start)
74 | }
75 |
--------------------------------------------------------------------------------
/programs/drift/src/signer.rs:
--------------------------------------------------------------------------------
1 | pub fn get_signer_seeds(nonce: &u8) -> [&[u8]; 2] {
2 | [b"drift_signer".as_ref(), bytemuck::bytes_of(nonce)]
3 | }
4 |
--------------------------------------------------------------------------------
/programs/drift/src/state/fill_mode.rs:
--------------------------------------------------------------------------------
1 | use crate::error::DriftResult;
2 | use crate::math::auction::calculate_auction_price;
3 | use crate::math::casting::Cast;
4 | use crate::math::safe_math::SafeMath;
5 | use crate::state::user::Order;
6 |
7 | #[cfg(test)]
8 | mod tests;
9 |
10 | #[derive(Copy, Clone, Debug, PartialEq, Eq)]
11 | pub enum FillMode {
12 | Fill,
13 | PlaceAndMake,
14 | PlaceAndTake(bool, u8),
15 | Liquidation,
16 | }
17 |
18 | impl FillMode {
19 | pub fn get_limit_price(
20 | &self,
21 | order: &Order,
22 | valid_oracle_price: Option,
23 | slot: u64,
24 | tick_size: u64,
25 | is_prediction_market: bool,
26 | ) -> DriftResult