Closed adrena-orex closed 1 year ago
I agree, I didn't include AUM updates in the original implementation because I wasn't sure if the difference in collected fees justifies heavier instructions (in both compute units and number of accounts passed). But we can include that it if you think it is necessary. Also please make sure these commands succeed:
anchor test -- --features test
cargo clippy +nightly --workspace --all-targets -- --deny=warnings
Fixed clippy warnings 🙏
cargo test -- --nocapture
runs fine.
Having issues running anchor test -- --features test
in local 🤔 (checked with 1.14.18
as well)
orex> anchor test -- --features test
Warning: cargo-build-bpf is deprecated. Please, use cargo-build-sbf
cargo-build-bpf child: /Users/orex/.local/share/solana/install/active_release/bin/cargo-build-sbf --features test --arch bpf
Compiling perpetuals v0.1.0 (/Users/orex/work/perpetuals-copy-to-do-pr-on-main/programs/perpetuals)
Finished release [optimized] target(s) in 13.09s
Found a 'test' script in the Anchor.toml. Running it as a test suite!
Running test suite: "/Users/orex/work/perpetuals-copy-to-do-pr-on-main/Anchor.toml"
Unable to get latest blockhash. Test validator does not look started. Check .anchor/test-ledger/test-ledger-log.txt for errors. Consider increasing [test.startup_wait] in Anchor.toml.
Stake By Version:
1.16.4 - 2 current validators (24.99%)
1.14.19 - 5 current validators (74.93%) 1 delinquent validators ( 0.07%)
1.14.18 - 1 current validators ( 0.00%)
1.14.16 - 0 current validators ( 0.00%) 1 delinquent validators ( 0.00%)
1.14.13 - 0 current validators ( 0.00%) 1 delinquent validators ( 0.00%)
1.13.6 - 0 current validators ( 0.00%) 1 delinquent validators ( 0.00%)
unknown - 0 current validators ( 0.00%) 34 delinquent validators ( 0.01%)
orex> solana --version
solana-cli 1.14.19 (src:5704dd6e; feat:1879391783)
did you check .anchor/test-ledger/test-ledger-log.txt for errors?
also, I'm still thinking if making more instructions super heavy is a good idea. The only problem we solve is making fees more accurate. If we don't update aum then sometimes users will pay more, sometimes less, in a long run it will cancels out. What if you add a permissionless update_aum
instruction, so it will be called periodically by crank or from UI if AUM is not updated for a while.
Thx found a fix about my error
Error I got:
orex> cat .anchor/test-ledger/test-ledger-log.txt
Ledger location: .anchor/test-ledger
Log: .anchor/test-ledger/validator.log
Initializing...
Error: failed to start validator: Failed to create ledger at .anchor/test-ledger: blockstore error
Fixed it using:
brew install gnu-tar
# Put this in ~/.zshrc
export PATH="/opt/homebrew/opt/gnu-tar/libexec/gnubin:$PATH"
did you check .anchor/test-ledger/test-ledger-log.txt for errors? also, I'm still thinking if making more instructions super heavy is a good idea. The only problem we solve is making fees more accurate. If we don't update aum then sometimes users will pay more, sometimes less, in a long run it will cancels out. What if you add a permissionless
update_aum
instruction, so it will be called periodically by crank or from UI if AUM is not updated for a while.
Long run it cancels out yes, but I'm more concern about the unfairness to users. One paying more than another when it shouldn't.
Also, right now pool.aum_usd
is updated only when adding/remove liquidity, so I guess not very often in real case, leading to inconsistent fees.
Crank is an interesting idea.
The longer the time between two update_aum
call is, the wronger the fee calculation becomes.
Looking at asset price changes on pyth, price doesn't shift by huge amount every second, so I think the fee variation should be acceptable for few seconds.
Note:
1 call every 3s using clockwork equals to 10.512 SOL of transaction fees per year. 1 call every 10s using clockwork equals to 3.1536 SOL of transaction fees per year.
Pool having:
pool.aum_usd
== $4_800_000
ratios:
Let's take a swap_in
fee of 100
BPS and a swap_out
fee of 100
BPS, configured with FeesMode::Linear
.
Let's swap 1_500 SOL for USDC.
With pool.aum_usd
updated:
fees_in
: 19_500_000_000 (19.5 SOL).With ETH price took +0.1%, BTC price took +0.05%, SOL price took +0.2%, USDC price even and non-updated pool.aum_usd
:
pool.aum_usd
= $4_804_025fees_in
: 19_350_000_000
(19.35 SOL). Difference of 0.7692307692% in the fees.With ETH price took +1%, BTC price took +0.5%, SOL price took +2%, USDC price even and non-updated pool.aum_usd
:
pool.aum_usd
= $4_840_250fees_in
: 17_850_000_000 (17.85 SOL). Difference of 8.4615384615% in the fees.I think your example shows that it can drift quite heavily for small price changes, I would go for updating the pool_aum to reflect the state as close as possible, especially if we can rely on clockwork to do that (in our fork we do rely on clockwork already to automate some tasks).
Wdyt @askibin ?
I also believe that keep the pool_aum up to date is important for composition and extensions on the codebase in need of this metric
I propose you add a permissionless update_aum instruction that you can prepend (when construction a tx in the client) before the actual interaction (trade, swap, etc.).
This is defiently better and reduces attack vectors on mutliple fronts however most of position management instructions can be made efficient with better fee modelling while instrcutions associated to the pool distribution need to necessarily update the pool aum instantaneously.
@askibin updated the PR.
update_pool_aum
instruction.update_pool_aum
in testsRefCell
to RwLock
in tests.
Issue
In fee calculation, ratios are computed in order to increase/decrease fees:
https://github.com/solana-labs/perpetuals/blob/43ed8424e816c9a89285f4f7267a52574fd7e651/programs/perpetuals/src/state/pool.rs#L937
https://github.com/solana-labs/perpetuals/blob/43ed8424e816c9a89285f4f7267a52574fd7e651/programs/perpetuals/src/state/pool.rs#L813
If we look at
get_current_ratio
function, we use the latest custody token price, but an outdatedaum_usd
.Soluce
pool.aum_usd
should be refreshed at the start of each ix paying fees. So when fees are calculated, thepool.aum_usd
is accurate and ratios are corrects.Demonstration
Imagine the initial state of the pool to be:
Now after some time, prices changes, and users execute
add_collateral
New prices:
When the calculation run in
add_collateral
for its ETH position, it will calculate thecurrent_ratio
as:current_ratio
= ((1_600 100) 10_000) / 4_150_000 = 385.542168675when it should have calculated the current ratio using the updated
pool.aum_usd
such as:Updated
pool.aum_usd
: (1_600 100) + (32_000 100) + 1_000_000 = 4_360_000current_ratio
= ((1_600 100) 10_000) / 4_360_000 = 366.972477064Note
This PR also include a refresh of
pool.aum_usd
at the end ofclose_position
andliquidate
because I think theses both ix may have impactfull impact onpool.aum_usd
due to the moving collateral and change ofassets.owned