From c593586445c7c0c4f379974b1abf3857f4e7dce6 Mon Sep 17 00:00:00 2001 From: PacificYield <173040337+PacificYield@users.noreply.github.com> Date: Wed, 4 Dec 2024 14:23:58 +0100 Subject: [PATCH] test: draft --- .../ConfidentialVestingWalletCliff.sol | 2 +- .../finance/TestConfidentialVestingWallet.sol | 16 +++ .../TestConfidentialVestingWalletCliff.sol | 17 +++ .../ConfidentialVestingWallet.fixture.ts | 30 +++++ .../finance/ConfidentialVestingWallet.test.ts | 114 ++++++++++++++++++ .../ConfidentialVestingWalletCliff.fixture.ts | 20 +++ 6 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 contracts/test/finance/TestConfidentialVestingWallet.sol create mode 100644 contracts/test/finance/TestConfidentialVestingWalletCliff.sol create mode 100644 test/finance/ConfidentialVestingWallet.fixture.ts create mode 100644 test/finance/ConfidentialVestingWallet.test.ts create mode 100644 test/finance/ConfidentialVestingWalletCliff.fixture.ts diff --git a/contracts/finance/ConfidentialVestingWalletCliff.sol b/contracts/finance/ConfidentialVestingWalletCliff.sol index fc34fab..3d8bb34 100644 --- a/contracts/finance/ConfidentialVestingWalletCliff.sol +++ b/contracts/finance/ConfidentialVestingWalletCliff.sol @@ -14,7 +14,7 @@ import { ConfidentialVestingWallet } from "./ConfidentialVestingWallet.sol"; * To use with the native asset, it is necessary to wrap the native asset to a ConfidentialERC20-like token. */ -abstract contract VestingWalletCliff is ConfidentialVestingWallet { +abstract contract ConfidentialVestingWalletCliff is ConfidentialVestingWallet { /// @notice Returned if the cliff duration is greater than the vesting duration. error InvalidCliffDuration(uint64 cliffSeconds, uint64 durationSeconds); diff --git a/contracts/test/finance/TestConfidentialVestingWallet.sol b/contracts/test/finance/TestConfidentialVestingWallet.sol new file mode 100644 index 0000000..8b5cc19 --- /dev/null +++ b/contracts/test/finance/TestConfidentialVestingWallet.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +pragma solidity ^0.8.24; + +import { ConfidentialVestingWallet } from "../../finance/ConfidentialVestingWallet.sol"; +import { SepoliaZamaFHEVMConfig } from "fhevm/config/ZamaFHEVMConfig.sol"; + +contract TestConfidentialVestingWallet is SepoliaZamaFHEVMConfig, ConfidentialVestingWallet { + constructor( + address beneficiary_, + address token_, + uint64 startTimestamp_, + uint64 duration_ + ) ConfidentialVestingWallet(beneficiary_, token_, startTimestamp_, duration_) { + // + } +} diff --git a/contracts/test/finance/TestConfidentialVestingWalletCliff.sol b/contracts/test/finance/TestConfidentialVestingWalletCliff.sol new file mode 100644 index 0000000..e4f5091 --- /dev/null +++ b/contracts/test/finance/TestConfidentialVestingWalletCliff.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +pragma solidity ^0.8.24; + +import { ConfidentialVestingWalletCliff } from "../../finance/ConfidentialVestingWalletCliff.sol"; +import { SepoliaZamaFHEVMConfig } from "fhevm/config/ZamaFHEVMConfig.sol"; + +contract TestConfidentialVestingWalletCliff is SepoliaZamaFHEVMConfig, ConfidentialVestingWalletCliff { + constructor( + address beneficiary_, + address token_, + uint64 startTimestamp_, + uint64 duration_, + uint64 cliff_ + ) ConfidentialVestingWalletCliff(beneficiary_, token_, startTimestamp_, duration_, cliff_) { + // + } +} diff --git a/test/finance/ConfidentialVestingWallet.fixture.ts b/test/finance/ConfidentialVestingWallet.fixture.ts new file mode 100644 index 0000000..b1e90d5 --- /dev/null +++ b/test/finance/ConfidentialVestingWallet.fixture.ts @@ -0,0 +1,30 @@ +import { Signer } from "ethers"; +import { FhevmInstance } from "fhevmjs/node"; +import { ethers } from "hardhat"; + +import type { ConfidentialVestingWallet, TestConfidentialVestingWallet } from "../../types"; +import { reencryptEuint64 } from "../reencrypt"; + +export async function deployConfidentialVestingWalletFixture( + account: Signer, + beneficiaryAddress: string, + token: string, + startTimestamp: bigint, + duration: bigint, +): Promise { + const contractFactory = await ethers.getContractFactory("TestConfidentialVestingWallet"); + const contract = await contractFactory.connect(account).deploy(beneficiaryAddress, token, startTimestamp, duration); + await contract.waitForDeployment(); + return contract; +} + +export async function reencryptReleased( + account: Signer, + instance: FhevmInstance, + vestingWallet: ConfidentialVestingWallet, + vestingWalletAddress: string, +): Promise { + const releasedHandled = await vestingWallet.released(); + const releasedAmount = await reencryptEuint64(account, instance, releasedHandled, vestingWalletAddress); + return releasedAmount; +} diff --git a/test/finance/ConfidentialVestingWallet.test.ts b/test/finance/ConfidentialVestingWallet.test.ts new file mode 100644 index 0000000..3800651 --- /dev/null +++ b/test/finance/ConfidentialVestingWallet.test.ts @@ -0,0 +1,114 @@ +import { expect } from "chai"; +import { parseUnits } from "ethers"; +import { ethers } from "hardhat"; + +import { deployConfidentialERC20Fixture } from "../confidentialERC20/ConfidentialERC20.fixture"; +import { createInstance } from "../instance"; +import { getSigners, initSigners } from "../signers"; +import { deployConfidentialVestingWalletFixture, reencryptReleased } from "./ConfidentialVestingWallet.fixture"; + +describe("ConfidentialVestingWallet", function () { + before(async function () { + await initSigners(); + this.signers = await getSigners(); + this.instance = await createInstance(); + }); + + beforeEach(async function () { + const latestBlockNumber = await ethers.provider.getBlockNumber(); + const block = await ethers.provider.getBlock(latestBlockNumber); + + this.beneficiary = this.signers.bob; + this.beneficiaryAddress = this.signers.bob.address; + + const contractConfidentialERC20 = await deployConfidentialERC20Fixture( + this.signers.alice, + "Naraggara", + "NARA", + this.signers.alice.address, + ); + this.confidentialERC20Address = await contractConfidentialERC20.getAddress(); + this.confidentialERC20 = contractConfidentialERC20; + this.startTimestamp = BigInt(block!.timestamp + 3600); + this.duration = BigInt(36_000); // 36,000 seconds + + const contractConfidentialVestingWallet = await deployConfidentialVestingWalletFixture( + this.signers.alice, + this.beneficiaryAddress, + this.confidentialERC20Address, + this.startTimestamp, + this.duration, + ); + + this.confidentialVestingWallet = contractConfidentialVestingWallet; + this.confidentialVestingWalletAddress = await contractConfidentialVestingWallet.getAddress(); + }); + + it.only("post-deployment state", async function () { + expect(await this.confidentialVestingWallet.BENEFICIARY()).to.equal(this.beneficiaryAddress); + expect(await this.confidentialVestingWallet.CONFIDENTIAL_ERC20()).to.equal(this.confidentialERC20); + expect(await this.confidentialVestingWallet.DURATION()).to.equal(this.duration); + expect(await this.confidentialVestingWallet.END_TIMESTAMP()).to.be.eq(this.startTimestamp + this.duration); + expect(await this.confidentialVestingWallet.START_TIMESTAMP()).to.be.eq(this.startTimestamp); + expect( + await reencryptReleased( + this.beneficiary, + this.instance, + this.confidentialVestingWallet, + this.confidentialVestingWalletAddress, + ), + ).to.be.eq(0n); + }); + + it.only("can release", async function () { + // 10M + const amount = parseUnits("10000000", 6); + + let tx = await this.confidentialERC20.connect(this.signers.alice).mint(amount); + await tx.wait(); + + const input = this.instance.createEncryptedInput(this.confidentialERC20Address, this.signers.alice.address); + input.add64(amount); + const encryptedTransferAmount = await input.encrypt(); + + tx = await this.confidentialERC20 + .connect(this.signers.alice) + [ + "transfer(address,bytes32,bytes)" + ](this.confidentialVestingWalletAddress, encryptedTransferAmount.handles[0], encryptedTransferAmount.inputProof); + + await tx.wait(); + + let nextTimestamp = this.startTimestamp; + await ethers.provider.send("evm_setNextBlockTimestamp", [nextTimestamp.toString()]); + + tx = await this.confidentialVestingWallet.connect(this.beneficiary).release(); + await expect(tx).to.emit(this.confidentialVestingWallet, "ConfidentialERC20Released"); + + // It should be equal to 0 because the vesting has not started. + expect( + await reencryptReleased( + this.beneficiary, + this.instance, + this.confidentialVestingWallet, + this.confidentialVestingWalletAddress, + ), + ).to.be.eq(0n); + + nextTimestamp = this.startTimestamp + this.duration / BigInt(4); + await ethers.provider.send("evm_setNextBlockTimestamp", [nextTimestamp.toString()]); + + tx = await this.confidentialVestingWallet.connect(this.beneficiary).release(); + await tx.wait(); + + // It should be equal to 1/4 of the amount vested. + expect( + await reencryptReleased( + this.beneficiary, + this.instance, + this.confidentialVestingWallet, + this.confidentialVestingWalletAddress, + ), + ).to.be.eq(BigInt(amount) / BigInt(4)); + }); +}); diff --git a/test/finance/ConfidentialVestingWalletCliff.fixture.ts b/test/finance/ConfidentialVestingWalletCliff.fixture.ts new file mode 100644 index 0000000..a65a4ce --- /dev/null +++ b/test/finance/ConfidentialVestingWalletCliff.fixture.ts @@ -0,0 +1,20 @@ +import { Signer } from "ethers"; +import { ethers } from "hardhat"; + +import type { TestConfidentialVestingWalletCliff } from "../../types"; + +export async function deployConfidentialVestingWalletFixture( + account: Signer, + beneficiaryAddress: string, + token: string, + startTimestamp: bigint, + duration: bigint, + cliff: bigint, +): Promise { + const contractFactory = await ethers.getContractFactory("TestConfidentialVestingWalletCliff"); + const contract = await contractFactory + .connect(account) + .deploy(beneficiaryAddress, token, startTimestamp, duration, cliff); + await contract.waitForDeployment(); + return contract; +}