diff --git a/contracts/erc4626/Vault.sol b/contracts/erc4626/Vault.sol index b0cb9d8..f319826 100644 --- a/contracts/erc4626/Vault.sol +++ b/contracts/erc4626/Vault.sol @@ -16,9 +16,9 @@ contract HederaVault is IERC4626 { using Bits for uint256; ERC20 public immutable asset; - address newTokenAddress; + address public newTokenAddress; uint public totalTokens; - address[] tokenAddress; + address[] public tokenAddress; address public owner; event createdToken(address indexed createdTokenAddress); @@ -26,8 +26,11 @@ contract HederaVault is IERC4626 { constructor( ERC20 _underlying, string memory _name, - string memory _symbol + string memory _symbol, + address[] memory rewardTokens ) payable ERC20(_name, _symbol, _underlying.decimals()) { + owner = msg.sender; + SafeHTS.safeAssociateToken(address(_underlying), address(this)); uint256 supplyKeyType; uint256 adminKeyType; @@ -58,6 +61,8 @@ contract HederaVault is IERC4626 { newTokenAddress = SafeHTS.safeCreateFungibleToken(newToken, 0, _underlying.decimals()); emit createdToken(newTokenAddress); asset = _underlying; + + tokenAddress = rewardTokens; } struct UserInfo { @@ -81,8 +86,6 @@ contract HederaVault is IERC4626 { function deposit(uint256 amount, address to) public override returns (uint256 shares) { require((shares = previewDeposit(amount)) != 0, "ZERO_SHARES"); - asset.approve(address(this), amount); - asset.safeTransferFrom(msg.sender, address(this), amount); totalTokens += amount; @@ -223,9 +226,11 @@ contract HederaVault is IERC4626 { REWARDS LOGIC //////////////////////////////////////////////////////////////*/ - function addReward(address _token, uint _amount) internal { + function addReward(address _token, uint _amount) public { require(_amount != 0, "please provide amount"); require(totalTokens != 0, "no token staked yet"); + require(msg.sender == owner, "Only owner"); + uint perShareRewards; perShareRewards = _amount.mulDivDown(1, totalTokens); if (!rewardsAddress[_token].exist) { diff --git a/scripts/deployVault.ts b/scripts/deployVault.ts index eb4e909..c817d91 100644 --- a/scripts/deployVault.ts +++ b/scripts/deployVault.ts @@ -14,35 +14,46 @@ async function main() { let client = Client.forTestnet(); const operatorPrKey = PrivateKey.fromStringECDSA(process.env.PRIVATE_KEY || ''); - const operatorAccountId = AccountId.fromString(process.env.OPERATOR_ID || ''); + const operatorAccountId = AccountId.fromString(process.env.ACCOUNT_ID || ''); client.setOperator( operatorAccountId, operatorPrKey ); - const createERC4626 = await createFungibleToken( + const stakingToken = await createFungibleToken( "ERC4626 on Hedera", "HERC4626", - process.env.OPERATOR_ID, + process.env.ACCOUNT_ID, operatorPrKey.publicKey, client, operatorPrKey ); - const stakingTokenAddress = "0x" + createERC4626!.toSolidityAddress(); + const rewardToken = await createFungibleToken( + "Reward Token 1", + "RT1", + process.env.ACCOUNT_ID, + operatorPrKey.publicKey, + client, + operatorPrKey + ); + + const stakingTokenAddress = "0x" + stakingToken!.toSolidityAddress(); + const rewardTokenAddress = "0x" + rewardToken!.toSolidityAddress(); const HederaVault = await ethers.getContractFactory("HederaVault"); const hederaVault = await HederaVault.deploy( stakingTokenAddress, "TST", "TST", - { from: deployer.address, value: ethers.parseUnits("10", 18) } + [rewardTokenAddress], + { from: deployer.address, gasLimit: 3000000, value: ethers.parseUnits("10", 18) } ); console.log("Hash ", hederaVault.deploymentTransaction()?.hash); await hederaVault.waitForDeployment(); - console.log(await hederaVault.getAddress()); + console.log("Vault deployed with address: ", await hederaVault.getAddress()); } main().catch((error) => { diff --git a/scripts/utils.ts b/scripts/utils.ts index 2c5fcd4..c3d8116 100644 --- a/scripts/utils.ts +++ b/scripts/utils.ts @@ -85,7 +85,7 @@ export async function addToken( client: Client ) { let contractFunctionParameters = new ContractFunctionParameters() - .addAddress(tokenId.toSolidityAddress()) + .addAddress(tokenId) .addUint256(amount * 1e8); const notifyRewardTx = await new ContractExecuteTransaction() @@ -153,5 +153,7 @@ module.exports = { getClient, createAccount, mintToken, - TokenTransfer + TokenTransfer, + TokenBalance, + addToken, } diff --git a/test/erc4626/vault.test.ts b/test/erc4626/vault.test.ts index a89631c..f2c2775 100644 --- a/test/erc4626/vault.test.ts +++ b/test/erc4626/vault.test.ts @@ -1,19 +1,25 @@ import { anyValue, ethers, expect } from "../setup"; import { TokenTransfer, createFungibleToken, TokenBalance, createAccount, addToken, mintToken } from "../../scripts/utils"; import { PrivateKey, Client, AccountId, TokenAssociateTransaction, AccountBalanceQuery } from "@hashgraph/sdk"; +import hre from "hardhat"; // constants -let client; -let aliceKey; -let aliceAccountId; +const stakingTokenId = "0.0.3757626"; +const sharesTokenAddress = "0x0000000000000000000000000000000000395640"; +const vaultAddress = "0xb3C24B140BA2a69099276e55dE1885e93517C6C6"; +const vaultId = "0.0.3757631"; + +const newStakingTokenId = "0.0.4178090"; +const newVaultAddress = "0x26767C096B669b0A5Df59efeF0d6CbA3840E47F6" +const newRewardTokenId = "0.0.4178093"; +const rewardTokenAddress = ""; +const newSharesTokenAddress = "0x00000000000000000000000000000000003fc0b0"; +const newVaultId = "0.0.4178095"; // Tests describe("Vault", function () { async function deployFixture() { const [ owner, - to, - admin, - ...otherAccounts ] = await ethers.getSigners(); let client = Client.forTestnet(); @@ -26,113 +32,176 @@ describe("Vault", function () { operatorPrKey ); - // aliceKey = PrivateKey.generateED25519(); - // aliceAccountId = await createAccount(client, aliceKey, 20); - // const alice = createAccount(client, aliceKey, aliceAccountId); + const erc20 = await hre.artifacts.readArtifact("contracts/erc4626/ERC20.sol:ERC20"); - // console.log("account creation success"); - - const stakingToken = await createFungibleToken( - "ERC4626 on Hedera", - "HERC4626", - process.env.ACCOUNT_ID, - operatorPrKey.publicKey, - client, - operatorPrKey - ); - - const stakingTokenAddress = "0x" + stakingToken!.toSolidityAddress(); - - const HederaVault = await ethers.getContractFactory("HederaVault"); - const hederaVault = await HederaVault.deploy( - stakingTokenAddress, - "TST", - "TST", - { from: owner.address, gasLimit: 3000000, value: ethers.parseUnits("10", 18) } - ); - await hederaVault.waitForDeployment(); + // const rewardToken = await createFungibleToken( + // "Reward Token 1", + // "RT1", + // process.env.ACCOUNT_ID, + // operatorPrKey.publicKey, + // client, + // operatorPrKey + // ); - // client.setOperator(aliceAccountId!, aliceKey); // const tokenAssociate = await new TokenAssociateTransaction() - // .setAccountId(aliceAccountId!) - // .setTokenIds([stakingToken!]) - // .execute(client); - - // console.log("association success"); - - // await mintToken(stakingToken, client, 100, operatorPrKey); - - // console.log("token mint success"); - - // let balanceCheckTreasury = await new AccountBalanceQuery() - // .setAccountId(operatorAccountId) + // .setAccountId(newVaultId) + // .setTokenIds([newRewardTokenId]) // .execute(client); - // console.log( - // " Treasury balance: " + balanceCheckTreasury.tokens - // ); - - // const stToken = ethers.getContractAt("HederaVault", stakingTokenAddress); + const hederaVaultRevertCases = await ethers.getContractAt( + "HederaVault", + vaultAddress + ); + const hederaVault = await ethers.getContractAt( + "HederaVault", + newVaultAddress + ); - // console.log("balance check", await stToken.(owner.address)); + const stakingToken = await ethers.getContractAt( + erc20.abi, + await hederaVault.asset() + ); - // client.setOperator( - // operatorAccountId, - // operatorPrKey - // ); - // await TokenTransfer(stakingToken, operatorAccountId, aliceAccountId, 50, client); + const sharesToken = await ethers.getContractAt( + erc20.abi, + sharesTokenAddress + ); - // console.log("token transfer success"); + // await TokenTransfer(stakingTokenId, operatorAccountId, vaultId, 10, client); - // console.log( - // await TokenBalance(receiver.address, client) - // ); + const stakingTokenOperatorBalance = await ( + await TokenBalance(operatorAccountId, client) + ).tokens!.get(stakingTokenId); + console.log("Staking token balance: ", stakingTokenOperatorBalance.toString()); return { hederaVault, + hederaVaultRevertCases, stakingToken, - // stToken, - // alice, - to, + sharesToken, client, owner, - admin, - otherAccounts, }; } describe("deposit", function () { - // it("Should deposit tokens and return shares", async function () { - // const { hederaVault, to, owner, client, stakingToken } = await deployFixture(); - // // const amountToDeposit = 1; - // // const amountToWithdraw = 1 * 1e8; + it("Should deposit tokens and return shares", async function () { + const { hederaVault, owner, stakingToken } = await deployFixture(); + const amountToDeposit = 1; - // // console.log("work1"); - // // await addToken(hederaVault, stakingToken, 10, client); + console.log(await hederaVault.previewDeposit(amountToDeposit)); - // // console.log("work"); + await stakingToken.approve(hederaVault.target, amountToDeposit); - // // const tx = await hederaVault.connect(owner).withdraw( - // // amountToWithdraw, - // // owner.address, - // // owner.address - // // ); + const tx = await hederaVault.connect(owner).deposit( + amountToDeposit, + owner.address, + { gasLimit: 3000000 } + ); - // // console.log("with"); + await expect( + tx + ).to.emit(hederaVault, "Deposit") + .withArgs(owner.address, owner.address, amountToDeposit, anyValue); + }); - // // // const tx = await hederaVault.connect(owner).deposit(amountToDeposit, to.address); + it("Should revert if zero shares", async function () { + const { hederaVaultRevertCases, owner } = await deployFixture(); + const amountToDeposit = 0; - // // await expect( - // // tx - // // ).to.emit(hederaVault, "Withdraw") - // // .withArgs(owner.address, owner.address, amountToWithdraw, anyValue); - // }); + console.log(await hederaVaultRevertCases.previewDeposit(amountToDeposit)); + + await expect( + hederaVaultRevertCases.connect(owner).deposit( + amountToDeposit, + owner.address, + { gasLimit: 3000000 } + ) + ).to.be.revertedWith("ZERO_SHARES"); + }); + }); + + describe("withdraw", function () { + it("Should withdraw tokens", async function () { + const { hederaVault, owner } = await deployFixture(); + const amountToWithdraw = 1; + + const tx = await hederaVault.connect(owner).withdraw( + amountToWithdraw, + owner.address, + owner.address, + { gasLimit: 3000000 } + ); + + await expect( + tx + ).to.emit(hederaVault, "Withdraw") + .withArgs(owner.address, owner.address, amountToWithdraw, anyValue); + }); + }); + + describe("mint", function () { + it("Should mint tokens", async function () { + const { hederaVault, owner, stakingToken } = await deployFixture(); + const amountOfShares = 1; + + const amount = await hederaVault.previewMint(amountOfShares); - it("preview", async function () { - const { hederaVault, to, owner } = await deployFixture(); - // const amountToDeposit = 1; + await stakingToken.approve(hederaVault.target, amount); - // console.log(await hederaVault.connect(owner).previewMint(amountToDeposit)); + const tx = await hederaVault.connect(owner).mint( + amountOfShares, + owner.address, + { gasLimit: 3000000 } + ); + + await expect( + tx + ).to.emit(hederaVault, "Deposit") + .withArgs(owner.address, owner.address, anyValue, amountOfShares); }); }); + + describe("redeem", function () { + it("Should redeem tokens", async function () { + const { hederaVault, owner, stakingToken, sharesToken, client } = await deployFixture(); + const amountOfShares = 1; + + const tokensAmount = await hederaVault.previewRedeem(amountOfShares); + console.log("Preview redeem ", tokensAmount); + + console.log(await sharesToken.balanceOf(owner.address)); + console.log("TOTAL SUPPLY", await hederaVault.totalSupply()); + console.log("TOTAL ASSETS", await hederaVault.totalAssets()); + console.log("TOTAL TOKENS", await hederaVault.totalTokens()); + + await stakingToken.approve(hederaVault.target, amountOfShares); + + const tx = await hederaVault.connect(owner).redeem( + amountOfShares, + owner.address, + owner.address, + { gasLimit: 3000000 } + ); + + await expect( + tx + ).to.emit(hederaVault, "Withdraw") + .withArgs(owner.address, owner.address, tokensAmount, amountOfShares); + }); + + // it("Should revert if zero assets", async function () { + // const { hederaVault, owner } = await deployFixture(); + // const amountToReedem = 0; + + // await expect( + // hederaVault.connect(owner).redeem( + // amountToReedem, + // owner.address, + // owner.address, + // { gasLimit: 3000000 } + // ) + // ).to.be.revertedWith("ZERO_ASSETS"); + // }); + }); });