From 7272eab598cfdfd2a501d2cfd01e8d365df1b2ef Mon Sep 17 00:00:00 2001 From: yu23ki14 Date: Sat, 12 Jul 2025 13:16:40 +0900 Subject: [PATCH 1/2] remove ownable from fraction token --- pkgs/contract/contracts/bigbang/BigBang.sol | 4 +- .../fractiontoken/HatsFractionTokenModule.sol | 22 ++---- pkgs/contract/helpers/deploy/FractionToken.ts | 57 --------------- pkgs/contract/helpers/deploy/Hats.ts | 6 +- pkgs/contract/test/BigBang.ts | 6 +- pkgs/contract/test/HatsFractionTokenModule.ts | 15 +--- pkgs/contract/test/IntegrationTest.ts | 6 +- pkgs/contract/test/SplitsCreator.ts | 32 +++------ pkgs/contract/test/ThanksToken.ts | 69 +++++++++++++++---- 9 files changed, 79 insertions(+), 138 deletions(-) delete mode 100644 pkgs/contract/helpers/deploy/FractionToken.ts diff --git a/pkgs/contract/contracts/bigbang/BigBang.sol b/pkgs/contract/contracts/bigbang/BigBang.sol index 825f2307..c54168c5 100644 --- a/pkgs/contract/contracts/bigbang/BigBang.sol +++ b/pkgs/contract/contracts/bigbang/BigBang.sol @@ -94,7 +94,6 @@ contract BigBang is OwnableUpgradeable, UUPSUpgradeable { string calldata _hatterHatImageURI ) external returns (uint256) { // 1. TopHatのMint - uint256 topHatId = Hats.mintTopHat( address(this), // target: Tophat's wearer address. topHatのみがHatterHatを作成できるためTophatを指定する _topHatDetails, @@ -102,7 +101,6 @@ contract BigBang is OwnableUpgradeable, UUPSUpgradeable { ); // 2. HatterHatの作成 - uint256 hatterHatId = Hats.createHat( topHatId, // _admin: The id of the Hat that will control who wears the newly created hat _hatterHatDetails, @@ -165,7 +163,7 @@ contract BigBang is OwnableUpgradeable, UUPSUpgradeable { HatsFractionTokenModule_IMPL, topHatId, "", - abi.encode(_owner, "", 10_000), + abi.encode("", 10_000), 0 ); diff --git a/pkgs/contract/contracts/hatsmodules/fractiontoken/HatsFractionTokenModule.sol b/pkgs/contract/contracts/hatsmodules/fractiontoken/HatsFractionTokenModule.sol index 46556a3c..f5d9815a 100644 --- a/pkgs/contract/contracts/hatsmodules/fractiontoken/HatsFractionTokenModule.sol +++ b/pkgs/contract/contracts/hatsmodules/fractiontoken/HatsFractionTokenModule.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.24; import {HatsModule} from "../../hats/module/HatsModule.sol"; -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; import {ERC1155Supply} from "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol"; import {IHatsFractionTokenModule} from "./IHatsFractionTokenModule.sol"; @@ -22,7 +21,6 @@ import {IHatsFractionTokenModule} from "./IHatsFractionTokenModule.sol"; */ contract HatsFractionTokenModule is HatsModule, - Ownable, ERC1155, ERC1155Supply, IHatsFractionTokenModule @@ -45,19 +43,15 @@ contract HatsFractionTokenModule is /** * @notice Initialize the contract with required parameters * @param _version The version of the contract for upgrade compatibility - * @param _tmpOwner The temporary owner of the contract (will be transferred during setup) */ - constructor( - string memory _version, - address _tmpOwner - ) HatsModule(_version) Ownable(_tmpOwner) ERC1155("") {} + constructor(string memory _version) HatsModule(_version) ERC1155("") {} // ============ Initialization ============ /** - * @notice Initializes the module with owner, URI, and token supply configuration + * @notice Initializes the module with URI, and token supply configuration * @dev This function is called once during module deployment via the factory - * @param _initData ABI-encoded data containing (address _owner, string _uri, uint256 _defaultTokenSupply) + * @param _initData ABI-encoded data containing (string _uri, uint256 _defaultTokenSupply) * * Requirements: * - The hatId must be a top hat (ensures domain isolation) @@ -67,11 +61,12 @@ contract HatsFractionTokenModule is * - Sets the base URI for token metadata * - Sets the token supply for initial minting * - Extracts and stores the domain from the top hat - * - Transfers ownership to the specified address */ function _setUp(bytes calldata _initData) internal override { - (address _owner, string memory _uri, uint256 _defaultTokenSupply) = abi - .decode(_initData, (address, string, uint256)); + (string memory _uri, uint256 _defaultTokenSupply) = abi.decode( + _initData, + (string, uint256) + ); _setURI(_uri); @@ -88,9 +83,6 @@ contract HatsFractionTokenModule is // Extract domain from the top hat for validation in future operations DOMAIN = HATS().getTopHatDomain(hatId()); - - // Transfer ownership to the specified address - _transferOwnership(_owner); } // ============ Minting Functions ============ diff --git a/pkgs/contract/helpers/deploy/FractionToken.ts b/pkgs/contract/helpers/deploy/FractionToken.ts deleted file mode 100644 index 277769c5..00000000 --- a/pkgs/contract/helpers/deploy/FractionToken.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { ethers, upgrades, viem } from "hardhat"; -import type { Address } from "viem"; -import { baseSalt, deployContract_Create2 } from "./Create2Factory"; -import { ProxyFactory } from "./Upgradeable"; - -export type FractionToken = Awaited< - ReturnType ->["FractionToken"]; - -export const deployFractionToken = async ( - uri: string, - tokenSupply: bigint, - hatsContractAddress: Address, - create2DeployerAddress?: string, -) => { - const [deployer] = await ethers.getSigners(); - const _tokenSupply = !tokenSupply ? 10000n : tokenSupply; - - const FractionTokenFactory = await ethers.getContractFactory("FractionToken"); - const FractionTokenImplTx = await FractionTokenFactory.getDeployTransaction(); - const FractionTokenImplAddress = await deployContract_Create2( - baseSalt, - FractionTokenImplTx.data || "0x", - ethers.keccak256(FractionTokenImplTx.data), - "FractionToken_Implementation", - create2DeployerAddress, - ); - - const FractionTokenInitData = - FractionTokenFactory.interface.encodeFunctionData("initialize", [ - deployer.address, - _tokenSupply, - hatsContractAddress, - uri, - ]); - - const UpgradeableProxy = await ProxyFactory(); - const FractionTokenProxyTx = await UpgradeableProxy.getDeployTransaction( - FractionTokenImplAddress, - FractionTokenInitData, - ); - const FractionTokenAddress = await deployContract_Create2( - baseSalt, - FractionTokenProxyTx.data || "0x", - ethers.keccak256(FractionTokenProxyTx.data), - "FractionToken", - create2DeployerAddress, - ); - - // create a new instance of the contract - const FractionToken = await viem.getContractAt( - "FractionToken", - FractionTokenAddress as Address, - ); - - return { FractionToken, FractionTokenImplAddress, FractionTokenInitData }; -}; diff --git a/pkgs/contract/helpers/deploy/Hats.ts b/pkgs/contract/helpers/deploy/Hats.ts index cc5592d7..5add9d29 100644 --- a/pkgs/contract/helpers/deploy/Hats.ts +++ b/pkgs/contract/helpers/deploy/Hats.ts @@ -96,7 +96,6 @@ export const deployHatsHatCreatorModule = async ( }; export const deployHatsFractionTokenModule = async ( - tmpOwner: Address, version = "0.0.0", create2DeployerAddress?: string, ) => { @@ -104,10 +103,7 @@ export const deployHatsFractionTokenModule = async ( "HatsFractionTokenModule", ); const HatsFractionTokenModuleTx = - await HatsFractionTokenModuleFactory.getDeployTransaction( - version, - tmpOwner, - ); + await HatsFractionTokenModuleFactory.getDeployTransaction(version); const HatsFractionTokenModuleAddress = await deployContract_Create2( baseSalt, HatsFractionTokenModuleTx.data || "0x", diff --git a/pkgs/contract/test/BigBang.ts b/pkgs/contract/test/BigBang.ts index 9aa1ce86..4ccd9635 100644 --- a/pkgs/contract/test/BigBang.ts +++ b/pkgs/contract/test/BigBang.ts @@ -83,11 +83,7 @@ describe("BigBang", () => { HatsHatCreatorModule_IMPL = _HatsHatCreatorModule; const { HatsFractionTokenModule: _HatsFractionTokenModule_IMPL } = - await deployHatsFractionTokenModule( - await address1.getAddresses().then((addresses) => addresses[0]), - "0.0.0", - Create2Deployer.address, - ); + await deployHatsFractionTokenModule("0.0.0", Create2Deployer.address); HatsFractionTokenModule_IMPL = _HatsFractionTokenModule_IMPL; const { diff --git a/pkgs/contract/test/HatsFractionTokenModule.ts b/pkgs/contract/test/HatsFractionTokenModule.ts index 56df4ade..a082efd8 100644 --- a/pkgs/contract/test/HatsFractionTokenModule.ts +++ b/pkgs/contract/test/HatsFractionTokenModule.ts @@ -76,11 +76,7 @@ describe("HatsFractionTokenModule", () => { ); const { HatsFractionTokenModule: _HatsFractionTokenModule_IMPL } = - await deployHatsFractionTokenModule( - address1Validated, - "0.0.0", - Create2Deployer.address, - ); + await deployHatsFractionTokenModule("0.0.0", Create2Deployer.address); HatsFractionTokenModule_IMPL = _HatsFractionTokenModule_IMPL; publicClient = await viem.getPublicClient(); @@ -90,8 +86,8 @@ describe("HatsFractionTokenModule", () => { it("should deploy fraction token module", async () => { // オーナーアドレス、トークンURI、トークン供給量をエンコード const initData = encodeAbiParameters( - [{ type: "address" }, { type: "string" }, { type: "uint256" }], - [address1Validated, "https://example.com/fraction-token", 10000n], + [{ type: "string" }, { type: "uint256" }], + ["https://example.com/fraction-token", 10000n], ); // アシストクレジットのモジュールをデプロイ @@ -197,11 +193,6 @@ describe("HatsFractionTokenModule", () => { }); it("should have valid owner, token URI, domain, and token supply", async () => { - // オーナーアドレスはエンコードした初期データと一致 - expect( - (await HatsFractionTokenModule.read.owner()).toLowerCase(), - ).to.equal(address1Validated.toLowerCase()); - // トークンURIはエンコードした初期データと一致 expect(await HatsFractionTokenModule.read.uri([0n])).to.equal( "https://example.com/fraction-token", diff --git a/pkgs/contract/test/IntegrationTest.ts b/pkgs/contract/test/IntegrationTest.ts index 3a0e93e8..d52f2bcb 100644 --- a/pkgs/contract/test/IntegrationTest.ts +++ b/pkgs/contract/test/IntegrationTest.ts @@ -103,11 +103,7 @@ describe("IntegrationTest", () => { HatsHatCreatorModule_IMPL = _HatsHatCreatorModule; const { HatsFractionTokenModule: _HatsFractionTokenModule_IMPL } = - await deployHatsFractionTokenModule( - await deployer.getAddresses().then((addresses) => addresses[0]), - "0.0.0", - Create2Deployer.address, - ); + await deployHatsFractionTokenModule("0.0.0", Create2Deployer.address); HatsFractionTokenModule_IMPL = _HatsFractionTokenModule_IMPL; const { diff --git a/pkgs/contract/test/SplitsCreator.ts b/pkgs/contract/test/SplitsCreator.ts index e19b5d83..128f84cb 100644 --- a/pkgs/contract/test/SplitsCreator.ts +++ b/pkgs/contract/test/SplitsCreator.ts @@ -155,16 +155,12 @@ describe("SplitsCreator Factory", () => { ); const { HatsFractionTokenModule: _HatsFractionTokenModule_IMPL } = - await deployHatsFractionTokenModule( - address1.account?.address!, - "0.0.0", - Create2Deployer.address, - ); + await deployHatsFractionTokenModule("0.0.0", Create2Deployer.address); HatsFractionTokenModule_IMPL = _HatsFractionTokenModule_IMPL; const timeFrameInitData = encodeAbiParameters( - [{ type: "address" }], - [address1.account?.address!], + [{ type: "uint256" }], + [timeFrameTobanId], ); await HatsModuleFactory.write.createHatsModule([ @@ -190,12 +186,8 @@ describe("SplitsCreator Factory", () => { // Deploy HatsFractionTokenModule const fractionTokenInitData = encodeAbiParameters( - [{ type: "address" }, { type: "string" }, { type: "uint256" }], - [ - address1.account?.address!, - "https://example.com/fraction-token", - 10000n, - ], + [{ type: "string" }, { type: "uint256" }], + ["https://example.com/fraction-token", 10000n], ); await HatsModuleFactory.write.createHatsModule([ @@ -428,11 +420,7 @@ describe("CreateSplit", () => { ); const { HatsFractionTokenModule: _HatsFractionTokenModule_IMPL } = - await deployHatsFractionTokenModule( - address1.account?.address!, - "0.0.0", - Create2Deployer.address, - ); + await deployHatsFractionTokenModule("0.0.0", Create2Deployer.address); HatsFractionTokenModule_IMPL = _HatsFractionTokenModule_IMPL; const timeFrameInitData = encodeAbiParameters( @@ -463,12 +451,8 @@ describe("CreateSplit", () => { // Deploy HatsFractionTokenModule const fractionTokenInitData = encodeAbiParameters( - [{ type: "address" }, { type: "string" }, { type: "uint256" }], - [ - address1.account?.address!, - "https://example.com/fraction-token", - 10000n, - ], + [{ type: "string" }, { type: "uint256" }], + ["https://example.com/fraction-token", 10000n], ); await HatsModuleFactory.write.createHatsModule([ diff --git a/pkgs/contract/test/ThanksToken.ts b/pkgs/contract/test/ThanksToken.ts index 86549c84..119ef0a2 100644 --- a/pkgs/contract/test/ThanksToken.ts +++ b/pkgs/contract/test/ThanksToken.ts @@ -28,6 +28,43 @@ interface ContractError extends Error { message: string; } +const createHat = async ( + Hats: Hats, + publicClient: PublicClient, + topHatId: bigint, + roleName: string, +): Promise => { + let txHash = await Hats.write.createHat([ + topHatId, + roleName, + 100, + "0x0000000000000000000000000000000000004a75", + "0x0000000000000000000000000000000000004a75", + true, + "", + ]); + let receipt = await publicClient.waitForTransactionReceipt({ + hash: txHash, + }); + + let hatId: bigint | undefined = undefined; + for (const log of receipt.logs) { + const decodedLog = decodeEventLog({ + abi: Hats.abi, + data: log.data, + topics: log.topics, + }); + if (decodedLog.eventName === "HatCreated") { + hatId = decodedLog.args.id; + } + } + + if (!hatId) { + throw new Error("Hatter hat ID not found in transaction logs"); + } + return hatId as bigint; +}; + describe("ThanksToken", () => { let Create2Deployer: Create2Deployer; let Hats: Hats; @@ -82,18 +119,9 @@ describe("ThanksToken", () => { HatsTimeFrameModule_IMPL = _HatsTimeFrameModule; const { HatsFractionTokenModule: _HatsFractionTokenModule_IMPL } = - await deployHatsFractionTokenModule( - validateAddress(deployer), - "0.0.0", - Create2Deployer.address, - ); + await deployHatsFractionTokenModule("0.0.0", Create2Deployer.address); HatsFractionTokenModule_IMPL = _HatsFractionTokenModule_IMPL; - const timeFrameInitData = encodeAbiParameters( - [{ type: "address" }], - [validateAddress(deployer)], - ); - const deployerAddress = validateAddress(deployer); const txHash = await Hats.write.mintTopHat([ deployerAddress, @@ -118,6 +146,23 @@ describe("ThanksToken", () => { } catch (error) {} } + const operatorTobanId = await createHat( + Hats, + publicClient, + topHatId, + "OperatorToban", + ); + const timeFrameHatId = await createHat( + Hats, + publicClient, + operatorTobanId, + "TimeFrameToban", + ); + const timeFrameInitData = encodeAbiParameters( + [{ type: "uint256" }], + [timeFrameHatId], + ); + await HatsModuleFactory.write.createHatsModule([ HatsTimeFrameModule_IMPL.address, topHatId, @@ -141,8 +186,8 @@ describe("ThanksToken", () => { // Deploy HatsFractionTokenModule const fractionTokenInitData = encodeAbiParameters( - [{ type: "address" }, { type: "string" }, { type: "uint256" }], - [validateAddress(deployer), "https://example.com/fraction-token", 10000n], + [{ type: "string" }, { type: "uint256" }], + ["https://example.com/fraction-token", 10000n], ); await HatsModuleFactory.write.createHatsModule([ From 4566d3627e21a9cad939a51f7eba5759d761a089 Mon Sep 17 00:00:00 2001 From: yu23ki14 Date: Sat, 12 Jul 2025 14:15:32 +0900 Subject: [PATCH 2/2] add test --- .../contracts/thankstoken/ThanksToken.sol | 6 +- pkgs/contract/test/ThanksToken.ts | 957 +++++------------- 2 files changed, 241 insertions(+), 722 deletions(-) diff --git a/pkgs/contract/contracts/thankstoken/ThanksToken.sol b/pkgs/contract/contracts/thankstoken/ThanksToken.sol index 49df5592..ce74fd36 100644 --- a/pkgs/contract/contracts/thankstoken/ThanksToken.sol +++ b/pkgs/contract/contracts/thankstoken/ThanksToken.sol @@ -32,7 +32,7 @@ contract ThanksToken is address _hatsAddress, address _fractionTokenAddress, address _hatsTimeFrameModuleAddress, - uint256 defaultCoefficient + uint256 _initialDefaultCoefficient ) public initializer { __ERC20_init(_name, _symbol); __Ownable_init(_initialOwner); @@ -40,8 +40,8 @@ contract ThanksToken is hatsContract = IHats(_hatsAddress); fractionToken = IHatsFractionTokenModule(_fractionTokenAddress); hatsTimeFrameModule = IHatsTimeFrameModule(_hatsTimeFrameModuleAddress); - _defaultCoefficient = defaultCoefficient > 0 - ? defaultCoefficient + _defaultCoefficient = _initialDefaultCoefficient > 0 + ? _initialDefaultCoefficient : 1e18; } diff --git a/pkgs/contract/test/ThanksToken.ts b/pkgs/contract/test/ThanksToken.ts index 119ef0a2..3cd9828d 100644 --- a/pkgs/contract/test/ThanksToken.ts +++ b/pkgs/contract/test/ThanksToken.ts @@ -335,7 +335,7 @@ describe("ThanksToken", () => { account: deployer.account, }) .catch((error) => { - console.log("Error minting FractionToken:", error.message); + console.log("Error minting HatsFractionTokenModule:", error.message); }); }); @@ -399,10 +399,13 @@ describe("ThanksToken", () => { }); it("should approve and use allowance correctly", async () => { - const initialBalance = await DeployedThanksToken.read.balanceOf([ + const address2BalanceBefore = await DeployedThanksToken.read.balanceOf([ address2Validated, ]); - const approveAmount = initialBalance / 2n; + const address3BalanceBefore = await DeployedThanksToken.read.balanceOf([ + address3Validated, + ]); + const approveAmount = address2BalanceBefore; await DeployedThanksToken.write.approve( [address3Validated, approveAmount], @@ -419,7 +422,7 @@ describe("ThanksToken", () => { expect(allowance).to.equal(approveAmount); await DeployedThanksToken.write.transferFrom( - [address2Validated, address3Validated, approveAmount / 2n], + [address2Validated, address3Validated, approveAmount], { account: address3.account }, ); @@ -434,724 +437,240 @@ describe("ThanksToken", () => { address3Validated, ]); - expect(address2BalanceAfter).to.equal( - initialBalance - approveAmount / 2n, + expect(address2BalanceAfter).to.equal(0n); + expect(address3BalanceAfter).to.equal( + approveAmount + address3BalanceBefore, ); - expect(address3BalanceAfter).to.equal(approveAmount / 2n); - expect(allowanceAfter).to.equal(approveAmount - approveAmount / 2n); + expect(allowanceAfter).to.equal(0n); }); }); - // describe("ThanksToken specific functions", () => { - // it("should mint tokens correctly", async () => { - // const isWearingHat = await Hats.read.balanceOf([ - // address1Validated, - // hatId, - // ]); - - // if (isWearingHat === 0n) { - // await HatsTimeFrameModule.write.mintHat([ - // hatId, - // address1Validated, - // BigInt(Math.floor(Date.now() / 1000) - 3600 * 10), // Wearing for 10 hours - // ]); - // } else { - // console.log("Address already wearing hat, skipping time update"); - // } - - // const fractionBalance = await FractionToken.read.balanceOf([ - // address1Validated, - // address1Validated, - // hatId, - // ]); - - // if (fractionBalance === 0n) { - // await FractionToken.write - // .mintInitialSupply([hatId, address1Validated, 0n], { - // account: deployer.account, - // }) - // .catch(() => { - // /* Ignore if already minted */ - // }); - // } - - // const relatedRoles = [ - // { - // hatId, - // wearer: address1Validated, - // }, - // ]; - - // const mintableAmount = await ThanksToken.read.mintableAmount([ - // address1Validated, - // relatedRoles, - // ]); - - // // Gather data for calculation - // const roleData = await gatherRoleData(address1Validated, relatedRoles); - // const tokenBalance = await ThanksToken.read.balanceOf([ - // address1Validated, - // ]); - // const addressCoefficient = await ThanksToken.read - // .addressCoefficient([address1Validated]) - // .catch(() => 0n); - // const defaultCoefficient = await ThanksToken.read - // .defaultCoefficient() - // .catch(() => 1000000000000000000n); // 1e18 as default - // const mintedAmount = await ThanksToken.read.mintedAmount([ - // address1Validated, - // ]); - - // const expectedMintableAmount = calculateExpectedMintableAmount( - // address1Validated, - // roleData, - // tokenBalance, - // addressCoefficient, - // defaultCoefficient, - // mintedAmount, - // ); - - // expect(mintableAmount).to.equal(expectedMintableAmount); - - // if (mintableAmount === 0n) { - // console.log("Setting up test data for mintableAmount calculation"); - // await ThanksToken.write.setAddressCoefficient([ - // address1Validated, - // 10000000000000000000n, // 10.0 in wei to ensure we get a non-zero value - // ]); - - // const updatedMintableAmount = await ThanksToken.read.mintableAmount([ - // address1Validated, - // relatedRoles, - // ]); - - // if (updatedMintableAmount === 0n) { - // console.log("Cannot get non-zero mintable amount for testing"); - // return; // Skip the test by returning early - // } - - // // Gather updated data for calculation - // const updatedRoleData = await gatherRoleData( - // address1Validated, - // relatedRoles, - // ); - // const updatedTokenBalance = await ThanksToken.read.balanceOf([ - // address1Validated, - // ]); - // const updatedAddressCoefficient = - // await ThanksToken.read.addressCoefficient([address1Validated]); - // const updatedDefaultCoefficient = - // await ThanksToken.read.defaultCoefficient(); - // const updatedMintedAmount = await ThanksToken.read.mintedAmount([ - // address1Validated, - // ]); - - // const expectedUpdatedAmount = calculateExpectedMintableAmount( - // address1Validated, - // updatedRoleData, - // updatedTokenBalance, - // updatedAddressCoefficient, - // updatedDefaultCoefficient, - // updatedMintedAmount, - // ); - - // expect(updatedMintableAmount).to.equal(expectedUpdatedAmount); - - // const amountToMint = updatedMintableAmount / 2n; - // return { updatedMintableAmount, amountToMint }; - // } - - // const amountToMint = mintableAmount / 2n; - - // const initialAddress2Balance = await ThanksToken.read.balanceOf([ - // address2Validated, - // ]); - // const initialAddress1MintedAmount = await ThanksToken.read.mintedAmount([ - // address1Validated, - // ]); - - // try { - // await ThanksToken.write.mint( - // [address2Validated, amountToMint, relatedRoles], - // { account: address1.account }, - // ); - // } catch (error: any) { - // console.log("Error minting tokens:", error.message); - // throw new Error(`Mint should have succeeded: ${error.message}`); - // } - - // const address2BalanceAfter = await ThanksToken.read.balanceOf([ - // address2Validated, - // ]); - // const address1MintedAmountAfter = await ThanksToken.read.mintedAmount([ - // address1Validated, - // ]); - - // expect(address2BalanceAfter).to.equal( - // initialAddress2Balance + amountToMint, - // ); - // expect(address1MintedAmountAfter).to.equal( - // initialAddress1MintedAmount + amountToMint, - // ); - // }); - - // it("should set address coefficient correctly", async () => { - // const relatedRoles = [ - // { - // hatId, - // wearer: address1Validated, - // }, - // ]; - - // const initialMintableAmount = await ThanksToken.read.mintableAmount([ - // address1Validated, - // relatedRoles, - // ]); - - // // Gather data for calculation - // const roleData = await gatherRoleData(address1Validated, relatedRoles); - // const tokenBalance = await ThanksToken.read.balanceOf([ - // address1Validated, - // ]); - // const addressCoefficient = await ThanksToken.read - // .addressCoefficient([address1Validated]) - // .catch(() => 0n); - // const defaultCoefficient = await ThanksToken.read - // .defaultCoefficient() - // .catch(() => 1000000000000000000n); // 1e18 as default - // const mintedAmount = await ThanksToken.read.mintedAmount([ - // address1Validated, - // ]); - - // const expectedInitialAmount = calculateExpectedMintableAmount( - // address1Validated, - // roleData, - // tokenBalance, - // addressCoefficient, - // defaultCoefficient, - // mintedAmount, - // ); - - // expect(initialMintableAmount).to.equal(expectedInitialAmount); - - // await ThanksToken.write.setAddressCoefficient([ - // address1Validated, - // 5000000000000000000n, // 5.0 in wei - // ]); - - // const newMintableAmount = await ThanksToken.read.mintableAmount([ - // address1Validated, - // relatedRoles, - // ]); - - // // Gather updated data for calculation - // const updatedRoleData = await gatherRoleData( - // address1Validated, - // relatedRoles, - // ); - // const updatedTokenBalance = await ThanksToken.read.balanceOf([ - // address1Validated, - // ]); - // const updatedAddressCoefficient = - // await ThanksToken.read.addressCoefficient([address1Validated]); - // const updatedDefaultCoefficient = - // await ThanksToken.read.defaultCoefficient(); - // const updatedMintedAmount = await ThanksToken.read.mintedAmount([ - // address1Validated, - // ]); - - // const expectedNewAmount = calculateExpectedMintableAmount( - // address1Validated, - // updatedRoleData, - // updatedTokenBalance, - // updatedAddressCoefficient, - // updatedDefaultCoefficient, - // updatedMintedAmount, - // ); - - // expect(newMintableAmount).to.equal(expectedNewAmount); - - // if (initialMintableAmount > 0n && newMintableAmount > 0n) { - // const ratio = Number(newMintableAmount) / Number(initialMintableAmount); - // console.log( - // `Coefficient test: initial=${initialMintableAmount}, new=${newMintableAmount}, ratio=${ratio}`, - // ); - - // expect(Number(newMintableAmount)).to.be.gt( - // Number(initialMintableAmount), - // ); - - // console.log( - // `Coefficient test: initial=${initialMintableAmount}, new=${newMintableAmount}, ratio=${Number(newMintableAmount) / Number(initialMintableAmount)}`, - // ); - // } - // }); - - // it("should set multiple address coefficients at once", async () => { - // await ThanksToken.write - // .setAddressCoefficient([ - // address1Validated, - // 1000000000000000000n, // 1.0 in wei (default) - // ]) - // .catch((error) => { - // console.log("Error setting address1 coefficient:", error.message); - // }); - - // await ThanksToken.write - // .setAddressCoefficient([ - // address2Validated, - // 1000000000000000000n, // 1.0 in wei (default) - // ]) - // .catch((error) => { - // console.log("Error setting address2 coefficient:", error.message); - // }); - - // const relatedRoles1 = [ - // { - // hatId, - // wearer: address1Validated, - // }, - // ]; - - // const relatedRoles2 = [ - // { - // hatId, - // wearer: address2Validated, - // }, - // ]; - - // const initialMintableAmount1 = await ThanksToken.read.mintableAmount([ - // address1Validated, - // relatedRoles1, - // ]); - - // const initialMintableAmount2 = await ThanksToken.read.mintableAmount([ - // address2Validated, - // relatedRoles2, - // ]); - - // // Gather data for calculation - address1 - // const roleData1 = await gatherRoleData(address1Validated, relatedRoles1); - // const tokenBalance1 = await ThanksToken.read.balanceOf([ - // address1Validated, - // ]); - // const addressCoefficient1 = await ThanksToken.read - // .addressCoefficient([address1Validated]) - // .catch(() => 0n); - // const defaultCoefficient1 = await ThanksToken.read - // .defaultCoefficient() - // .catch(() => 1000000000000000000n); // 1e18 as default - // const mintedAmount1 = await ThanksToken.read.mintedAmount([ - // address1Validated, - // ]); - - // const expectedInitialAmount1 = calculateExpectedMintableAmount( - // address1Validated, - // roleData1, - // tokenBalance1, - // addressCoefficient1, - // defaultCoefficient1, - // mintedAmount1, - // ); - - // // Gather data for calculation - address2 - // const roleData2 = await gatherRoleData(address2Validated, relatedRoles2); - // const tokenBalance2 = await ThanksToken.read.balanceOf([ - // address2Validated, - // ]); - // const addressCoefficient2 = await ThanksToken.read - // .addressCoefficient([address2Validated]) - // .catch(() => 0n); - // const defaultCoefficient2 = await ThanksToken.read - // .defaultCoefficient() - // .catch(() => 1000000000000000000n); // 1e18 as default - // const mintedAmount2 = await ThanksToken.read.mintedAmount([ - // address2Validated, - // ]); - - // const expectedInitialAmount2 = calculateExpectedMintableAmount( - // address2Validated, - // roleData2, - // tokenBalance2, - // addressCoefficient2, - // defaultCoefficient2, - // mintedAmount2, - // ); - - // expect(initialMintableAmount1).to.equal(expectedInitialAmount1); - // expect(initialMintableAmount2).to.equal(expectedInitialAmount2); - - // await ThanksToken.write.setAddressCoefficients([ - // [address1Validated, address2Validated], - // [2000000000000000000n, 3000000000000000000n], // 2.0 and 3.0 in wei - // ]); - - // const newMintableAmount1 = await ThanksToken.read.mintableAmount([ - // address1Validated, - // relatedRoles1, - // ]); - - // const newMintableAmount2 = await ThanksToken.read.mintableAmount([ - // address2Validated, - // relatedRoles2, - // ]); - - // // Gather updated data for calculation - address1 - // const updatedRoleData1 = await gatherRoleData( - // address1Validated, - // relatedRoles1, - // ); - // const updatedTokenBalance1 = await ThanksToken.read.balanceOf([ - // address1Validated, - // ]); - // const updatedAddressCoefficient1 = - // await ThanksToken.read.addressCoefficient([address1Validated]); - // const updatedDefaultCoefficient1 = - // await ThanksToken.read.defaultCoefficient(); - // const updatedMintedAmount1 = await ThanksToken.read.mintedAmount([ - // address1Validated, - // ]); - - // const expectedNewAmount1 = calculateExpectedMintableAmount( - // address1Validated, - // updatedRoleData1, - // updatedTokenBalance1, - // updatedAddressCoefficient1, - // updatedDefaultCoefficient1, - // updatedMintedAmount1, - // ); - - // // Gather updated data for calculation - address2 - // const updatedRoleData2 = await gatherRoleData( - // address2Validated, - // relatedRoles2, - // ); - // const updatedTokenBalance2 = await ThanksToken.read.balanceOf([ - // address2Validated, - // ]); - // const updatedAddressCoefficient2 = - // await ThanksToken.read.addressCoefficient([address2Validated]); - // const updatedDefaultCoefficient2 = - // await ThanksToken.read.defaultCoefficient(); - // const updatedMintedAmount2 = await ThanksToken.read.mintedAmount([ - // address2Validated, - // ]); - - // const expectedNewAmount2 = calculateExpectedMintableAmount( - // address2Validated, - // updatedRoleData2, - // updatedTokenBalance2, - // updatedAddressCoefficient2, - // updatedDefaultCoefficient2, - // updatedMintedAmount2, - // ); - - // expect(newMintableAmount1).to.equal(expectedNewAmount1); - // expect(newMintableAmount2).to.equal(expectedNewAmount2); - - // // Verify coefficients were set correctly - // const coefficient1 = await ThanksToken.read.addressCoefficient([ - // address1Validated, - // ]); - // const coefficient2 = await ThanksToken.read.addressCoefficient([ - // address2Validated, - // ]); - - // expect(coefficient1).to.equal(2000000000000000000n); - // expect(coefficient2).to.equal(3000000000000000000n); - // }); - - // it("should calculate mintable amount for user with RoleShare but without hat", async () => { - // // First, ensure address1 has a hat and RoleShare - // const isWearingHat = await Hats.read.balanceOf([ - // address1Validated, - // hatId, - // ]); - - // if (isWearingHat === 0n) { - // await HatsTimeFrameModule.write.mintHat([ - // hatId, - // address1Validated, - // BigInt(Math.floor(Date.now() / 1000) - 3600 * 10), // Wearing for 10 hours - // ]); - // } - - // // First, ensure we have a significant amount of tokens minted - // try { - // await FractionToken.write.mintInitialSupply( - // [hatId, address1Validated, 10000n], - // { account: deployer.account }, - // ); - // console.log("Successfully minted initial supply"); - // } catch (error: any) { - // console.log("Initial supply already minted, continuing"); - // } - - // const tokenId = await FractionToken.read.getTokenId([ - // hatId, - // address1Validated, - // ]); - - // const currentBalance = await FractionToken.read.balanceOf([ - // address1Validated, - // address1Validated, - // hatId, - // ]); - - // console.log(`Current balance for address1: ${currentBalance}`); - - // expect( - // currentBalance > 0n, - // "Need non-zero RoleShare balance for this test", - // ).to.be.true; - // console.log(`Current balance for address1: ${currentBalance}`); - - // const transferAmount = 1n; - - // // Transfer a small amount to address2 (who doesn't have the hat) - // await FractionToken.write - // .safeTransferFrom( - // [ - // address1Validated, - // address2Validated, - // tokenId, - // transferAmount, - // "0x", // empty bytes data - // ], - // { account: address1.account }, - // ) - // .then(() => { - // console.log( - // `Successfully transferred ${transferAmount} tokens to address2`, - // ); - // }) - // .catch((error: any) => { - // console.log(`Transfer failed: ${error.message}`); - // throw new Error( - // "Cannot test RoleShare without hat due to transfer failure", - // ); - // }); - - // const relatedRoles = [ - // { - // hatId, - // wearer: address1Validated, - // }, - // ]; - - // // Check mintable amount for address2 (has RoleShare but no hat) - // const mintableAmount = await ThanksToken.read.mintableAmount([ - // address2Validated, - // relatedRoles, - // ]); - - // await ThanksToken.write.setAddressCoefficient([ - // address2Validated, - // 10000000000000000000n, // 10.0 in wei to ensure we get a non-zero value - // ]); - - // // Check mintable amount again after setting coefficient - // const updatedMintableAmount = await ThanksToken.read.mintableAmount([ - // address2Validated, - // relatedRoles, - // ]); - - // // Gather data for calculation - // const roleData = await gatherRoleData(address2Validated, relatedRoles); - // const tokenBalance = await ThanksToken.read.balanceOf([ - // address2Validated, - // ]); - // const addressCoefficient = await ThanksToken.read.addressCoefficient([ - // address2Validated, - // ]); - // const defaultCoefficient = await ThanksToken.read.defaultCoefficient(); - // const mintedAmount = await ThanksToken.read.mintedAmount([ - // address2Validated, - // ]); - - // console.log( - // "Role data:", - // JSON.stringify( - // roleData, - // (key, value) => - // typeof value === "bigint" ? value.toString() : value, - // 2, - // ), - // ); - // console.log("Token balance:", tokenBalance.toString()); - // console.log("Address coefficient:", addressCoefficient.toString()); - // console.log("Default coefficient:", defaultCoefficient.toString()); - // console.log("Minted amount:", mintedAmount.toString()); - - // const expectedMintableAmount = calculateExpectedMintableAmount( - // address2Validated, - // roleData, - // tokenBalance, - // addressCoefficient, - // defaultCoefficient, - // mintedAmount, - // ); - - // // Check that the expected mintable amount matches the actual amount - // expect(updatedMintableAmount).to.equal(expectedMintableAmount); - - // // For this test, we specifically expect the amount to be greater than 0 - // // since we have RoleShare but no hat - // expect(Number(updatedMintableAmount)).to.be.gt(0); - - // console.log( - // `Mintable amount for user with RoleShare but no hat: ${updatedMintableAmount}`, - // ); - // }); - - // it("should prevent minting to yourself", async () => { - // const relatedRoles = [ - // { - // hatId, - // wearer: address1Validated, - // }, - // ]; - - // await ThanksToken.write.setAddressCoefficient([ - // address1Validated, - // 10000000000000000000n, // 10.0 in wei to ensure we get a non-zero value - // ]); - - // const mintableAmount = await ThanksToken.read.mintableAmount([ - // address1Validated, - // relatedRoles, - // ]); - - // expect( - // mintableAmount > 0n, - // "Need non-zero mintable amount to test self-minting prevention", - // ).to.be.true; - // console.log(`Mintable amount for self-minting test: ${mintableAmount}`); - - // try { - // await ThanksToken.write.mint( - // [address1Validated, mintableAmount, relatedRoles], - // { account: address1.account }, - // ); - // // If we get here, the mint succeeded when it should have failed - // expect.fail("Mint to self should have failed"); - // } catch (error: any) { - // expect(error.message).to.include("Cannot mint to yourself"); - // } - // }); - - // it("should prevent minting more than mintable amount", async () => { - // const relatedRoles3 = [ - // { - // hatId, - // wearer: address3Validated, - // }, - // ]; - - // await ThanksToken.write.setAddressCoefficient([ - // address3Validated, - // 10000000000000000000n, // 10.0 in wei to ensure we get a non-zero value - // ]); - - // const mintableAmount3 = await ThanksToken.read.mintableAmount([ - // address3Validated, - // relatedRoles3, - // ]); - - // // Gather data for calculation - // const roleData3 = await gatherRoleData(address3Validated, relatedRoles3); - // const tokenBalance3 = await ThanksToken.read.balanceOf([ - // address3Validated, - // ]); - // const addressCoefficient3 = await ThanksToken.read.addressCoefficient([ - // address3Validated, - // ]); - // const defaultCoefficient3 = await ThanksToken.read.defaultCoefficient(); - // const mintedAmount3 = await ThanksToken.read.mintedAmount([ - // address3Validated, - // ]); - - // const expectedMintableAmount3 = calculateExpectedMintableAmount( - // address3Validated, - // roleData3, - // tokenBalance3, - // addressCoefficient3, - // defaultCoefficient3, - // mintedAmount3, - // ); - - // expect(mintableAmount3).to.equal(expectedMintableAmount3); - - // expect( - // mintableAmount3 > 0n, - // "Need non-zero mintable amount to test minting limits", - // ).to.be.true; - // console.log(`Mintable amount for minting limit test: ${mintableAmount3}`); - - // try { - // await ThanksToken.write.mint( - // [address1Validated, mintableAmount3 + 1n, relatedRoles3], - // { account: address3.account }, - // ); - // // If we get here, the mint succeeded when it should have failed - // expect.fail("Mint exceeding mintable amount should have failed"); - // } catch (error: any) { - // expect(error.message).to.include("Amount exceeds mintable amount"); - // } - // }); - - // it("should use default coefficient when address coefficient is not set", async () => { - // // Reset address coefficient to 0 - // await ThanksToken.write.setAddressCoefficient([address1Validated, 0n]); - - // // Set default coefficient - // const newDefaultCoefficient = 2000000000000000000n; // 2.0 in wei - // await ThanksToken.write.setDefaultCoefficient([newDefaultCoefficient]); - - // const relatedRoles = [ - // { - // hatId, - // wearer: address1Validated, - // }, - // ]; - - // const mintableAmount = await ThanksToken.read.mintableAmount([ - // address1Validated, - // relatedRoles, - // ]); - - // // Gather data for calculation - // const roleData = await gatherRoleData(address1Validated, relatedRoles); - // const tokenBalance = await ThanksToken.read.balanceOf([ - // address1Validated, - // ]); - // const addressCoefficient = await ThanksToken.read.addressCoefficient([ - // address1Validated, - // ]); - // const defaultCoefficient = await ThanksToken.read.defaultCoefficient(); - // const mintedAmount = await ThanksToken.read.mintedAmount([ - // address1Validated, - // ]); - - // const expectedMintableAmount = calculateExpectedMintableAmount( - // address1Validated, - // roleData, - // tokenBalance, - // addressCoefficient, - // defaultCoefficient, - // mintedAmount, - // ); - - // expect(mintableAmount).to.equal(expectedMintableAmount); - // expect(defaultCoefficient).to.equal(newDefaultCoefficient); - - // // Reset default coefficient for other tests - // await ThanksToken.write - // .setDefaultCoefficient([1000000000000000000n]) - // .catch((error) => { - // console.log("Error resetting default coefficient:", error.message); - // }); - // }); - // }); + describe("ThanksToken specific functions", () => { + it("should mint tokens correctly", async () => { + const isWearingHat = await Hats.read.balanceOf([ + address1Validated, + hatId, + ]); + + if (isWearingHat === 0n) { + await HatsTimeFrameModule.write.mintHat([ + hatId, + address1Validated, + BigInt(Math.floor(Date.now() / 1000) - 3600 * 10), // Wearing for 10 hours + ]); + } else { + console.log("Address already wearing hat, skipping time update"); + } + + const fractionBalance = await HatsFractionTokenModule.read.balanceOf([ + address1Validated, + address1Validated, + hatId, + ]); + + if (fractionBalance === 0n) { + await HatsFractionTokenModule.write + .mintInitialSupply([hatId, address1Validated, 0n], { + account: deployer.account, + }) + .catch(() => { + /* Ignore if already minted */ + }); + } + + const relatedRoles = [ + { + hatId, + wearer: address1Validated, + }, + ]; + + const mintableAmount = await DeployedThanksToken.read.mintableAmount([ + address1Validated, + relatedRoles, + ]); + const mintedAmount = await DeployedThanksToken.read.mintedAmount([ + address1Validated, + ]); + + // Mintable amount - minted amount + // (10 hours * 10 coefficient) - 50 + expect(mintableAmount).to.equal(50n); + + const amountToMint = mintableAmount / 2n; + + const initialAddress2Balance = await DeployedThanksToken.read.balanceOf([ + address2Validated, + ]); + const initialAddress1MintedAmount = + await DeployedThanksToken.read.mintedAmount([address1Validated]); + + try { + await DeployedThanksToken.write.mint( + [address2Validated, amountToMint, relatedRoles], + { account: address1.account }, + ); + } catch (error: any) { + console.log("Error minting tokens:", error.message); + throw new Error(`Mint should have succeeded: ${error.message}`); + } + + const address2BalanceAfter = await DeployedThanksToken.read.balanceOf([ + address2Validated, + ]); + const address1MintedAmountAfter = + await DeployedThanksToken.read.mintedAmount([address1Validated]); + + expect(address2BalanceAfter).to.equal( + initialAddress2Balance + amountToMint, + ); + expect(address1MintedAmountAfter).to.equal( + initialAddress1MintedAmount + amountToMint, + ); + }); + + it("should update address coeefficient", async () => { + const address1CoefficientBefore = + await DeployedThanksToken.read.addressCoefficient([address1Validated]); + expect(address1CoefficientBefore).to.equal(10000000000000000000n); // 10.0 in wei + + await DeployedThanksToken.write.setAddressCoefficient([ + address1Validated, + 20000000000000000000n, // 20.0 in wei + ]); + + const address1CoefficientAfter = + await DeployedThanksToken.read.addressCoefficient([address1Validated]); + + expect(address1CoefficientAfter).to.equal(20000000000000000000n); + + await DeployedThanksToken.write.setAddressCoefficients([ + [address2Validated, address3Validated], + [30000000000000000000n, 40000000000000000000n], // 30.0 and 40.0 in wei + ]); + + const address2CoefficientAfter = + await DeployedThanksToken.read.addressCoefficient([address2Validated]); + const address3CoefficientAfter = + await DeployedThanksToken.read.addressCoefficient([address3Validated]); + + expect(address2CoefficientAfter).to.equal(30000000000000000000n); + expect(address3CoefficientAfter).to.equal(40000000000000000000n); + }); + + it("should calculate mintable amount for user with RoleShare but without hat", async () => { + // First, ensure address1 has a hat and RoleShare + const isWearingHat = await Hats.read.balanceOf([ + address1Validated, + hatId, + ]); + + if (isWearingHat === 0n) { + await HatsTimeFrameModule.write.mintHat([ + hatId, + address1Validated, + BigInt(Math.floor(Date.now() / 1000) - 3600 * 10), // Wearing for 10 hours + ]); + } + + // First, ensure we have a significant amount of tokens minted + try { + await HatsFractionTokenModule.write.mintInitialSupply( + [hatId, address1Validated, 10000n], + { account: deployer.account }, + ); + console.log("Successfully minted initial supply"); + } catch (error: any) { + console.log("Initial supply already minted, continuing"); + } + + const tokenId = await HatsFractionTokenModule.read.getTokenId([ + hatId, + address1Validated, + ]); + + const currentBalance = await HatsFractionTokenModule.read.balanceOf([ + address1Validated, + address1Validated, + hatId, + ]); + + expect( + currentBalance > 0n, + "Need non-zero RoleShare balance for this test", + ).to.be.true; + + const transferAmount = 1000n; + + // Transfer a small amount to address2 (who doesn't have the hat) + await HatsFractionTokenModule.write.safeTransferFrom( + [ + address1Validated, + address2Validated, + tokenId, + transferAmount, + "0x", // empty bytes data + ], + { account: address1.account }, + ); + + const relatedRoles = [ + { + hatId, + wearer: address1Validated, + }, + ]; + + // Check mintable amount for address2 (has RoleShare but no hat) + const mintableAmount = await DeployedThanksToken.read.mintableAmount([ + address2Validated, + relatedRoles, + ]); + + // Check that the expected mintable amount matches the actual amount + // ((10h * 1000 / 10000) + (20 / 10) GiveBack) * 30 Coefficient + // 3 * 30 + expect(mintableAmount).to.equal(90n); + }); + + it("should prevent minting to yourself", async () => { + const relatedRoles = [ + { + hatId, + wearer: address1Validated, + }, + ]; + + try { + await DeployedThanksToken.write.mint( + [address1Validated, 1n, relatedRoles], + { account: address1.account }, + ); + // If we get here, the mint succeeded when it should have failed + expect.fail("Mint to self should have failed"); + } catch (error: any) { + expect(error.message).to.include("Cannot mint to yourself"); + } + }); + + it("should prevent minting more than mintable amount", async () => { + const relatedRoles = [ + { + hatId, + wearer: address1Validated, + }, + ]; + try { + await DeployedThanksToken.write.mint( + [address2Validated, 1000000n, relatedRoles], + { account: address1.account }, + ); + // If we get here, the mint succeeded when it should have failed + expect.fail("Mint exceeding mintable amount should have failed"); + } catch (error: any) { + expect(error.message).to.include("Amount exceeds mintable amount"); + } + }); + }); });