Skip to content

Commit

Permalink
calculatePresentValue cleanup (#680)
Browse files Browse the repository at this point in the history
* Cleaned up the `calculatePresentValue` function

* Simplied the max buy case of `calculatePresentValue`

* Fixed the broken test

* Optimize the happy path of `calculateCloseLong` and `calculateCloseShort`

* Fixed the Rust tests

* Optimized `calculatePresentValue`

* Moved `permitForAll` to `HyperdriveTarget0`

* Addressed review feedback from @jrhea

* Re-applied review feedback from @jrhea

* Cleaned up the comments a bit more in `YieldSpaceMath`
  • Loading branch information
jalextowle authored Nov 21, 2023
1 parent 5264062 commit 22ab58e
Show file tree
Hide file tree
Showing 16 changed files with 505 additions and 311 deletions.
55 changes: 51 additions & 4 deletions contracts/src/external/Hyperdrive.sol
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.19;

import { HyperdriveTarget0 } from "../external/HyperdriveTarget0.sol";
import { IHyperdrive } from "../interfaces/IHyperdrive.sol";
import { IHyperdriveCore } from "../interfaces/IHyperdriveCore.sol";
import { HyperdriveAdmin } from "../internal/HyperdriveAdmin.sol";
import { HyperdriveBase } from "../internal/HyperdriveBase.sol";
import { HyperdriveCheckpoint } from "../internal/HyperdriveCheckpoint.sol";
import { HyperdriveLong } from "../internal/HyperdriveLong.sol";
import { HyperdriveLP } from "../internal/HyperdriveLP.sol";
import { HyperdrivePermitForAll } from "../internal/HyperdrivePermitForAll.sol";
import { HyperdriveShort } from "../internal/HyperdriveShort.sol";
import { HyperdriveStorage } from "../internal/HyperdriveStorage.sol";

Expand All @@ -20,7 +20,6 @@ import { HyperdriveStorage } from "../internal/HyperdriveStorage.sol";
/// particular legal or regulatory significance.
abstract contract Hyperdrive is
IHyperdriveCore,
HyperdrivePermitForAll,
HyperdriveAdmin,
HyperdriveLP,
HyperdriveLong,
Expand All @@ -36,6 +35,15 @@ abstract contract Hyperdrive is
/// some stateful functions.
address public immutable target1;

/// @notice The typehash used to calculate the EIP712 hash for `permitForAll`.
bytes32 public constant PERMIT_TYPEHASH =
keccak256(
"PermitForAll(address owner,address spender,bool _approved,uint256 nonce,uint256 deadline)"
);

/// @notice This contract's EIP712 domain separator.
bytes32 public immutable DOMAIN_SEPARATOR; // solhint-disable-line var-name-mixedcase

/// @notice Instantiates a Hyperdrive pool.
/// @param _config The configuration of the pool.
/// @param _target0 The target0 address.
Expand All @@ -44,10 +52,26 @@ abstract contract Hyperdrive is
IHyperdrive.PoolConfig memory _config,
address _target0,
address _target1
) HyperdriveStorage(_config) HyperdrivePermitForAll() {
) HyperdriveStorage(_config) {
// Initialize the target contracts.
target0 = _target0;
target1 = _target1;

// NOTE: It's convenient to keep this in the `Hyperdrive.sol`
// entry-point to avoiding issues with initializing the domain
// separator with the contract address. If this is moved to one of
// the targets, the domain separator will need to be computed
// differently.
DOMAIN_SEPARATOR = keccak256(
abi.encode(
keccak256(
"EIP712Domain(string version,uint256 chainId,address verifyingContract)"
),
keccak256(bytes("1")),
block.chainid,
address(this)
)
);
}

/// @notice If we get to the fallback function, we make a read-only
Expand Down Expand Up @@ -288,7 +312,30 @@ abstract contract Hyperdrive is
bytes32 r,
bytes32 s
) external {
_permitForAll(owner, spender, _approved, deadline, v, r, s);
(bool success, bytes memory result) = target0.delegatecall(
abi.encodeCall(
HyperdriveTarget0.permitForAll,
(
DOMAIN_SEPARATOR,
PERMIT_TYPEHASH,
owner,
spender,
_approved,
deadline,
v,
r,
s
)
)
);
if (!success) {
assembly {
revert(add(result, 32), mload(result))
}
}
assembly {
return(add(result, 32), mload(result))
}
}

/// Helpers ///
Expand Down
43 changes: 43 additions & 0 deletions contracts/src/external/HyperdriveTarget0.sol
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,49 @@ abstract contract HyperdriveTarget0 is
_batchTransferFrom(from, to, ids, values);
}

/// @notice Allows a caller who is not the owner of an account to execute the
/// functionality of 'approve' for all assets with the owners signature.
/// @param domainSeparator The EIP712 domain separator of the contract.
/// @param permitTypeHash The EIP712 domain separator of the contract.
/// @param owner The owner of the account which is having the new approval set.
/// @param spender The address which will be allowed to spend owner's tokens.
/// @param _approved A boolean of the approval status to set to.
/// @param deadline The timestamp which the signature must be submitted by
/// to be valid.
/// @param v Extra ECDSA data which allows public key recovery from
/// signature assumed to be 27 or 28.
/// @param r The r component of the ECDSA signature.
/// @param s The s component of the ECDSA signature.
/// @dev The signature for this function follows EIP 712 standard and should
/// be generated with the eth_signTypedData JSON RPC call instead of
/// the eth_sign JSON RPC call. If using out of date parity signing
/// libraries the v component may need to be adjusted. Also it is very
/// rare but possible for v to be other values, those values are not
/// supported.
function permitForAll(
bytes32 domainSeparator,
bytes32 permitTypeHash,
address owner,
address spender,
bool _approved,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external {
_permitForAll(
domainSeparator,
permitTypeHash,
owner,
spender,
_approved,
deadline,
v,
r,
s
);
}

/// Getters ///

/// @notice Gets the base token.
Expand Down
3 changes: 1 addition & 2 deletions contracts/src/instances/ERC4626Base.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ abstract contract ERC4626Base is HyperdriveBase {
// Deposit the base into the yield source.
ERC20(address(_baseToken)).safeApprove(address(_pool), _amount);
sharesMinted = _pool.deposit(_amount, address(this));
sharePrice = _pricePerShare();
} else {
// WARN: This logic doesn't account for slippage in the conversion
// from base to shares. If deposits to the yield source incur
Expand All @@ -73,8 +72,8 @@ abstract contract ERC4626Base is HyperdriveBase {
address(this),
sharesMinted
);
sharePrice = _pricePerShare();
}
sharePrice = _pricePerShare();
}

/// @notice Processes a trader's withdrawal in either base or vault shares.
Expand Down
12 changes: 7 additions & 5 deletions contracts/src/internal/HyperdriveLP.sol
Original file line number Diff line number Diff line change
Expand Up @@ -278,11 +278,13 @@ abstract contract HyperdriveLP is HyperdriveBase, HyperdriveMultiToken {
);

// Mint the withdrawal shares to the LP.
_mint(
AssetId._WITHDRAWAL_SHARE_ASSET_ID,
_options.destination,
withdrawalShares
);
if (withdrawalShares > 0) {
_mint(
AssetId._WITHDRAWAL_SHARE_ASSET_ID,
_options.destination,
withdrawalShares
);
}

// Withdraw the shares from the yield source.
proceeds = _withdraw(shareProceeds, _options);
Expand Down
2 changes: 1 addition & 1 deletion contracts/src/internal/HyperdriveLong.sol
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,8 @@ abstract contract HyperdriveLong is HyperdriveLP {
uint256 _checkpointTime,
uint256 _maturityTime
) internal {
uint128 longsOutstanding_ = _marketState.longsOutstanding;
// Update the average maturity time of long positions.
uint128 longsOutstanding_ = _marketState.longsOutstanding;
_marketState.longAverageMaturityTime = uint256(
_marketState.longAverageMaturityTime
)
Expand Down
70 changes: 70 additions & 0 deletions contracts/src/internal/HyperdriveMultiToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,76 @@ abstract contract HyperdriveMultiToken is HyperdriveBase {
emit TransferSingle(msg.sender, from, address(0), tokenID, amount);
}

/// @dev Allows a caller who is not the owner of an account to execute the
/// functionality of 'approve' for all assets with the owners signature.
/// @param domainSeparator The EIP712 domain separator for this contract.
/// @param permitTypehash The EIP712 typehash for the permit data.
/// @param owner The owner of the account which is having the new approval set.
/// @param spender The address which will be allowed to spend owner's tokens.
/// @param _approved A boolean of the approval status to set to.
/// @param deadline The timestamp which the signature must be submitted by
/// to be valid.
/// @param v Extra ECDSA data which allows public key recovery from
/// signature assumed to be 27 or 28.
/// @param r The r component of the ECDSA signature.
/// @param s The s component of the ECDSA signature.
/// @dev The signature for this function follows EIP 712 standard and should
/// be generated with the eth_signTypedData JSON RPC call instead of
/// the eth_sign JSON RPC call. If using out of date parity signing
/// libraries the v component may need to be adjusted. Also it is very
/// rare but possible for v to be other values, those values are not
/// supported.
function _permitForAll(
bytes32 domainSeparator,
bytes32 permitTypehash,
address owner,
address spender,
bool _approved,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
// Require that the signature is not expired.
if (block.timestamp > deadline) {
revert IHyperdrive.ExpiredDeadline();
}

// Require that the owner is not zero.
if (owner == address(0)) {
revert IHyperdrive.RestrictedZeroAddress();
}

// Check that the signature is valid and recovers to the owner.
bytes32 structHash = keccak256(
abi.encodePacked(
"\x19\x01",
domainSeparator,
keccak256(
abi.encode(
permitTypehash,
owner,
spender,
_approved,
_nonces[owner],
deadline
)
)
)
);
address signer = ecrecover(structHash, v, r, s);
if (signer != owner) revert IHyperdrive.InvalidSignature();

// Increment the signature nonce.
++_nonces[owner];

// Set the state.
_isApprovedForAll[owner][spender] = _approved;

// Emit an event to track approval.
emit ApprovalForAll(owner, spender, _approved);
}

/// @notice Derive the ERC20 forwarder address for a provided `tokenId`.
/// @param tokenId Token Id of the token whose forwarder contract address
/// need to derived.
Expand Down
113 changes: 0 additions & 113 deletions contracts/src/internal/HyperdrivePermitForAll.sol

This file was deleted.

Loading

0 comments on commit 22ab58e

Please sign in to comment.