Skip to content

Commit

Permalink
LiquidStone End-to-End Testing (#172)
Browse files Browse the repository at this point in the history
* feat: Update Plume and Arbitrum config
* feat: Add LiquidStone test case for 90 day variation
---------
Co-authored-by: nawar-hisso <[email protected]>
  • Loading branch information
lucasia authored Dec 3, 2024
1 parent 968c1f3 commit 7420746
Show file tree
Hide file tree
Showing 25 changed files with 32,869 additions and 5,398 deletions.
6 changes: 6 additions & 0 deletions packages/contracts/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ deploy-plumeTestnet:
resume-plumeTestnet:
$(MAKE) deploy RPC_URL=plumeTestnet EXTRA_FLAGS="--verify --verifier blockscout --verifier-url https://test-explorer.plumenetwork.xyz/api\? --legacy --skip-simulation --slow --resume"

deploy-plume:
$(MAKE) deploy RPC_URL=plume EXTRA_FLAGS="--verify --verifier blockscout --verifier-url https://phoenix-explorer.plumenetwork.xyz/api\? --legacy --skip-simulation --slow"

resume-plume:
$(MAKE) deploy RPC_URL=plume EXTRA_FLAGS="--verify --verifier blockscout --verifier-url https://phoenix-explorer.plumenetwork.xyz/api\? --legacy --skip-simulation --slow --resume"

deploy-arbitrum:
$(MAKE) deploy RPC_URL=arbitrum EXTRA_FLAGS="--verify --slow"

Expand Down
2 changes: 2 additions & 0 deletions packages/contracts/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ polygon = "https://polygon-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}"
polygonMumbai = "https://polygon-mumbai.g.alchemy.com/v2/${ALCHEMY_API_KEY}"
bitlayer = "https://rpc.bitlayer.org"
bitlayerTestnet = "https://testnet-rpc.bitlayer.org"
plume = "https://phoenix-rpc.plumenetwork.xyz/${PLUME_API_KEY}"
plumeDevnet = "https://devnet-rpc.plumenetwork.xyz"
plumeTestnet_old = "https://testnet-rpc.plumenetwork.xyz"
plumeTestnet= "https://test-rpc.plumenetwork.xyz"
Expand All @@ -35,6 +36,7 @@ arbitrum = { key = "${ARB_ETHERSCAN_API_KEY}", url = "https://api.arbiscan.io/ap
arbitrumSepolia = { key = "${ARB_SEPOLIA_ETHERSCAN_API_KEY}", url = "https://api-sepolia.arbiscan.io/api", chain=421614 }
bitlayer = { key = "bitlayer", url="https://rpc.bitlayer.org" }
bitlayerTestnet = { key = "bitlayerTestnet", url="https://testnet-scan.bitlayer.org" }
plume = { key = "plume", url = 'https://phoenix-explorer.plumenetwork.xyz/api\?', chain=98865 }
plumeDevnet = { key = "plumeDevnet", url = 'https://devnet-explorer.plumenetwork.xyz/api\?', chain=18230 }
plumeTestnet_old = { key = "plumeTestnet_old", url = 'https://testnet-explorer.plumenetwork.xyz/api\?', chain=161221135 }
plumeTestnet = { key = "plumeTestnet", url = 'https://test-explorer.plumenetwork.xyz/api\?', chain=98864 }
41 changes: 41 additions & 0 deletions packages/contracts/resource/plumeMainnet.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
##
# The Application Configuration for the Plume Mainnet Environment.
##

[evm]
# blockchain id, e.g. plumeMainnet=98865, plumeTestnet=98864, plumeTestnet_old=161221135, plumeDevnet=18230
chain_id = 98865
deploy_mocks = false


[evm.address]
# credbull.cb.id admin/owner (#1)
owner = "0x0fEcd2f7B3EC4BeE8001B4F73df32e7917d8fdE3"
# credbulloper.cb.id operator (#2)
operator = "0xf589233a140F037976f2786C8A4Fba4920EB687b"
# credbullupgrader.cb.id upgrader (#4)
upgrader = "0x3E449960Ba36cB00B728A370F991c658e7cca459"
# credbullassetmgr.cb.id asset manager (#7)
asset_manager = "0x97BE1b79AA9dB55e7235a9dd5E686cc4A26A7959"

# USDC.e token address (Fiat Proxy
usdc_token="0x0F3B6CC558A714ecf4Cc9ec8caFF0b57ECf65890"
# Liquid Continuous Multi Token Vault Proxy
liquid_vault_proxy="0xb89846b74f3B190F6e00fc35B3aFfCDF5d4BB9f9"
# Liquid Continuous Multi Token Vault Impl
liquid_vault_impl="0x8f87E1258d645d948F270221C34202Be0583F29b"


[evm.contracts.liquid_continuous_multi_token_vault]
# rate in basis points, e.g. 10% = 1000 bps
full_rate_bps = 10_00
# rate in basis points, e.g. 5.5% = 550 bps
reduced_rate_bps = 5_50
# January 1, 2025 2:00:00 PM UTC = 1735740000
vault_start_timestamp = 1735740000

[services.supabase]
url = ""

# Save the contract deployment details to the database.
update_contract_addresses = false
14 changes: 13 additions & 1 deletion packages/contracts/resource/plumeTestnet.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[evm]
# blockchain id, e.g. plumeTestnet=98864, plumeTestnet_old=161221135, plumeDevnet=18230
chain_id = 98864
deploy_mocks = true
deploy_mocks = false

[evm.address]
# credbull-devops wallets. wallet numbers are 1-based (as opposed to 0-based in anvil)
Expand All @@ -19,12 +19,24 @@ custodian = "0x8561845F6a9511cD8e2daCae77A961e718A77cF6"
upgrader = "0xaD3C004eE1f942BFDA2DA0D2DAaC94d6aC012F75"
# devops asset manager (wallet 7) - public address, okay to share
asset_manager = "0xd097E901FB9B75C2d2f97E142d73fA79C31FcAb3"
# CBL token address - Plume Testnet
cbl_token="0x931Cf9ab674bAbfa7De712EE635b75b5636b4D29"
# USDC.e token address - Plume Testnet
usdc_token="0x401eCb1D350407f13ba348573E5630B83638E30D"
# Liquid Continuous Multi Token Vault Proxy - Plume Testnet
liquid_vault_proxy="0x4B1fC984F324D2A0fDD5cD83925124b61175f5C6"
# Liquid Continuous Multi Token Vault Impl - Plume Testnet
liquid_vault_impl="0x9Db9df1D91c5cdE0c92cf02B9992d42f47028b4A"


[evm.contracts.liquid_continuous_multi_token_vault]
# rate in basis points, e.g. 10% = 1000 bps
full_rate_bps = 10_00
# rate in basis points, e.g. 5.5% = 550 bps
reduced_rate_bps = 5_50
# January 1, 2024 2:00:00 PM UTC = 1704117600
vault_start_timestamp = 1704117600


[evm.contracts.upside_vault]
# 2 decimal place percentage (meaining value divided by 100) as integer.
Expand Down
50 changes: 50 additions & 0 deletions packages/contracts/resource/testnetArbSepolia.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
##
# The Application Configuration for the TestNet Environment.
##

[evm]
# blockchain id, e.g. baseSepolia=84532, arbSepolia=421614
chain_id = 421614
deploy_mocks = false

[evm.address]
# credbull-devops wallets. wallet numbers are 1-based (as opposed to 0-based in anvil)
# devops admin/owner (wallet 1) - public address, okay to share
owner = "0xD79Be36f61fce3B8EF2FBF22b13B2b9a68eE15A2"
# devops operator (wallet 2) - public address, okay to share
operator = "0xaD3C004eE1f942BFDA2DA0D2DAaC94d6aC012F75"
# devops custodian (wallet 3) - public address, okay to share
custodian = "0x8561845F6a9511cD8e2daCae77A961e718A77cF6"
# devops upgrader (wallet 4) - public address, okay to share
upgrader = "0xaD3C004eE1f942BFDA2DA0D2DAaC94d6aC012F75"
# devops asset manager (wallet 7) - public address, okay to share
asset_manager = "0xd097E901FB9B75C2d2f97E142d73fA79C31FcAb3"
# CBL token address - Arbitrum Sepolia
cbl_token="0x3Dd53Ec7DFff8cf774391867C83583E634363345"
# USDC token address - Arbitrum Sepolia
usdc_token="0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d"

[evm.contracts.liquid_continuous_multi_token_vault]
# rate in basis points, e.g. 10% = 1000 bps
full_rate_bps = 10_00
# rate in basis points, e.g. 5.5% = 550 bps
reduced_rate_bps = 5_50

[evm.contracts.upside_vault]
# 2 decimal place percentage (meaining value divided by 100) as integer.
collateral_percentage = 200

[evm.contracts.cbl]
# CBL token params
# devops admin/owner (wallet 1) - public address, okay to share
owner = "0xD79Be36f61fce3B8EF2FBF22b13B2b9a68eE15A2"
# devops operator (wallet 2) - public address, okay to share
minter = "0xaD3C004eE1f942BFDA2DA0D2DAaC94d6aC012F75"
# CBL token params
max_supply = 10_000_000 # 10 million in wei

[services.supabase]
url = ""

# Save the contract deployment details to the database.
update_contract_addresses = false
7 changes: 4 additions & 3 deletions packages/contracts/script/DeployLiquidMultiTokenVault.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,13 @@ contract DeployLiquidMultiTokenVault is TomlConfig {
IERC20Metadata asset,
IYieldStrategy yieldStrategy,
IRedeemOptimizer redeemOptimizer
) public view returns (LiquidContinuousMultiTokenVault.VaultParams memory vaultParams_) {
) public view virtual returns (LiquidContinuousMultiTokenVault.VaultParams memory vaultParams_) {
uint256 fullRateBasisPoints = _tomlConfig.readUint(string.concat(CONTRACT_TOML_KEY, ".full_rate_bps"));
uint256 reducedRateBasisPoints = _tomlConfig.readUint(string.concat(CONTRACT_TOML_KEY, ".reduced_rate_bps"));
uint256 startTimestamp = _startTimestamp();

uint256 scale = 10 ** asset.decimals();
uint256 decimals = asset.decimals();
uint256 scale = 10 ** decimals;

TripleRateContext.ContextParams memory contextParams = TripleRateContext.ContextParams({
fullRateScaled: fullRateBasisPoints * scale / 100,
Expand All @@ -118,7 +119,7 @@ contract DeployLiquidMultiTokenVault is TomlConfig {
}),
frequency: 360,
tenor: 30,
decimals: asset.decimals()
decimals: decimals
});

LiquidContinuousMultiTokenVault.VaultParams memory vaultParams = LiquidContinuousMultiTokenVault.VaultParams({
Expand Down
2 changes: 1 addition & 1 deletion packages/contracts/script/utils/generateTsAbis.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ function getInheritedFunctions(mainArtifact) {
}

function main() {
const current_path_to_broadcast = path.join(__dirname, '../..', 'broadcast/DeployAndLoadLiquidMultiTokenVault.s.sol'); // data loading variant
const current_path_to_broadcast = path.join(__dirname, '../..', 'broadcast/DeployLiquidMultiTokenVault.s.sol'); // data loading variant
const current_path_to_deployments = path.join(__dirname, '../..', 'broadcast');

const chains = getDirectories(current_path_to_broadcast);
Expand Down
118 changes: 118 additions & 0 deletions packages/contracts/test/src/LiquidStoneNinetyDayTest.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import { LiquidContinuousMultiTokenVault } from "@credbull/yield/LiquidContinuousMultiTokenVault.sol";
import { LiquidContinuousMultiTokenVaultTestBase } from "@test/test/yield/LiquidContinuousMultiTokenVaultTestBase.t.sol";

import { CalcSimpleInterest } from "@credbull/yield/CalcSimpleInterest.sol";

import { TestParamSet } from "@test/test/token/ERC1155/TestParamSet.t.sol";

import { IYieldStrategy } from "@credbull/yield/strategy/IYieldStrategy.sol";
import { IRedeemOptimizer } from "@credbull/token/ERC1155/IRedeemOptimizer.sol";
import { DeployLiquidMultiTokenVault } from "@script/DeployLiquidMultiTokenVault.s.sol";

import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

contract DeployLiquidStoneNinetyDay is DeployLiquidMultiTokenVault {
function _createVaultParams(
LiquidContinuousMultiTokenVault.VaultAuth memory vaultAuth,
IERC20Metadata asset,
IYieldStrategy yieldStrategy,
IRedeemOptimizer redeemOptimizer
) public view override returns (LiquidContinuousMultiTokenVault.VaultParams memory vaultParams_) {
LiquidContinuousMultiTokenVault.VaultParams memory vaultParams =
super._createVaultParams(vaultAuth, asset, yieldStrategy, redeemOptimizer);

uint256 scale = 10 ** asset.decimals();
vaultParams.contextParams.tenor = 90;
vaultParams.redeemNoticePeriod = 0;
vaultParams.contextParams.fullRateScaled = 10 * scale;
vaultParams.contextParams.initialReducedRate.interestRate = 0; // zero for less than tenor

return vaultParams;
}
}

contract LiquidStoneNinetyDayTest is LiquidContinuousMultiTokenVaultTestBase {
using TestParamSet for TestParamSet.TestParam[];

function setUp() public virtual override {
DeployLiquidMultiTokenVault _deployVault = new DeployLiquidStoneNinetyDay();
_liquidVault = _deployVault.run(_vaultAuth);

// warp to a "real time" time rather than block.timestamp=1
vm.warp(_liquidVault._vaultStartTimestamp() + 1);

_asset = IERC20Metadata(_liquidVault.asset());
_scale = 10 ** _asset.decimals();

_transferAndAssert(_asset, _vaultAuth.owner, alice, 100_000 * _scale);
_transferAndAssert(_asset, _vaultAuth.owner, bob, 100_000 * _scale);
}

function test__LiquidStoneNinetyDay__VerifyDeployTenor() public view {
assertEq(90, _liquidVault.TENOR(), "tenor incorrect");
}

// TODO - need to implement and test for RetainedAssetsReceive1APY

function test__LiquidStoneNinetyDay__RedeemFullTenor() public {
uint256 depositPeriod = 5;
uint256 redeemPeriod = depositPeriod + _liquidVault.TENOR();
uint256 principal = 105 * _scale;

TestParamSet.TestUsers memory aliceTestUsers = TestParamSet.toSingletonUsers(alice);

TestParamSet.TestParam[] memory testParams = new TestParamSet.TestParam[](1);
testParams[0] =
TestParamSet.TestParam({ principal: principal, depositPeriod: depositPeriod, redeemPeriod: redeemPeriod });

uint256[] memory sharesAtPeriods = _testDepositOnly(aliceTestUsers, _liquidVault, testParams);

uint256 expectedReturns = CalcSimpleInterest.calcInterest(
principal, _liquidVault.rateScaled(), redeemPeriod - depositPeriod, _liquidVault.frequency(), _scale
);

_transferFromTokenOwner(_asset, address(_liquidVault), expectedReturns); // give the vault enough to cover returns

// warp to the redeem period
_warpToPeriod(_liquidVault, redeemPeriod);

vm.prank(alice);
_liquidVault.requestRedeem(sharesAtPeriods[0], alice, alice);

vm.prank(alice);
uint256 assets = _liquidVault.redeem(sharesAtPeriods[0], alice, alice);

assertEq(principal + expectedReturns, assets, "wrong assets returned");
}

function test__LiquidStoneNinetyDay__EarlyRedemptionGivesZeroYield() public {
uint256 depositPeriod = 25;
uint256 earlyRedeemPeriod = depositPeriod + _liquidVault.TENOR() - 1; // less than full tenor period
uint256 principal = 125 * _scale;

TestParamSet.TestUsers memory aliceTestUsers = TestParamSet.toSingletonUsers(alice);

TestParamSet.TestParam[] memory testParams = new TestParamSet.TestParam[](1);
testParams[0] = TestParamSet.TestParam({
principal: principal,
depositPeriod: depositPeriod,
redeemPeriod: earlyRedeemPeriod
});

uint256[] memory sharesAtPeriods = _testDepositOnly(aliceTestUsers, _liquidVault, testParams);

// warp to the redeem period
_warpToPeriod(_liquidVault, earlyRedeemPeriod);

vm.prank(alice);
_liquidVault.requestRedeem(sharesAtPeriods[0], alice, alice);

vm.prank(alice);
uint256 assets = _liquidVault.redeem(sharesAtPeriods[0], alice, alice);

assertEq(principal, assets, "early redemption should give back principal and zero returns");
}
}
2 changes: 2 additions & 0 deletions spikes/spike-liquid-stone/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"verify": "yarn workspace @se-2/foundry verify"
},
"devDependencies": {
"@types/dotenv": "^8.2.3",
"husky": "^9.1.4",
"lint-staged": "^15.2.9"
},
Expand All @@ -44,6 +45,7 @@
"dependencies": {
"@openzeppelin/contracts-upgradeable": "^5.0.2",
"alchemy-sdk": "^3.4.2",
"dotenv": "^16.4.5",
"ethers": "^6.13.3",
"react-tooltip": "^5.28.0"
},
Expand Down
6 changes: 6 additions & 0 deletions spikes/spike-liquid-stone/packages/nextjs/.env.sample
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
NEXT_PUBLIC_NETWORK=localhost

NEXT_PUBLIC_CUSTODIAN=0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC

NEXT_PUBLIC_ALCHEMY_API_KEY=

# Required for Plume Mainnet only
# TODO - do not check this in - API key!
PLUME_API_KEY=
Loading

0 comments on commit 7420746

Please sign in to comment.