Merge branch 'dev' into feature/close-mango-account
This commit is contained in:
commit
3077f57c2f
|
@ -11,7 +11,6 @@ jobs:
|
||||||
format:
|
format:
|
||||||
name: Format
|
name: Format
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: (github.actor != 'dependabot[bot]')
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
@ -31,7 +30,6 @@ jobs:
|
||||||
lint:
|
lint:
|
||||||
name: Lint
|
name: Lint
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: (github.actor != 'dependabot[bot]')
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
@ -51,7 +49,6 @@ jobs:
|
||||||
unit-test:
|
unit-test:
|
||||||
name: Unit Test
|
name: Unit Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: (github.actor != 'dependabot[bot]')
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
@ -71,7 +68,6 @@ jobs:
|
||||||
semgrep:
|
semgrep:
|
||||||
name: Security Scan
|
name: Security Scan
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: (github.actor != 'dependabot[bot]')
|
|
||||||
container:
|
container:
|
||||||
image: returntocorp/semgrep
|
image: returntocorp/semgrep
|
||||||
|
|
||||||
|
@ -80,6 +76,12 @@ jobs:
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Run semgrep
|
- name: Run semgrep
|
||||||
run: semgrep ci
|
run: semgrep ci --sarif --output=semgrep-results.sarif
|
||||||
env:
|
env:
|
||||||
SEMGREP_RULES: p/typescript
|
SEMGREP_RULES: p/typescript
|
||||||
|
|
||||||
|
- name: Upload output
|
||||||
|
uses: github/codeql-action/upload-sarif@v2
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
sarif_file: semgrep-results.sarif
|
||||||
|
|
|
@ -25,12 +25,11 @@ jobs:
|
||||||
trivy:
|
trivy:
|
||||||
name: Dependency Scan
|
name: Dependency Scan
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: (github.actor != 'dependabot[bot]')
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
# Report all vulnerabilities in CI output
|
# Report all vulnerabilities in security tab
|
||||||
- name: Report on all vulnerabilities
|
- name: Report on all vulnerabilities
|
||||||
uses: aquasecurity/trivy-action@master
|
uses: aquasecurity/trivy-action@master
|
||||||
with:
|
with:
|
||||||
|
@ -38,7 +37,8 @@ jobs:
|
||||||
scan-ref: 'Cargo.lock'
|
scan-ref: 'Cargo.lock'
|
||||||
ignore-unfixed: true
|
ignore-unfixed: true
|
||||||
hide-progress: true
|
hide-progress: true
|
||||||
format: 'table'
|
format: 'sarif'
|
||||||
|
output: 'trivy-results.sarif'
|
||||||
|
|
||||||
# Fail the job on critical vulnerabiliies with fix available
|
# Fail the job on critical vulnerabiliies with fix available
|
||||||
- name: Fail on critical vulnerabilities
|
- name: Fail on critical vulnerabilities
|
||||||
|
@ -51,3 +51,9 @@ jobs:
|
||||||
format: 'table'
|
format: 'table'
|
||||||
severity: 'CRITICAL'
|
severity: 'CRITICAL'
|
||||||
exit-code: '1'
|
exit-code: '1'
|
||||||
|
|
||||||
|
- name: Upload output
|
||||||
|
uses: github/codeql-action/upload-sarif@v2
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
sarif_file: 'trivy-results.sarif'
|
|
@ -11,12 +11,11 @@ jobs:
|
||||||
trivy:
|
trivy:
|
||||||
name: Dependency Scan
|
name: Dependency Scan
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: (github.actor != 'dependabot[bot]')
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
# Report all vulnerabilities in CI output
|
# Report all vulnerabilities in security tab
|
||||||
- name: Report on all vulnerabilities
|
- name: Report on all vulnerabilities
|
||||||
uses: aquasecurity/trivy-action@master
|
uses: aquasecurity/trivy-action@master
|
||||||
with:
|
with:
|
||||||
|
@ -24,7 +23,8 @@ jobs:
|
||||||
scan-ref: 'yarn.lock'
|
scan-ref: 'yarn.lock'
|
||||||
ignore-unfixed: true
|
ignore-unfixed: true
|
||||||
hide-progress: true
|
hide-progress: true
|
||||||
format: 'table'
|
format: 'sarif'
|
||||||
|
output: 'trivy-results.sarif'
|
||||||
|
|
||||||
# Fail the job on critical vulnerabiliies with fix available
|
# Fail the job on critical vulnerabiliies with fix available
|
||||||
- name: Fail on critical vulnerabilities
|
- name: Fail on critical vulnerabilities
|
||||||
|
@ -37,3 +37,9 @@ jobs:
|
||||||
format: 'table'
|
format: 'table'
|
||||||
severity: 'CRITICAL'
|
severity: 'CRITICAL'
|
||||||
exit-code: '1'
|
exit-code: '1'
|
||||||
|
|
||||||
|
- name: Upload output
|
||||||
|
uses: github/codeql-action/upload-sarif@v2
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
sarif_file: 'trivy-results.sarif'
|
37
CHANGELOG.md
37
CHANGELOG.md
|
@ -1,42 +1,55 @@
|
||||||
# Mango v4 Program Change Log
|
# Mango v4 Program Change Log
|
||||||
|
|
||||||
Update this for each mainnet deployment.
|
Update this for each program release and mainnet deployment.
|
||||||
|
|
||||||
## not on mainnet
|
## not on mainnet
|
||||||
|
|
||||||
## mainnet
|
## mainnet
|
||||||
|
|
||||||
Jan 5, 2023 Central European Standard Time
|
### v0.2.0, 2023-1-13
|
||||||
|
|
||||||
|
Deployment: Jan 13, 2023 at 11:31:05 Central European Standard Time, https://explorer.solana.com/tx/4yGRUk6QwntvC4umECDPDZJNcbevSJ1fdZi75Mz9rGa9SHKzUtjMF3V5FCTkzBZqAETQTccqv63BYw6yX8JNxiur
|
||||||
|
|
||||||
|
- Add an optional security authority with the ability to halt a group or
|
||||||
|
temporarily freeze user accounts.
|
||||||
|
- Extend perp pnl settle limits to apply to realized pnl
|
||||||
|
- Rename perp_liq_bankruptcy to perp_liq_quote_and_bankruptcy and extend it to
|
||||||
|
cover taking over the liqee's negative pnl while the settle limits and perp
|
||||||
|
settle health allow it.
|
||||||
|
- Perp bankruptcy is now allowed when settling is impossible, even when there are
|
||||||
|
spot assets remaining.
|
||||||
|
|
||||||
|
### Jan 5, 2023 Central European Standard Time
|
||||||
|
|
||||||
- Change max staleness slots from -1 to 600 for trustless token registering
|
- Change max staleness slots from -1 to 600 for trustless token registering
|
||||||
|
|
||||||
Jan 4, 2023 Central European Standard Time
|
### Jan 4, 2023 Central European Standard Time
|
||||||
|
|
||||||
- Reduce only mode for tokens, and perp markets
|
- Reduce only mode for tokens, and perp markets
|
||||||
- Perp settlement applies no loan origination fee
|
- Perp settlement applies no loan origination fee
|
||||||
|
|
||||||
Dec 16, 2022 at 16:40 Central European Standard Time
|
### Dec 16, 2022 at 16:40 Central European Standard Time
|
||||||
|
|
||||||
Oct 8, 2022 at 14:38:31 Central European Summer Time
|
### Oct 8, 2022 at 14:38:31 Central European Summer Time
|
||||||
https://explorer.solana.com/tx/3m8EDohkgwJZyiwpGXztBWARWQVxyhnSNDVuH467D7FPS2wxJerr79HhdhDEed5hpConHgGsKHvxtW1HJP6GixX9
|
https://explorer.solana.com/tx/3m8EDohkgwJZyiwpGXztBWARWQVxyhnSNDVuH467D7FPS2wxJerr79HhdhDEed5hpConHgGsKHvxtW1HJP6GixX9
|
||||||
|
|
||||||
Oct 8, 2022 at 14:38:31 Central European Summer Time
|
### Oct 8, 2022 at 14:38:31 Central European Summer Time
|
||||||
https://explorer.solana.com/tx/3m8EDohkgwJZyiwpGXztBWARWQVxyhnSNDVuH467D7FPS2wxJerr79HhdhDEed5hpConHgGsKHvxtW1HJP6GixX9
|
https://explorer.solana.com/tx/3m8EDohkgwJZyiwpGXztBWARWQVxyhnSNDVuH467D7FPS2wxJerr79HhdhDEed5hpConHgGsKHvxtW1HJP6GixX9
|
||||||
|
|
||||||
- New ix `TokenDepositIntoExisting`
|
- New ix `TokenDepositIntoExisting`
|
||||||
|
|
||||||
Sep 1, 2022 at 10:24:35 Central European Summer Time
|
### Sep 1, 2022 at 10:24:35 Central European Summer Time
|
||||||
https://explorer.solana.com/tx/3NnX13A3QwsREKKKo3iYR4jqgoongpCjdhhXuJ3y5iP6FwfPcNieVop623tpgPbyreC7m7KtphwdWdoHYE5YC394
|
https://explorer.solana.com/tx/3NnX13A3QwsREKKKo3iYR4jqgoongpCjdhhXuJ3y5iP6FwfPcNieVop623tpgPbyreC7m7KtphwdWdoHYE5YC394
|
||||||
|
|
||||||
- Add HealthRegionBegin, -End instructions
|
- Add HealthRegionBegin, -End instructions
|
||||||
- Add explicit "oracle" account argument for TokenDeposit and TokenWithdraw instructions
|
- Add explicit "oracle" account argument for TokenDeposit and TokenWithdraw instructions
|
||||||
|
|
||||||
Aug 20, 2022 at 19:58:29 Central European Summer Time
|
### Aug 20, 2022 at 19:58:29 Central European Summer Time
|
||||||
https://explorer.solana.com/tx/3R4frko1AekQKJmmQ5T6k3mdXF9uZVHTR7oocdspTPsc82xX7qrbgnG61r28UdhCxsjMxtQHgBqMc37FSvoHQfCN
|
https://explorer.solana.com/tx/3R4frko1AekQKJmmQ5T6k3mdXF9uZVHTR7oocdspTPsc82xX7qrbgnG61r28UdhCxsjMxtQHgBqMc37FSvoHQfCN
|
||||||
|
|
||||||
- loan fee logging for off-chain services
|
- loan fee logging for off-chain services
|
||||||
|
|
||||||
Aug 18, 2022 at 17:17:40 Central European Summer Time
|
### Aug 18, 2022 at 17:17:40 Central European Summer Time
|
||||||
https://explorer.solana.com/tx/4Xnyswcwx98y6khw8ptNVmdhQZwJjuNy2BvmQg2pJayoThFiw8kmS2ecRAg5cg2DncvW3NQgn2vtP8mCUtv6Q1yB
|
https://explorer.solana.com/tx/4Xnyswcwx98y6khw8ptNVmdhQZwJjuNy2BvmQg2pJayoThFiw8kmS2ecRAg5cg2DncvW3NQgn2vtP8mCUtv6Q1yB
|
||||||
|
|
||||||
- liq_token_bankruptcy: removed liab_token_index argument
|
- liq_token_bankruptcy: removed liab_token_index argument
|
||||||
|
@ -82,13 +95,13 @@ https://explorer.solana.com/tx/4Xnyswcwx98y6khw8ptNVmdhQZwJjuNy2BvmQg2pJayoThFiw
|
||||||
marginTrade takes inputMintPk and outputMintPk instead of inputToken and outputToken
|
marginTrade takes inputMintPk and outputMintPk instead of inputToken and outputToken
|
||||||
marginTrade takes flashLoanType as an argument
|
marginTrade takes flashLoanType as an argument
|
||||||
|
|
||||||
Aug 8, 2022 at 18:56:04 Central European Summer Time
|
### Aug 8, 2022 at 18:56:04 Central European Summer Time
|
||||||
https://explorer.solana.com/tx/yjZggRTrcDNquMkftNvBKLv77Dk4xp5yQPYXgN3qvBHTBWWJVhLPGHxqpGwosmEq3j8byHZMa13oxLLerBWUdgW
|
https://explorer.solana.com/tx/yjZggRTrcDNquMkftNvBKLv77Dk4xp5yQPYXgN3qvBHTBWWJVhLPGHxqpGwosmEq3j8byHZMa13oxLLerBWUdgW
|
||||||
|
|
||||||
- improved logging for off chain services
|
- improved logging for off chain services
|
||||||
- `AccountCreate` ix takes explicit input for sizes of various features
|
- `AccountCreate` ix takes explicit input for sizes of various features
|
||||||
|
|
||||||
Aug 4, 2022 at 09:30:00 Central European Summer Time
|
### Aug 4, 2022 at 09:30:00 Central European Summer Time
|
||||||
|
|
||||||
ts/client changes
|
ts/client changes
|
||||||
|
|
||||||
|
@ -114,7 +127,7 @@ New features
|
||||||
- `TokenRegistration` and `TokenRegisterTrustless` ixs dont take a bank_num anymore, hardcoded to 0.
|
- `TokenRegistration` and `TokenRegisterTrustless` ixs dont take a bank_num anymore, hardcoded to 0.
|
||||||
- Enforced a minimum maximum rate of 50% so that rates don't fall so low that they cannot recover.
|
- Enforced a minimum maximum rate of 50% so that rates don't fall so low that they cannot recover.
|
||||||
|
|
||||||
Jul 14, 2022 at 09:33:52 Central European Summer Time
|
### Jul 14, 2022 at 09:33:52 Central European Summer Time
|
||||||
https://explorer.solana.com/tx/vZ5hP1vGp37fgzBfG9nb4nfA5ZdmYgk8meq53YPR4ReFxrcTwBUxTYBQUgnfAnq9u5fH36S3QTfb9mVkBXt5A6C
|
https://explorer.solana.com/tx/vZ5hP1vGp37fgzBfG9nb4nfA5ZdmYgk8meq53YPR4ReFxrcTwBUxTYBQUgnfAnq9u5fH36S3QTfb9mVkBXt5A6C
|
||||||
|
|
||||||
- Account data was rearranged to put fields that are often used with gPA first
|
- Account data was rearranged to put fields that are often used with gPA first
|
||||||
|
|
|
@ -1094,7 +1094,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cli"
|
name = "cli"
|
||||||
version = "0.1.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anchor-client",
|
"anchor-client",
|
||||||
"anchor-lang",
|
"anchor-lang",
|
||||||
|
@ -1118,7 +1118,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "client"
|
name = "client"
|
||||||
version = "0.1.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anchor-client",
|
"anchor-client",
|
||||||
"anchor-lang",
|
"anchor-lang",
|
||||||
|
@ -2846,7 +2846,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "keeper"
|
name = "keeper"
|
||||||
version = "0.1.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anchor-client",
|
"anchor-client",
|
||||||
"anchor-lang",
|
"anchor-lang",
|
||||||
|
@ -3121,7 +3121,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mango-v4"
|
name = "mango-v4"
|
||||||
version = "0.1.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anchor-lang",
|
"anchor-lang",
|
||||||
"anchor-spl",
|
"anchor-spl",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "cli"
|
name = "cli"
|
||||||
version = "0.1.0"
|
version = "0.3.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "client"
|
name = "client"
|
||||||
version = "0.1.0"
|
version = "0.3.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "keeper"
|
name = "keeper"
|
||||||
version = "0.1.0"
|
version = "0.3.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "mango-v4"
|
name = "mango-v4"
|
||||||
version = "0.1.0"
|
version = "0.3.0"
|
||||||
description = "Created with Anchor"
|
description = "Created with Anchor"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
|
|
@ -537,21 +537,6 @@ impl HealthCache {
|
||||||
self.has_spot_borrows() || perp_borrows
|
self.has_spot_borrows() || perp_borrows
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_liquidatable_spot_or_perp_base(&self) -> bool {
|
|
||||||
let spot_liquidatable = self.has_spot_assets();
|
|
||||||
let serum3_cancelable = self.has_serum3_open_orders_funds();
|
|
||||||
let perp_liquidatable = self.perp_infos.iter().any(|p| {
|
|
||||||
// can use perp_liq_base_position
|
|
||||||
p.base_lots != 0
|
|
||||||
// can use perp_liq_force_cancel_orders
|
|
||||||
|| p.has_open_orders
|
|
||||||
// A remaining quote position can be reduced with perp_settle_pnl and that can improve health.
|
|
||||||
// However, since it's not guaranteed that there is a counterparty, a positive perp quote position
|
|
||||||
// does not prevent bankruptcy.
|
|
||||||
});
|
|
||||||
spot_liquidatable || serum3_cancelable || perp_liquidatable
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn compute_serum3_reservations(
|
pub(crate) fn compute_serum3_reservations(
|
||||||
&self,
|
&self,
|
||||||
health_type: HealthType,
|
health_type: HealthType,
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use fixed::types::I80F48;
|
use fixed::types::I80F48;
|
||||||
|
|
||||||
pub trait ClampedToNum {
|
pub trait ClampToInt {
|
||||||
fn clamp_to_i64(&self) -> i64;
|
fn clamp_to_i64(&self) -> i64;
|
||||||
fn clamp_to_u64(&self) -> u64;
|
fn clamp_to_u64(&self) -> u64;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClampedToNum for I80F48 {
|
impl ClampToInt for I80F48 {
|
||||||
fn clamp_to_i64(&self) -> i64 {
|
fn clamp_to_i64(&self) -> i64 {
|
||||||
if *self <= i64::MIN {
|
if *self <= i64::MIN {
|
||||||
i64::MIN
|
i64::MIN
|
||||||
|
@ -27,7 +27,7 @@ impl ClampedToNum for I80F48 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClampedToNum for f64 {
|
impl ClampToInt for f64 {
|
||||||
fn clamp_to_i64(&self) -> i64 {
|
fn clamp_to_i64(&self) -> i64 {
|
||||||
if *self <= i64::MIN as f64 {
|
if *self <= i64::MIN as f64 {
|
||||||
i64::MIN
|
i64::MIN
|
||||||
|
@ -49,7 +49,7 @@ impl ClampedToNum for f64 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClampedToNum for u64 {
|
impl ClampToInt for u64 {
|
||||||
fn clamp_to_i64(&self) -> i64 {
|
fn clamp_to_i64(&self) -> i64 {
|
||||||
if *self >= i64::MAX as u64 {
|
if *self >= i64::MAX as u64 {
|
||||||
i64::MAX
|
i64::MAX
|
||||||
|
|
|
@ -71,24 +71,8 @@ pub fn perp_liq_base_position(
|
||||||
let liqee_init_health = liqee_health_cache.health(HealthType::Init);
|
let liqee_init_health = liqee_health_cache.health(HealthType::Init);
|
||||||
liqee_health_cache.require_after_phase1_liquidation()?;
|
liqee_health_cache.require_after_phase1_liquidation()?;
|
||||||
|
|
||||||
// Once maint_health falls below 0, we want to start liquidating,
|
if !liqee.check_liquidatable(&liqee_health_cache)? {
|
||||||
// we want to allow liquidation to continue until init_health is positive,
|
return Ok(());
|
||||||
// to prevent constant oscillation between the two states
|
|
||||||
if liqee.being_liquidated() {
|
|
||||||
if liqee
|
|
||||||
.fixed
|
|
||||||
.maybe_recover_from_being_liquidated(liqee_init_health)
|
|
||||||
{
|
|
||||||
msg!("Liqee init_health above zero");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let maint_health = liqee_health_cache.health(HealthType::Maint);
|
|
||||||
require!(
|
|
||||||
maint_health < I80F48::ZERO,
|
|
||||||
MangoError::HealthMustBeNegative
|
|
||||||
);
|
|
||||||
liqee.fixed.set_being_liquidated(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut perp_market = ctx.accounts.perp_market.load_mut()?;
|
let mut perp_market = ctx.accounts.perp_market.load_mut()?;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use anchor_lang::prelude::*;
|
use anchor_lang::prelude::*;
|
||||||
use fixed::types::I80F48;
|
|
||||||
|
|
||||||
use crate::error::*;
|
use crate::error::*;
|
||||||
use crate::health::*;
|
use crate::health::*;
|
||||||
|
@ -47,22 +46,20 @@ pub fn perp_liq_force_cancel_orders(
|
||||||
let health_cache =
|
let health_cache =
|
||||||
new_health_cache(&account.borrow(), &retriever).context("create health cache")?;
|
new_health_cache(&account.borrow(), &retriever).context("create health cache")?;
|
||||||
|
|
||||||
if account.being_liquidated() {
|
{
|
||||||
let init_health = health_cache.health(HealthType::Init);
|
let result = account.check_liquidatable(&health_cache);
|
||||||
if account
|
if account.fixed.is_operational() {
|
||||||
.fixed
|
if !result? {
|
||||||
.maybe_recover_from_being_liquidated(init_health)
|
return Ok(());
|
||||||
{
|
}
|
||||||
msg!("Liqee init_health above zero");
|
} else {
|
||||||
return Ok(());
|
// Frozen accounts can always have their orders cancelled
|
||||||
|
if let Err(Error::AnchorError(ref inner)) = result {
|
||||||
|
if inner.error_code_number != MangoError::HealthMustBeNegative as u32 {
|
||||||
|
result?;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
let maint_health = health_cache.health(HealthType::Maint);
|
|
||||||
require!(
|
|
||||||
maint_health < I80F48::ZERO,
|
|
||||||
MangoError::HealthMustBeNegative
|
|
||||||
);
|
|
||||||
account.fixed.set_being_liquidated(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
health_cache
|
health_cache
|
||||||
|
|
|
@ -118,24 +118,8 @@ pub fn perp_liq_quote_and_bankruptcy(
|
||||||
let liqee_settle_health = liqee_health_cache.perp_settle_health();
|
let liqee_settle_health = liqee_health_cache.perp_settle_health();
|
||||||
liqee_health_cache.require_after_phase2_liquidation()?;
|
liqee_health_cache.require_after_phase2_liquidation()?;
|
||||||
|
|
||||||
// Once maint_health falls below 0, we want to start liquidating,
|
if !liqee.check_liquidatable(&liqee_health_cache)? {
|
||||||
// we want to allow liquidation to continue until init_health is positive,
|
return Ok(());
|
||||||
// to prevent constant oscillation between the two states
|
|
||||||
if liqee.being_liquidated() {
|
|
||||||
if liqee
|
|
||||||
.fixed
|
|
||||||
.maybe_recover_from_being_liquidated(liqee_init_health)
|
|
||||||
{
|
|
||||||
msg!("Liqee init_health above zero");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let maint_health = liqee_health_cache.health(HealthType::Maint);
|
|
||||||
require!(
|
|
||||||
maint_health < I80F48::ZERO,
|
|
||||||
MangoError::HealthMustBeNegative
|
|
||||||
);
|
|
||||||
liqee.fixed.set_being_liquidated(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check positions exist/create them, done early for nicer error messages
|
// check positions exist/create them, done early for nicer error messages
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use anchor_lang::prelude::*;
|
use anchor_lang::prelude::*;
|
||||||
use anchor_spl::token::{Token, TokenAccount};
|
use anchor_spl::token::{Token, TokenAccount};
|
||||||
use fixed::types::I80F48;
|
|
||||||
|
|
||||||
use crate::error::*;
|
use crate::error::*;
|
||||||
use crate::health::*;
|
use crate::health::*;
|
||||||
|
@ -126,22 +125,21 @@ pub fn serum3_liq_force_cancel_orders(
|
||||||
let health_cache =
|
let health_cache =
|
||||||
new_health_cache(&account.borrow(), &retriever).context("create health cache")?;
|
new_health_cache(&account.borrow(), &retriever).context("create health cache")?;
|
||||||
|
|
||||||
if account.being_liquidated() {
|
{
|
||||||
let init_health = health_cache.health(HealthType::Init);
|
let result = account.check_liquidatable(&health_cache);
|
||||||
if account
|
if account.fixed.is_operational() {
|
||||||
.fixed
|
if !result? {
|
||||||
.maybe_recover_from_being_liquidated(init_health)
|
return Ok(());
|
||||||
{
|
}
|
||||||
msg!("Liqee init_health above zero");
|
} else {
|
||||||
return Ok(());
|
// Frozen accounts can always have their orders cancelled
|
||||||
|
if let Err(Error::AnchorError(ref inner)) = result {
|
||||||
|
if inner.error_code_number != MangoError::HealthMustBeNegative as u32 {
|
||||||
|
// propagate all unexpected errors
|
||||||
|
result?;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
let maint_health = health_cache.health(HealthType::Maint);
|
|
||||||
require!(
|
|
||||||
maint_health < I80F48::ZERO,
|
|
||||||
MangoError::HealthMustBeNegative
|
|
||||||
);
|
|
||||||
account.fixed.set_being_liquidated(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
health_cache
|
health_cache
|
||||||
|
|
|
@ -69,21 +69,8 @@ pub fn token_liq_with_token(
|
||||||
let init_health = liqee_health_cache.health(HealthType::Init);
|
let init_health = liqee_health_cache.health(HealthType::Init);
|
||||||
liqee_health_cache.require_after_phase1_liquidation()?;
|
liqee_health_cache.require_after_phase1_liquidation()?;
|
||||||
|
|
||||||
// Once maint_health falls below 0, we want to start liquidating,
|
if !liqee.check_liquidatable(&liqee_health_cache)? {
|
||||||
// we want to allow liquidation to continue until init_health is positive,
|
return Ok(());
|
||||||
// to prevent constant oscillation between the two states
|
|
||||||
if liqee.being_liquidated() {
|
|
||||||
if liqee.fixed.maybe_recover_from_being_liquidated(init_health) {
|
|
||||||
msg!("Liqee init_health above zero");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let maint_health = liqee_health_cache.health(HealthType::Maint);
|
|
||||||
require!(
|
|
||||||
maint_health < I80F48::ZERO,
|
|
||||||
MangoError::HealthMustBeNegative
|
|
||||||
);
|
|
||||||
liqee.fixed.set_being_liquidated(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -86,7 +86,7 @@ pub struct MangoAccount {
|
||||||
/// Init health as calculated during HealthReginBegin, rounded up.
|
/// Init health as calculated during HealthReginBegin, rounded up.
|
||||||
pub health_region_begin_init_health: i64,
|
pub health_region_begin_init_health: i64,
|
||||||
|
|
||||||
pub frozen_until: i64,
|
pub frozen_until: u64,
|
||||||
|
|
||||||
pub reserved: [u8; 232],
|
pub reserved: [u8; 232],
|
||||||
|
|
||||||
|
@ -968,6 +968,30 @@ impl<
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn check_liquidatable(&mut self, health_cache: &HealthCache) -> Result<bool> {
|
||||||
|
// Once maint_health falls below 0, we want to start liquidating,
|
||||||
|
// we want to allow liquidation to continue until init_health is positive,
|
||||||
|
// to prevent constant oscillation between the two states
|
||||||
|
if self.being_liquidated() {
|
||||||
|
let init_health = health_cache.health(HealthType::Init);
|
||||||
|
if self
|
||||||
|
.fixed_mut()
|
||||||
|
.maybe_recover_from_being_liquidated(init_health)
|
||||||
|
{
|
||||||
|
msg!("Liqee init_health above zero");
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let maint_health = health_cache.health(HealthType::Maint);
|
||||||
|
require!(
|
||||||
|
maint_health < I80F48::ZERO,
|
||||||
|
MangoError::HealthMustBeNegative
|
||||||
|
);
|
||||||
|
self.fixed_mut().set_being_liquidated(true);
|
||||||
|
}
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
// writes length of tokens vec at appropriate offset so that borsh can infer the vector length
|
// writes length of tokens vec at appropriate offset so that borsh can infer the vector length
|
||||||
// length used is that present in the header
|
// length used is that present in the header
|
||||||
fn write_token_length(&mut self) {
|
fn write_token_length(&mut self) {
|
||||||
|
|
|
@ -6,7 +6,7 @@ use static_assertions::const_assert_eq;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
|
||||||
use crate::i80f48::ClampedToNum;
|
use crate::i80f48::ClampToInt;
|
||||||
use crate::state::*;
|
use crate::state::*;
|
||||||
|
|
||||||
pub const FREE_ORDER_SLOT: PerpMarketIndex = PerpMarketIndex::MAX;
|
pub const FREE_ORDER_SLOT: PerpMarketIndex = PerpMarketIndex::MAX;
|
||||||
|
|
|
@ -58,7 +58,7 @@ import {
|
||||||
} from './utils';
|
} from './utils';
|
||||||
import { sendTransaction } from './utils/rpc';
|
import { sendTransaction } from './utils/rpc';
|
||||||
|
|
||||||
enum AccountRetriever {
|
export enum AccountRetriever {
|
||||||
Scanning,
|
Scanning,
|
||||||
Fixed,
|
Fixed,
|
||||||
}
|
}
|
||||||
|
@ -2657,9 +2657,7 @@ export class MangoClient {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// private
|
public buildHealthRemainingAccounts(
|
||||||
|
|
||||||
private buildHealthRemainingAccounts(
|
|
||||||
retriever: AccountRetriever,
|
retriever: AccountRetriever,
|
||||||
group: Group,
|
group: Group,
|
||||||
mangoAccounts: MangoAccount[],
|
mangoAccounts: MangoAccount[],
|
||||||
|
@ -2699,12 +2697,12 @@ export class MangoClient {
|
||||||
const tokenPositionIndices = mangoAccount.tokens.map((t) => t.tokenIndex);
|
const tokenPositionIndices = mangoAccount.tokens.map((t) => t.tokenIndex);
|
||||||
for (const bank of banks) {
|
for (const bank of banks) {
|
||||||
const tokenPositionExists =
|
const tokenPositionExists =
|
||||||
tokenPositionIndices.indexOf(bank.tokenIndex) > 0;
|
tokenPositionIndices.indexOf(bank.tokenIndex) > -1;
|
||||||
if (!tokenPositionExists) {
|
if (!tokenPositionExists) {
|
||||||
const inactiveTokenPosition = tokenPositionIndices.findIndex(
|
const inactiveTokenPosition = tokenPositionIndices.findIndex(
|
||||||
(index) => index === TokenPosition.TokenIndexUnset,
|
(index) => index === TokenPosition.TokenIndexUnset,
|
||||||
);
|
);
|
||||||
if (inactiveTokenPosition) {
|
if (inactiveTokenPosition != -1) {
|
||||||
tokenPositionIndices[inactiveTokenPosition] = bank.tokenIndex;
|
tokenPositionIndices[inactiveTokenPosition] = bank.tokenIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2724,12 +2722,12 @@ export class MangoClient {
|
||||||
const perpPositionIndices = mangoAccount.perps.map((p) => p.marketIndex);
|
const perpPositionIndices = mangoAccount.perps.map((p) => p.marketIndex);
|
||||||
for (const perpMarket of perpMarkets) {
|
for (const perpMarket of perpMarkets) {
|
||||||
const perpPositionExists =
|
const perpPositionExists =
|
||||||
perpPositionIndices.indexOf(perpMarket.perpMarketIndex) > 0;
|
perpPositionIndices.indexOf(perpMarket.perpMarketIndex) > -1;
|
||||||
if (!perpPositionExists) {
|
if (!perpPositionExists) {
|
||||||
const inactivePerpPosition = perpPositionIndices.find(
|
const inactivePerpPosition = perpPositionIndices.findIndex(
|
||||||
(perpIdx) => perpIdx === PerpPosition.PerpMarketIndexUnset,
|
(perpIdx) => perpIdx === PerpPosition.PerpMarketIndexUnset,
|
||||||
);
|
);
|
||||||
if (inactivePerpPosition) {
|
if (inactivePerpPosition != -1) {
|
||||||
perpPositionIndices[inactivePerpPosition] =
|
perpPositionIndices[inactivePerpPosition] =
|
||||||
perpMarket.perpMarketIndex;
|
perpMarket.perpMarketIndex;
|
||||||
}
|
}
|
||||||
|
@ -2753,15 +2751,16 @@ export class MangoClient {
|
||||||
const ooPositionExists =
|
const ooPositionExists =
|
||||||
serumPositionIndices.findIndex(
|
serumPositionIndices.findIndex(
|
||||||
(i) => i.marketIndex === serum3Market.marketIndex,
|
(i) => i.marketIndex === serum3Market.marketIndex,
|
||||||
) > 0;
|
) > -1;
|
||||||
if (!ooPositionExists) {
|
if (!ooPositionExists) {
|
||||||
const inactiveSerumPosition = serumPositionIndices.find(
|
const inactiveSerumPosition = serumPositionIndices.findIndex(
|
||||||
(serumPos) =>
|
(serumPos) =>
|
||||||
serumPos.marketIndex === Serum3Orders.Serum3MarketIndexUnset,
|
serumPos.marketIndex === Serum3Orders.Serum3MarketIndexUnset,
|
||||||
);
|
);
|
||||||
if (inactiveSerumPosition) {
|
if (inactiveSerumPosition != -1) {
|
||||||
inactiveSerumPosition.marketIndex = serum3Market.marketIndex;
|
serumPositionIndices[inactiveSerumPosition].marketIndex =
|
||||||
inactiveSerumPosition.openOrders = openOrderPk;
|
serum3Market.marketIndex;
|
||||||
|
serumPositionIndices[inactiveSerumPosition].openOrders = openOrderPk;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export type MangoV4 = {
|
export type MangoV4 = {
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"name": "mango_v4",
|
"name": "mango_v4",
|
||||||
"instructions": [
|
"instructions": [
|
||||||
{
|
{
|
||||||
|
@ -4030,7 +4030,7 @@ export type MangoV4 = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "frozenUntil",
|
"name": "frozenUntil",
|
||||||
"type": "i64"
|
"type": "u64"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "reserved",
|
"name": "reserved",
|
||||||
|
@ -7717,7 +7717,7 @@ export type MangoV4 = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IDL: MangoV4 = {
|
export const IDL: MangoV4 = {
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"name": "mango_v4",
|
"name": "mango_v4",
|
||||||
"instructions": [
|
"instructions": [
|
||||||
{
|
{
|
||||||
|
@ -11748,7 +11748,7 @@ export const IDL: MangoV4 = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "frozenUntil",
|
"name": "frozenUntil",
|
||||||
"type": "i64"
|
"type": "u64"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "reserved",
|
"name": "reserved",
|
||||||
|
|
|
@ -509,6 +509,45 @@ async function makePerpMarketReduceOnly() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function makePerpMarketUntrusted() {
|
||||||
|
const result = await buildAdminClient();
|
||||||
|
const client = result[0];
|
||||||
|
const admin = result[1];
|
||||||
|
const creator = result[2];
|
||||||
|
|
||||||
|
const group = await client.getGroupForCreator(creator.publicKey, GROUP_NUM);
|
||||||
|
const perpMarket = group.getPerpMarketByName('BTC-PERP');
|
||||||
|
await client.perpEditMarket(
|
||||||
|
group,
|
||||||
|
perpMarket.perpMarketIndex,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async function createAndPopulateAlt() {
|
async function createAndPopulateAlt() {
|
||||||
const result = await buildAdminClient();
|
const result = await buildAdminClient();
|
||||||
const client = result[0];
|
const client = result[0];
|
||||||
|
@ -669,6 +708,7 @@ async function main() {
|
||||||
try {
|
try {
|
||||||
// await registerPerpMarkets();
|
// await registerPerpMarkets();
|
||||||
// await makePerpMarketReduceOnly();
|
// await makePerpMarketReduceOnly();
|
||||||
|
// await makePerpMarketUntrusted();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue