Skip to content

Commit

Permalink
Added several tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jalextowle committed Sep 25, 2023
1 parent 078bd65 commit dc2cf66
Show file tree
Hide file tree
Showing 2 changed files with 265 additions and 5 deletions.
6 changes: 3 additions & 3 deletions crates/hyperdrive-math/tests/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,10 @@ pub async fn test_integration_get_max_short() -> Result<()> {
bob.open_short(max_short, None).await?;

if max_short != global_max_short {
// We currently allow up to a tolerance of 0.1%, which means
// that the max short is always consuming at least 99% of
// We currently allow up to a tolerance of 3%, which means
// that the max short is always consuming at least 97% of
// the budget.
let error_tolerance = fixed!(0.01e18);
let error_tolerance = fixed!(0.03e18);
assert!(
bob.base() < budget * (fixed!(1e18) - slippage_tolerance) * error_tolerance,
"expected (base={}) < (budget={}) * {} = {}",
Expand Down
264 changes: 262 additions & 2 deletions test/integrations/hyperdrive/PresentValueTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import { IHyperdrive, HyperdriveTest, HyperdriveUtils } from "test/utils/Hyperdr
import { Lib } from "test/utils/Lib.sol";

contract PresentValueTest is HyperdriveTest {
using Lib for *;
using FixedPointMath for *;
using HyperdriveUtils for *;
using Lib for *;

bytes32 internal _seed;

Expand Down Expand Up @@ -488,7 +489,7 @@ contract PresentValueTest is HyperdriveTest {
uint256 nextPresentValue;

// Execute a series of random open trades. We ensure that the present
// value does not decrease.
// value stays the same.
Trade[] memory trades = randomOpenTrades();
for (uint256 i = 0; i < trades.length; i++) {
executeTrade(trades[i]);
Expand Down Expand Up @@ -523,6 +524,228 @@ contract PresentValueTest is HyperdriveTest {
}
}

function test_present_value(bytes32 __seed) external {
// TODO: This tolerance is WAY too large.
uint256 tolerance = 1_000_000e18;

// Set the seed.
_seed = __seed;

// Initialize the pool.
initialize(alice, 0.02e18, 500_000_000e18);

// Execute a series of random open trades. We ensure that the present
// value stays within a given tolerance.
uint256 nextPresentValue;
uint256 currentPresentValue = HyperdriveUtils.presentValue(hyperdrive);
uint256 maturityTime0 = hyperdrive.maturityTimeFromLatestCheckpoint();
Trade[] memory trades0 = randomOpenTrades();
for (uint256 i = 0; i < trades0.length; i++) {
executeTrade(trades0[i]);
nextPresentValue = HyperdriveUtils.presentValue(hyperdrive);
assertApproxEqAbs(nextPresentValue, currentPresentValue, tolerance);
currentPresentValue = nextPresentValue;
}

// Time passes and interest accrues.
{
uint256 timeDelta = uint256(seed()).normalizeToRange(
CHECKPOINT_DURATION,
POSITION_DURATION.mulDown(0.99e18)
);
int256 variableRate = int256(uint256(seed())).normalizeToRange(
-0.1e18,
2e18
);
advanceTime(timeDelta, variableRate);
}

// Execute a series of random open trades. We ensure that the present
// value stays within a given tolerance.
currentPresentValue = HyperdriveUtils.presentValue(hyperdrive);
uint256 maturityTime1 = hyperdrive.maturityTimeFromLatestCheckpoint();
Trade[] memory trades1 = randomOpenTrades();
for (uint256 i = 0; i < trades1.length; i++) {
executeTrade(trades1[i]);
nextPresentValue = HyperdriveUtils.presentValue(hyperdrive);
assertApproxEqAbs(nextPresentValue, currentPresentValue, tolerance);
currentPresentValue = nextPresentValue;
}

// Construct a set of close trades.
Trade[] memory closeTrades;
{
Trade[] memory closeTrades0 = randomCloseTrades(
maturityTime0,
hyperdrive.balanceOf(
AssetId.encodeAssetId(
AssetId.AssetIdPrefix.Long,
maturityTime0
),
alice
),
maturityTime0,
hyperdrive.balanceOf(
AssetId.encodeAssetId(
AssetId.AssetIdPrefix.Short,
maturityTime0
),
alice
)
);
Trade[] memory closeTrades1 = randomCloseTrades(
maturityTime1,
hyperdrive.balanceOf(
AssetId.encodeAssetId(
AssetId.AssetIdPrefix.Long,
maturityTime1
),
alice
),
maturityTime1,
hyperdrive.balanceOf(
AssetId.encodeAssetId(
AssetId.AssetIdPrefix.Short,
maturityTime1
),
alice
)
);
closeTrades = combineTrades(closeTrades0, closeTrades1);
}

// Execute a series of random close trades. We ensure that the present
// value stays within a given tolerance.
currentPresentValue = HyperdriveUtils.presentValue(hyperdrive);
for (uint256 i = 0; i < closeTrades.length; i++) {
executeTrade(closeTrades[i]);
nextPresentValue = HyperdriveUtils.presentValue(hyperdrive);
assertApproxEqAbs(nextPresentValue, currentPresentValue, tolerance);
currentPresentValue = nextPresentValue;
}
}

function test_path_independence(bytes32 __seed) external {
// Set the seed.
_seed = __seed;

// Initialize the pool.
initialize(alice, 0.02e18, 500_000_000e18);

// Execute a series of random open trades.
uint256 maturityTime0 = hyperdrive.maturityTimeFromLatestCheckpoint();
Trade[] memory trades0 = randomOpenTrades();
for (uint256 i = 0; i < trades0.length; i++) {
executeTrade(trades0[i]);
}

// Time passes and interest accrues.
{
uint256 timeDelta = uint256(seed()).normalizeToRange(
CHECKPOINT_DURATION,
POSITION_DURATION.mulDown(0.99e18)
);
int256 variableRate = int256(uint256(seed())).normalizeToRange(
0,
1e18
);
advanceTime(timeDelta, variableRate);
}

// Execute a series of random open trades.
uint256 maturityTime1 = hyperdrive.maturityTimeFromLatestCheckpoint();
Trade[] memory trades1 = randomOpenTrades();
for (uint256 i = 0; i < trades1.length; i++) {
executeTrade(trades1[i]);
}

// Close all of the positions in a random order and record the ending
// pool info.
IHyperdrive.PoolInfo memory info0;
Trade[] memory closeTrades;
{
uint256 snapshotId = vm.snapshot();

// Construct a set of close trades.
{
Trade[] memory closeTrades0 = randomCloseTrades(
maturityTime0,
hyperdrive.balanceOf(
AssetId.encodeAssetId(
AssetId.AssetIdPrefix.Long,
maturityTime0
),
alice
),
maturityTime0,
hyperdrive.balanceOf(
AssetId.encodeAssetId(
AssetId.AssetIdPrefix.Short,
maturityTime0
),
alice
)
);
Trade[] memory closeTrades1 = randomCloseTrades(
maturityTime1,
hyperdrive.balanceOf(
AssetId.encodeAssetId(
AssetId.AssetIdPrefix.Long,
maturityTime1
),
alice
),
maturityTime1,
hyperdrive.balanceOf(
AssetId.encodeAssetId(
AssetId.AssetIdPrefix.Short,
maturityTime1
),
alice
)
);
closeTrades = combineTrades(closeTrades0, closeTrades1);
}

// Execute a series of random close trades. We ensure that the present
// value stays within a given tolerance.
for (uint256 i = 0; i < closeTrades.length; i++) {
executeTrade(closeTrades[i]);
}

// Record the ending pool info.
info0 = hyperdrive.getPoolInfo();

vm.revertTo(snapshotId);
}

// Advance the seed a few times to ensure different trades in the next
// round.
for (uint256 i = 0; i < 5; i++) {
seed();
}

// Close all of the positions in a different random order and record the
// ending pool info.
IHyperdrive.PoolInfo memory info1;
{
// Reorder the trades used in the last round and execute them.
closeTrades = reorderTrades(closeTrades);
for (uint256 i = 0; i < closeTrades.length; i++) {
executeTrade(closeTrades[i]);
}

// Record the ending pool info.
info1 = hyperdrive.getPoolInfo();
}

// Ensure that the reserves are approximately the same across the two
// rounds of trading.
assertApproxEqAbs(info0.shareReserves, info1.shareReserves, 1e12);
assertApproxEqAbs(info0.bondReserves, info1.bondReserves, 1e12);
assertApproxEqAbs(info0.shareAdjustment, info1.shareAdjustment, 1e12);
}

/// Random Trading ///

enum TradeType {
Expand Down Expand Up @@ -602,6 +825,43 @@ contract PresentValueTest is HyperdriveTest {
return trades;
}

function reorderTrades(
Trade[] memory source
) internal returns (Trade[] memory result) {
result = new Trade[](source.length);
for (uint256 i = 0; i < result.length; i++) {
// Sample a random index and copy from the source array to the new
// array.
uint256 sourceLength = source.length - i;
uint256 idx = uint256(seed()).normalizeToRange(0, sourceLength - 1);
result[i] = source[idx];

// Move the last element of the source array to the sampled index.
source[idx] = source[sourceLength - 1];
}
}

function combineTrades(
Trade[] memory a,
Trade[] memory b
) internal returns (Trade[] memory result) {
result = new Trade[](a.length + b.length);

uint256 aIdx = 0;
uint256 bIdx = 0;
for (uint256 i = 0; i < result.length; i++) {
if (aIdx >= a.length) {
result[i] = b[bIdx++];
} else if (bIdx >= b.length) {
result[i] = a[aIdx++];
} else if (uint256(seed()) % 2 == 0) {
result[i] = a[aIdx++];
} else {
result[i] = b[bIdx++];
}
}
}

function seed() internal returns (bytes32 seed_) {
seed_ = _seed;
_seed = keccak256(abi.encodePacked(seed_));
Expand Down

0 comments on commit dc2cf66

Please sign in to comment.