hats-finance / VMEX-0xb6861bdeb368a1bf628fc36a36cec62d04fb6a77

LP token lending protocol
MIT License
2 stars 4 forks source link

Some stable velo pool oracles destabilize with massive swaps #12

Open hats-bug-reporter[bot] opened 1 year ago

hats-bug-reporter[bot] commented 1 year ago

Github username: -- Submission hash (on-chain): 0x57cb4226d489216ce6c5eda56f994c7ebc21c9b3f23f9c9cb171685c79c10a61 Severity: high severity

Description: Description\ Swapping tokens in some stable velo pools (namely, the USDC/USDT pool, 0x2B47C794c3789f499D8A54Ec12f949EeCCE8bA16) causes the reported price by the oracle to change drastically.

See below output of test

currentAsset:  0x2B47C794c3789f499D8A54Ec12f949EeCCE8bA16
Pricing  velo_USDTUSDC
Price:  BigNumber { value: "199993158704736408212" }
Naive pricing:  199993301239618960000
percent diff:  7.126987917041003e-7
stable
try swapping  0x7F5c764cBc14f9669B88837ca1490cCa17c31607
amount to swap:  5000000000
manip price 1:  BigNumber { value: "199995284623978362488" } percent diff:  0.000010629959823276315
Naive pricing after big swap:  2.605232473087505e+23
amount to swap:  25000000000
manip price 1:  BigNumber { value: "237505466159266789674" } percent diff:  0.1875679533114049
Naive pricing after big swap:  1.5626493404792083e+24
amount to swap:  125000000000
manip price 1:  BigNumber { value: "684396002421037701868" } percent diff:  2.4220970699875712
Naive pricing after big swap:  8.073279806331553e+24

Attack Scenario\ Flashloan huge amount of USDC, deposit to pool, overborrow on VMEX, repay flashloan.

  1. Proof of Concept (PoC) File (test suite)

  2. Revised Code File (Optional)

Files:

ksyao2002 commented 1 year ago

Looks like you need over 100 billion to swap before the price doubles. Doesn't seem like USDC or USDT even have that much in circulation, much less in a single protocol that allows flashloaning.

Using 25 billion increases the price by around 19%. Again doing this swap is extremely difficult. Looks like all other tokens are immune to this though.

ksyao2002 commented 1 year ago

Found out the reason behind the price difference: it's because velodrome sometimes allows huge swaps that causes the value of K to deviate:

The state of the reserve is initially:

[
  BigNumber { value: "1000000" },
  BigNumber { value: "1000000" },
  BigNumber { value: "1881147701861" },
  BigNumber { value: "1956543407411" },
  true,
  '0x7F5c764cBc14f9669B88837ca1490cCa17c31607',
  '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58',
  dec0: BigNumber { value: "1000000" },
  dec1: BigNumber { value: "1000000" },
  r0: BigNumber { value: "1881147701861" },
  r1: BigNumber { value: "1956543407411" },
  st: true,
  t0: '0x7F5c764cBc14f9669B88837ca1490cCa17c31607',
  t1: '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58'
]

Then, after a huge swap, this is the state:

amount to swap:  78125000000
metadata:  [
  BigNumber { value: "1000000" },
  BigNumber { value: "1000000" },
  BigNumber { value: "97608053647701861" },
  BigNumber { value: "1" },
  true,
  '0x7F5c764cBc14f9669B88837ca1490cCa17c31607',
  '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58',
  dec0: BigNumber { value: "1000000" },
  dec1: BigNumber { value: "1000000" },
  r0: BigNumber { value: "97608053647701861" },
  r1: BigNumber { value: "1" },
  st: true,
  t0: '0x7F5c764cBc14f9669B88837ca1490cCa17c31607',
  t1: '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58'
]
totalSupply:  BigNumber { value: "1918845263070" }
stable
Expected price:  483984971416333800000
percentDiff math:  0.5867781635461193
percent diff in K:  33.297853904096876
manip price 1:  BigNumber { value: "483984971416333866564" }
percent diff:  1.420007637015594

It's easy to see that K is much different, and that causes the pricing to be different cause it assumes that K is constant on swaps.

Consequence: we need to evaluate all pools for the ability to change K easily.