diff --git a/pkgs/contract/contracts/fractiontoken/FractionToken.sol b/pkgs/contract/contracts/fractiontoken/FractionToken.sol index 177f21d..df9f9f9 100644 --- a/pkgs/contract/contracts/fractiontoken/FractionToken.sol +++ b/pkgs/contract/contracts/fractiontoken/FractionToken.sol @@ -33,7 +33,7 @@ contract FractionToken is ERC1155Upgradeable, ERC2771ContextUpgradeable{ require( amount <= TOKEN_SUPPLY, "Amount exceeds token supply" - ) + ); require( _hasHatAuthority(hatId), diff --git a/pkgs/contract/contracts/fractiontoken/IFractionToken.sol b/pkgs/contract/contracts/fractiontoken/IFractionToken.sol index d430b29..7f68caa 100644 --- a/pkgs/contract/contracts/fractiontoken/IFractionToken.sol +++ b/pkgs/contract/contracts/fractiontoken/IFractionToken.sol @@ -5,7 +5,11 @@ pragma solidity ^0.8.24; import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; interface IFractionToken is IERC1155 { - function mint(string memory hatId, address account) external; + function mintInitialSupply( + string memory hatId, + address account, + uint256 amount + ) external; function burn( address from, diff --git a/pkgs/contract/gas-report.txt b/pkgs/contract/gas-report.txt index be3d880..21fe516 100644 --- a/pkgs/contract/gas-report.txt +++ b/pkgs/contract/gas-report.txt @@ -7,25 +7,7 @@ ··············|··········|··············|·············|·············|···············|·············· | Deployments · · % of limit · │ ·························|··············|·············|·············|···············|·············· -| BigBang · - · - · 1248128 · 4.2 % · - │ -·························|··············|·············|·············|···············|·············· -| FractionToken · - · - · 2859199 · 9.5 % · - │ +| FractionToken · - · - · 3076626 · 10.3 % · - │ ·························|··············|·············|·············|···············|·············· | Hats · - · - · 7032431 · 23.4 % · - │ -·························|··············|·············|·············|···············|·············· -| HatsModule · - · - · 754132 · 2.5 % · - │ -·························|··············|·············|·············|···············|·············· -| HatsModuleFactory · - · - · 1101122 · 3.7 % · - │ -·························|··············|·············|·············|···············|·············· -| HatsTimeFrameModule · - · - · 1287099 · 4.3 % · - │ -·························|··············|·············|·············|···············|·············· -| PullSplitFactory · - · - · 4535827 · 15.1 % · - │ -·························|··············|·············|·············|···············|·············· -| PushSplitFactory · - · - · 4483113 · 14.9 % · - │ -·························|··············|·············|·············|···············|·············· -| SplitsCreator · - · - · 1487532 · 5 % · - │ -·························|··············|·············|·············|···············|·············· -| SplitsCreatorFactory · - · - · 526836 · 1.8 % · - │ -·························|··············|·············|·············|···············|·············· -| SplitsWarehouse · - · - · 3934655 · 13.1 % · - │ ·------------------------|--------------|-------------|-------------|---------------|-------------· \ No newline at end of file diff --git a/pkgs/contract/test/Burn.ts b/pkgs/contract/test/Burn.ts deleted file mode 100644 index f6a76cc..0000000 --- a/pkgs/contract/test/Burn.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { expect } from "chai"; -import { viem } from "hardhat"; -import { decodeEventLog, PublicClient, WalletClient, zeroAddress } from "viem"; -import { - deployFractionToken, - FractionToken, -} from "../helpers/deploy/FractionToken"; -import { deployHatsProtocol, Hats } from "../helpers/deploy/Hats"; - -describe("Burn", () => { - let Hats: Hats; - let FractionToken: FractionToken; - - let address1: WalletClient; - let address2: WalletClient; - let address3: WalletClient; - - let hatId: bigint; - - let publicClient: PublicClient; - - before(async () => { - const { Hats: _Hats } = await deployHatsProtocol(); - Hats = _Hats; - - const { FractionToken: _FractionToken } = await deployFractionToken( - "", - 10000n, - Hats.address, - zeroAddress - ); - FractionToken = _FractionToken; - - [address1, address2, address3] = await viem.getWalletClients(); - - publicClient = await viem.getPublicClient(); - - await Hats.write.mintTopHat([ - address1.account?.address!, - "Description", - "https://test.com/tophat.png", - ]); - - let txHash = await Hats.write.createHat([ - BigInt( - "0x0000000100000000000000000000000000000000000000000000000000000000" - ), - "role1", - 10, - "0x0000000000000000000000000000000000004a75", - "0x0000000000000000000000000000000000004a75", - true, - "https://test.com/hat_image.png", - ]); - - let receipt = await publicClient.waitForTransactionReceipt({ - hash: txHash, - }); - - for (const log of receipt.logs) { - try { - const decodedLog = decodeEventLog({ - abi: Hats.abi, - data: log.data, - topics: log.topics, - }); - if (decodedLog.eventName == "HatCreated") hatId = decodedLog.args.id; - } catch (error) {} - } - - // address1とaddress2にHatをmint - await Hats.write.mintHat([hatId, address1.account?.address!]); - await Hats.write.mintHat([hatId, address2.account?.address!]); - - // address1とaddress2にFractionTokenをmint - await FractionToken.write.mint([hatId, address1.account?.address!]); - await FractionToken.write.mint([hatId, address2.account?.address!]); - - const tokenId = await FractionToken.read.getTokenId([ - hatId, - address2.account?.address!, - ]); - - // address2のtokenの半分をaddress3に移動 - await FractionToken.write.safeTransferFrom( - [ - address2.account?.address!, - address3.account?.address!, - tokenId, - 5000n, - "0x", - ], - { - account: address2.account!, - } - ); - }); - - it("should burn tokens", async () => { - // address1のtokenを自身で半分burnする - await FractionToken.write.burn( - [address1.account?.address!, address1.account?.address!, hatId, 5000n], - { - account: address1.account!, - } - ); - - // address3のtokenをaddress2によってすべてburnする - await FractionToken.write.burn( - [address3.account?.address!, address2.account?.address!, hatId, 5000n], - { - account: address2.account!, - } - ); - - // address3のtokenをaddress1によってすべてburnするとRevertする - await expect( - FractionToken.write.burn( - [address3.account?.address!, address2.account?.address!, hatId, 5000n], - { - account: address1.account!, - } - ) - ).to.be.rejectedWith("not authorized"); - - let balance: bigint; - - // address1のbalance - balance = await FractionToken.read.balanceOf([ - address1.account?.address!, - address1.account?.address!, - hatId, - ]); - expect(balance).to.equal(5000n); - - // address2のbalance - balance = await FractionToken.read.balanceOf([ - address2.account?.address!, - address2.account?.address!, - hatId, - ]); - expect(balance).to.equal(5000n); - - // address3のbalance - balance = await FractionToken.read.balanceOf([ - address3.account?.address!, - address2.account?.address!, - hatId, - ]); - expect(balance).to.equal(0n); - }); -}); diff --git a/pkgs/contract/test/FractionToken.ts b/pkgs/contract/test/FractionToken.ts index 9f4e036..72373bd 100644 --- a/pkgs/contract/test/FractionToken.ts +++ b/pkgs/contract/test/FractionToken.ts @@ -17,6 +17,7 @@ describe("FractionToken", () => { let address4: WalletClient; let hatId: bigint; + let topHatId: bigint; let publicClient: PublicClient; @@ -36,13 +37,28 @@ describe("FractionToken", () => { publicClient = await viem.getPublicClient(); - await Hats.write.mintTopHat([ + let tx1 = await Hats.write.mintTopHat([ address1.account?.address!, "Description", "https://test.com/tophat.png", ]); - let txHash = await Hats.write.createHat([ + let receipt1 = await publicClient.waitForTransactionReceipt({ + hash: tx1, + }); + + for (const log of receipt1.logs) { + try { + const decodedLog = decodeEventLog({ + abi: Hats.abi, + data: log.data, + topics: log.topics, + }); + if (decodedLog.eventName === "HatCreated") topHatId = decodedLog.args.id; + } catch (error) {} + } + + let tx2 = await Hats.write.createHat([ BigInt( "0x0000000100000000000000000000000000000000000000000000000000000000" ), @@ -54,11 +70,11 @@ describe("FractionToken", () => { "https://test.com/hat_image.png", ]); - let receipt = await publicClient.waitForTransactionReceipt({ - hash: txHash, + let receipt2 = await publicClient.waitForTransactionReceipt({ + hash: tx2, }); - for (const log of receipt.logs) { + for (const log of receipt2.logs) { try { const decodedLog = decodeEventLog({ abi: Hats.abi, @@ -69,26 +85,44 @@ describe("FractionToken", () => { } catch (error) {} } - // address1,address2,address4にHatをmint - await Hats.write.mintHat([hatId, address1.account?.address!]); + // address2,address3にHatをmint await Hats.write.mintHat([hatId, address2.account?.address!]); + await Hats.write.mintHat([hatId, address3.account?.address!]); }); it("should mint, transfer and burn tokens", async () => { - // address1,address2にtokenをmint - await FractionToken.write.mint([hatId, address1.account?.address!]); - await FractionToken.write.mint([hatId, address2.account?.address!]); + // address1がaddress2,address3にtokenをmint + await FractionToken.write.mintInitialSupply( + [ + hatId, + address2.account?.address!, + 10000n, + ], + { + account: address1.account!, + } + ); + await FractionToken.write.mintInitialSupply( + [ + hatId, + address3.account?.address!, + 10000n, + ], + { + account: address1.account!, + } + ); const tokenId = await FractionToken.read.getTokenId([ hatId, address2.account?.address!, ]); - // address2のtokenの半分をaddress3に移動 + // address2のtokenの半分をaddress4に移動 await FractionToken.write.safeTransferFrom( [ address2.account?.address!, - address3.account?.address!, + address4.account?.address!, tokenId, 5000n, "0x", @@ -98,76 +132,78 @@ describe("FractionToken", () => { } ); - // address1のtokenを自ら半分burnする + // address2のtokenをaddress1が半分burnする await FractionToken.write.burn( - [address1.account?.address!, address1.account?.address!, hatId, 5000n], + [ + address2.account?.address!, + address2.account?.address!, + hatId, + 2500n + ], { account: address1.account!, } ); - // address3のtokenをaddress2によって半分burnする - await FractionToken.write.burn( - [address3.account?.address!, address2.account?.address!, hatId, 2500n], - { - account: address2.account!, - } - ); - let balance: bigint; - // address1のbalance - balance = await FractionToken.read.balanceOf([ - address1.account?.address!, - address1.account?.address!, - hatId, - ]); - expect(balance).to.equal(5000n); - // address2のbalance balance = await FractionToken.read.balanceOf([ address2.account?.address!, address2.account?.address!, hatId, ]); - expect(balance).to.equal(5000n); + expect(balance).to.equal(2500n); // address3のbalance balance = await FractionToken.read.balanceOf([ address3.account?.address!, + address3.account?.address!, + hatId, + ]); + expect(balance).to.equal(10000n); + + // address4のbalance + balance = await FractionToken.read.balanceOf([ + address4.account?.address!, address2.account?.address!, hatId, ]); - expect(balance).to.equal(2500n); + expect(balance).to.equal(5000n); }); it("should fail to mint a token", async () => { - // roleのない人にtokenはmintできない + // 権限のない人にtokenはmintできない await FractionToken.write - .mint([hatId, address3.account?.address!]) + .mintInitialSupply([hatId, address4.account?.address!, 10000n]) .catch((error: any) => { - expect(error.message).to.include("not authorized"); + expect(error.message).to.include("Not authorized"); }); // tokenは二度mintできない await FractionToken.write - .mint([hatId, address1.account?.address!]) + .mintInitialSupply([hatId, address2.account?.address!, 10000n]) .catch((error: any) => { - expect(error.message).to.include("already received"); + expect(error.message).to.include("This account has already received"); }); }); it("should fail to burn a token", async () => { - // address1のtokenはaddress2によってburnできない + // address2のtokenはaddress3によってburnできない await FractionToken.write - .burn([ - address1.account?.address!, - address1.account?.address!, - hatId, - 5000n, - ]) + .burn( + [ + address2.account?.address!, + address2.account?.address!, + hatId, + 5000n, + ], + { + account: address3.account!, + } + ) .catch((error: any) => { - expect(error.message).to.include("not authorized"); + expect(error.message).to.include("Not authorized"); }); }); });