Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion codegen/hyperdrive_codegen/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ class FileInfo:
TemplatePathInfo("interfaces/IYieldSource.sol.jinja", "", "interfaces"),
]

test_templates = [
TemplatePathInfo("tests/Test.t.sol.jinja", "Test", "test"),
TemplatePathInfo("tests/InstanceTest.t.sol.jinja", "InstanceTest", "test"),
]


def get_templates(env: Environment) -> list[TemplateInfo]:
"""Returns a list of template files for generating customized Hyperdrive instances.
Expand All @@ -76,7 +81,7 @@ def get_templates(env: Environment) -> list[TemplateInfo]:
"""

# Gather the template file strings and return a list of TemplateInfo's.
path_infos = deployer_templates + instance_templates + interface_templates
path_infos = deployer_templates + instance_templates + interface_templates + test_templates
return [TemplateInfo(template=env.get_template(path_info.path), path_info=path_info) for path_info in path_infos]


Expand All @@ -102,12 +107,19 @@ def write_templates_to_files(templates: list[TemplateInfo], output_path: Path, t

# Prepend 'I' to the file name if it is an interface file
is_interface_file = file_info.template.path_info.folder == "interfaces"
is_test_file = file_info.template.path_info.folder == "test"
if is_interface_file:
contract_file_name = "I" + contract_file_name
# NOTE: don't place interface files in a subfolder
contract_file_path = Path(
os.path.join(output_path, file_info.template.path_info.folder, contract_file_name)
)
# Put test files in test/instances
elif is_test_file:
# NOTE: don't place interface files in a subfolder
contract_file_path = Path(
os.path.join(file_info.template.path_info.folder, "instances", contract_file_name)
)
else:
contract_file_path = Path(
os.path.join(
Expand Down
159 changes: 159 additions & 0 deletions codegen/templates/tests/InstanceTest.t.sol.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.22;

import { {{ name.capitalized }}HyperdriveCoreDeployer } from "../../../contracts/src/deployers/{{ name.lowercase }}/{{ name.capitalized }}HyperdriveCoreDeployer.sol";
import { {{ name.capitalized }}HyperdriveDeployerCoordinator } from "../../../contracts/src/deployers/{{ name.lowercase }}/{{ name.capitalized }}HyperdriveDeployerCoordinator.sol";
import { {{ name.capitalized }}Target0Deployer } from "../../../contracts/src/deployers/{{ name.lowercase }}/{{ name.capitalized }}Target0Deployer.sol";
import { {{ name.capitalized }}Target1Deployer } from "../../../contracts/src/deployers/{{ name.lowercase }}/{{ name.capitalized }}Target1Deployer.sol";
import { {{ name.capitalized }}Target2Deployer } from "../../../contracts/src/deployers/{{ name.lowercase }}/{{ name.capitalized }}Target2Deployer.sol";
import { {{ name.capitalized }}Target3Deployer } from "../../../contracts/src/deployers/{{ name.lowercase }}/{{ name.capitalized }}Target3Deployer.sol";
import { {{ name.capitalized }}Target4Deployer } from "../../../contracts/src/deployers/{{ name.lowercase }}/{{ name.capitalized }}Target4Deployer.sol";
import { {{ name.capitalized }}Conversions } from "../../../contracts/src/instances/{{ name.lowercase }}/{{ name.capitalized }}Conversions.sol";
import { IERC20 } from "../../../contracts/src/interfaces/IERC20.sol";
import { I{{ name.capitalized }} } from "../../../contracts/src/interfaces/I{{ name.capitalized }}.sol";
import { IHyperdrive } from "../../../contracts/src/interfaces/IHyperdrive.sol";
import { ERC20ForwarderFactory } from "../../../contracts/src/token/ERC20ForwarderFactory.sol";
import { FixedPointMath } from "../../../contracts/src/libraries/FixedPointMath.sol";
import { ERC20ForwarderFactory } from "../../../contracts/src/token/ERC20ForwarderFactory.sol";
import { InstanceTest } from "../../utils/InstanceTest.sol";
import { HyperdriveUtils } from "../../utils/HyperdriveUtils.sol";
import { Lib } from "../../utils/Lib.sol";

abstract contract {{ name.capitalized }}InstanceTest is InstanceTest {
using FixedPointMath for uint256;
using HyperdriveUtils for IHyperdrive;
using Lib for *;

/// Overrides ///

/// @dev Gets the extra data used to deploy Hyperdrive instances.
/// @return The extra data.
function getExtraData() internal pure override returns (bytes memory) {
return new bytes(0);
}

/// @dev Converts base amount to the equivalent about in shares.
/// @param baseAmount The base amount.
/// @return The converted share amount.
function convertToShares(
uint256 baseAmount
) internal view override returns (uint256) {
// FIXME: add the correct conversion
return
{{ name.capitalized }}Conversions.convertToShares(
config.vaultSharesToken,
baseAmount
);
}

/// @dev Converts share amount to the equivalent amount in base.
/// @param shareAmount The share amount.
/// @return The converted base amount.
function convertToBase(
uint256 shareAmount
) internal view override returns (uint256) {
// FIXME: add the correct conversion
return
{{ name.capitalized }}Conversions.convertToBase(
config.vaultSharesToken,
shareAmount
);
}

/// @dev Deploys the rsETH Linea deployer coordinator contract.
/// @param _factory The address of the Hyperdrive factory.
/// @return The coordinator address.
function deployCoordinator(
address _factory
) internal override returns (address) {
vm.startPrank(alice);
return
address(
new {{ name.capitalized }}HyperdriveDeployerCoordinator(
string.concat(config.name, "DeployerCoordinator"),
_factory,
address(new {{ name.capitalized }}HyperdriveCoreDeployer()),
address(new {{ name.capitalized }}Target0Deployer()),
address(new {{ name.capitalized }}Target1Deployer()),
address(new {{ name.capitalized }}Target2Deployer()),
address(new {{ name.capitalized }}Target3Deployer()),
address(new {{ name.capitalized }}Target4Deployer())
)
);
}

/// @dev Fetches the total supply of the base and share tokens.
/// @return The total supply of base.
/// @return The total supply of vault shares.
function getSupply()
internal
view
virtual
override
returns (uint256, uint256)
{
return (
I{{ name.capitalized }}(address(config.vaultSharesToken)).totalAssets(),
I{{ name.capitalized }}(address(config.vaultSharesToken)).totalSupply()
);
}

/// @dev Fetches the token balance information of an account.
/// @param account The account to query.
/// @return The balance of base.
/// @return The balance of vault shares.
function getTokenBalances(
address account
) internal view override returns (uint256, uint256) {
return (
config.baseToken.balanceOf(account),
config.vaultSharesToken.balanceOf(account)
);
}

/// Getters ///

/// @dev Test for the additional getters. In the case of the {{ name.capitalized }}Hyperdrive
/// instance, there are no additional getter and we just test the
/// `totalShares` implementation.
function test_getters() external view {
// FIXME: add t.ests for additional getters here
(, uint256 totalShares) = getTokenBalances(address(hyperdrive));
assertEq(hyperdrive.totalShares(), totalShares);
}

/// Price Per Share ///

/// @dev Fuzz test that verifies that the vault share price is the price
/// that dictates the conversion between base and shares.
/// @param basePaid the fuzz parameter for the base paid.
function test__pricePerVaultShare(uint256 basePaid) external virtual {
// FIXME: add test for the price per vaul share here. {{ name.capitalized }} example
// is provided.
// Ensure that the share price is the expected value.
(uint256 totalBase, uint256 totalSupply) = getSupply();
uint256 vaultSharePrice = hyperdrive.getPoolInfo().vaultSharePrice;
assertApproxEqAbs(vaultSharePrice, totalBase.divDown(totalSupply), 1);

// Ensure that the share price accurately predicts the amount of shares
// that will be minted for depositing a given amount of shares. This will
// be an approximation.
basePaid = basePaid.normalizeToRange(
2 * hyperdrive.getPoolConfig().minimumTransactionAmount,
hyperdrive.calculateMaxLong()
);
(, uint256 hyperdriveSharesBefore) = getTokenBalances(
address(hyperdrive)
);
openLong(bob, basePaid);
(, uint256 hyperdriveSharesAfter) = getTokenBalances(
address(hyperdrive)
);
assertApproxEqAbs(
hyperdriveSharesAfter,
hyperdriveSharesBefore + basePaid.divDown(vaultSharePrice),
config.shareTolerance
);
}
}

Loading