From 552c8f79403356c9ea7e00f9b43379d3c7c3286a Mon Sep 17 00:00:00 2001 From: koo-virtuals Date: Fri, 23 Jan 2026 14:58:56 +0800 Subject: [PATCH 1/8] support project60days tax creator change with limitation --- contracts/launchpadv2/BondingV2.sol | 56 +++ contracts/tax/AgentTax.sol | 47 +- test/project60days/project60days.js | 637 ++++++++++++++++++++++++++++ 3 files changed, 739 insertions(+), 1 deletion(-) create mode 100644 test/project60days/project60days.js diff --git a/contracts/launchpadv2/BondingV2.sol b/contracts/launchpadv2/BondingV2.sol index 58dbba1..d378700 100644 --- a/contracts/launchpadv2/BondingV2.sol +++ b/contracts/launchpadv2/BondingV2.sol @@ -94,6 +94,9 @@ contract BondingV2 is // this is for BE to separate with old virtualId from bondingV1, but this field is not used yet uint256 public constant VirtualIdBase = 20_000_000_000; + // Mapping to mark tokens that allow tax recipient updates + mapping(address => bool) public allowTaxRecipientUpdate; + event PreLaunched( address indexed token, address indexed pair, @@ -202,6 +205,55 @@ contract BondingV2 is uint256 purchaseAmount, uint256 startTime ) public nonReentrant returns (address, address, uint, uint256) { + return + _preLaunch( + _name, + _ticker, + cores, + desc, + img, + urls, + purchaseAmount, + startTime, + false // allowTaxRecipientUpdate_ defaults to false for backward compatibility + ); + } + + function preLaunchProject60days( + string memory _name, + string memory _ticker, + uint8[] memory cores, + string memory desc, + string memory img, + string[4] memory urls, + uint256 purchaseAmount, + uint256 startTime + ) public nonReentrant returns (address, address, uint, uint256) { + return + _preLaunch( + _name, + _ticker, + cores, + desc, + img, + urls, + purchaseAmount, + startTime, + true // allowTaxRecipientUpdate_ defaults to true for Project60days + ); + } + + function _preLaunch( + string memory _name, + string memory _ticker, + uint8[] memory cores, + string memory desc, + string memory img, + string[4] memory urls, + uint256 purchaseAmount, + uint256 startTime, + bool allowTaxRecipientUpdate_ + ) internal returns (address, address, uint, uint256) { if (purchaseAmount < fee || cores.length <= 0) { revert InvalidInput(); } @@ -292,6 +344,10 @@ contract BondingV2 is newToken.applicationId = applicationId; newToken.initialPurchase = initialPurchase; newToken.virtualId = VirtualIdBase + tokenInfos.length; + // Mark token if it allows tax recipient updates + if (allowTaxRecipientUpdate_) { + allowTaxRecipientUpdate[token] = true; + } newToken.launchExecuted = false; // Set Data struct fields diff --git a/contracts/tax/AgentTax.sol b/contracts/tax/AgentTax.sol index a5e33b9..77d1bb8 100644 --- a/contracts/tax/AgentTax.sol +++ b/contracts/tax/AgentTax.sol @@ -9,6 +9,7 @@ import "@openzeppelin/contracts/utils/math/Math.sol"; import "../pool/IRouter.sol"; import "../virtualPersona/IAgentNft.sol"; import "./ITBABonus.sol"; +import "../launchpadv2/BondingV2.sol"; contract AgentTax is Initializable, AccessControlUpgradeable { using SafeERC20 for IERC20; @@ -37,6 +38,7 @@ contract AgentTax is Initializable, AccessControlUpgradeable { uint256 public minSwapThreshold; uint256 public maxSwapThreshold; IAgentNft public agentNft; + BondingV2 public bondingV2; event SwapThresholdUpdated( uint256 oldMinThreshold, @@ -342,6 +344,49 @@ contract AgentTax is Initializable, AccessControlUpgradeable { emit CreatorUpdated(agentId, tba, creator); } + /** + * @notice Set BondingV2 contract address + * @param bondingV2_ The address of the BondingV2 contract + */ + function setBondingV2(address bondingV2_) public onlyRole(ADMIN_ROLE) { + require(bondingV2_ != address(0), "Invalid BondingV2 address"); + bondingV2 = BondingV2(bondingV2_); + } + + /** + * @notice Update tax recipient for new feature agents launched via BondingV2 + * @param agentId The agentId (virtualId) of the agent + * @param tba The Token Bound Address for the agent + * @param creator The creator address that will receive tax rewards + */ + function updateCreatorForProject60daysAgents( + uint256 agentId, + address tba, + address creator + ) public onlyRole(EXECUTOR_V2_ROLE) { + require(address(bondingV2) != address(0), "BondingV2 not set"); + + // Get token address from agentId + IAgentNft.VirtualInfo memory info = agentNft.virtualInfo(agentId); + address token = info.token; + require(token != address(0), "Token not found"); + + // Check if this token allows tax recipient updates + require( + bondingV2.allowTaxRecipientUpdate(token), + "Token does not allow tax recipient updates" + ); + + require(tba != address(0), "Invalid TBA"); + require(creator != address(0), "Invalid creator"); + + TaxRecipient storage recipient = _agentRecipients[agentId]; + address oldCreator = recipient.creator; + recipient.tba = tba; + recipient.creator = creator; + emit CreatorUpdated(agentId, oldCreator, creator); + } + function dcaSell( uint256[] memory agentIds, uint256 minConversionRate, @@ -365,7 +410,7 @@ contract AgentTax is Initializable, AccessControlUpgradeable { if (amountToSwap > maxOverride) { amountToSwap = maxOverride; } - + uint256 minOutput = (amountToSwap * minConversionRate) / rateDenom; _swapForAsset(agentId, minOutput, maxOverride); } diff --git a/test/project60days/project60days.js b/test/project60days/project60days.js new file mode 100644 index 0000000..beba44d --- /dev/null +++ b/test/project60days/project60days.js @@ -0,0 +1,637 @@ +const { expect } = require("chai"); +const { ethers, upgrades } = require("hardhat"); +const { time } = require("@nomicfoundation/hardhat-network-helpers"); +const { + loadFixture, +} = require("@nomicfoundation/hardhat-toolbox/network-helpers"); +const { setupNewLaunchpadTest } = require("../launchpadv2/setup"); +const { + START_TIME_DELAY, + INITIAL_SUPPLY, + TEAM_TOKEN_RESERVED_SUPPLY, +} = require("../launchpadv2/const"); + +describe("Project60days - AgentTax Integration", function () { + let setup; + let contracts; + let accounts; + let addresses; + let agentTax; + let bondingV2; + let virtualToken; + let agentNftV2; + + before(async function () { + setup = await loadFixture(setupNewLaunchpadTest); + contracts = setup.contracts; + accounts = setup.accounts; + addresses = setup.addresses; + + bondingV2 = contracts.bondingV2; + virtualToken = contracts.virtualToken; + agentNftV2 = contracts.agentNftV2; + + // Deploy AgentTax contract + console.log("\n--- Deploying AgentTax ---"); + const AgentTax = await ethers.getContractFactory("AgentTax"); + agentTax = await upgrades.deployProxy( + AgentTax, + [ + accounts.owner.address, // defaultAdmin_ + await virtualToken.getAddress(), // assetToken_ + await virtualToken.getAddress(), // taxToken_ + addresses.fRouterV2, // router_ + accounts.owner.address, // treasury_ + ethers.parseEther("100"), // minSwapThreshold_ + ethers.parseEther("10000"), // maxSwapThreshold_ + await agentNftV2.getAddress(), // nft_ + ], + { initializer: "initialize" } + ); + await agentTax.waitForDeployment(); + console.log("AgentTax deployed at:", await agentTax.getAddress()); + + // Set BondingV2 address in AgentTax + await agentTax.setBondingV2(await bondingV2.getAddress()); + console.log("BondingV2 address set in AgentTax"); + + // Grant EXECUTOR_V2_ROLE to accounts.admin for testing + const EXECUTOR_V2_ROLE = await agentTax.EXECUTOR_V2_ROLE(); + await agentTax.grantRole(EXECUTOR_V2_ROLE, accounts.admin.address); + console.log("EXECUTOR_V2_ROLE granted to admin"); + }); + + describe("preLaunchProject60days", function () { + it("Should create a token with allowTaxRecipientUpdate set to true", async function () { + const { user1 } = accounts; + + const tokenName = "Project60days Token"; + const tokenTicker = "P60"; + const cores = [0, 1, 2]; + const description = "Project60days test token"; + const image = "https://example.com/image.png"; + const urls = [ + "https://twitter.com/test", + "https://t.me/test", + "https://youtube.com/test", + "https://example.com", + ]; + const purchaseAmount = ethers.parseEther("1000"); + const startTime = (await time.latest()) + START_TIME_DELAY + 1; + + // Approve virtual tokens + await virtualToken + .connect(user1) + .approve(await bondingV2.getAddress(), purchaseAmount); + + // Call preLaunchProject60days + const tx = await bondingV2 + .connect(user1) + .preLaunchProject60days( + tokenName, + tokenTicker, + cores, + description, + image, + urls, + purchaseAmount, + startTime + ); + + const receipt = await tx.wait(); + const event = receipt.logs.find((log) => { + try { + const parsed = bondingV2.interface.parseLog(log); + return parsed && parsed.name === "PreLaunched"; + } catch (e) { + return false; + } + }); + + expect(event).to.not.be.undefined; + const parsedEvent = bondingV2.interface.parseLog(event); + const tokenAddress = parsedEvent.args.token; + + // Verify allowTaxRecipientUpdate is set to true + const allowUpdate = await bondingV2.allowTaxRecipientUpdate(tokenAddress); + expect(allowUpdate).to.be.true; + + // Verify token info + const tokenInfo = await bondingV2.tokenInfo(tokenAddress); + expect(tokenInfo.token).to.equal(tokenAddress); + expect(tokenInfo.creator).to.equal(user1.address); + }); + + it("Should emit PreLaunched event with correct parameters", async function () { + const { user1 } = accounts; + + const tokenName = "Project60days Token 2"; + const tokenTicker = "P602"; + const cores = [0, 1]; + const description = "Project60days test token 2"; + const image = "https://example.com/image2.png"; + const urls = [ + "https://twitter.com/test2", + "https://t.me/test2", + "https://youtube.com/test2", + "https://example2.com", + ]; + const purchaseAmount = ethers.parseEther("2000"); + const startTime = (await time.latest()) + START_TIME_DELAY + 1; + + await virtualToken + .connect(user1) + .approve(await bondingV2.getAddress(), purchaseAmount); + + const tx = await bondingV2 + .connect(user1) + .preLaunchProject60days( + tokenName, + tokenTicker, + cores, + description, + image, + urls, + purchaseAmount, + startTime + ); + + await expect(tx) + .to.emit(bondingV2, "PreLaunched") + .withArgs( + (token) => token !== ethers.ZeroAddress, + (pair) => pair !== ethers.ZeroAddress, + (virtualId) => virtualId > 0n, + (initialPurchase) => initialPurchase > 0n + ); + }); + }); + + describe("updateCreatorForProject60daysAgents", function () { + let tokenAddress; + let agentId; + + beforeEach(async function () { + const { user1 } = accounts; + + // Create a Project60days token + const tokenName = "Test Project60days"; + const tokenTicker = "TP60"; + const cores = [0, 1, 2]; + const description = "Test description"; + const image = "https://example.com/image.png"; + const urls = [ + "https://twitter.com/test", + "https://t.me/test", + "https://youtube.com/test", + "https://example.com", + ]; + const purchaseAmount = ethers.parseEther("1000"); + const startTime = (await time.latest()) + START_TIME_DELAY + 1; + + await virtualToken + .connect(user1) + .approve(await bondingV2.getAddress(), purchaseAmount); + + const tx = await bondingV2 + .connect(user1) + .preLaunchProject60days( + tokenName, + tokenTicker, + cores, + description, + image, + urls, + purchaseAmount, + startTime + ); + + const receipt = await tx.wait(); + const event = receipt.logs.find((log) => { + try { + const parsed = bondingV2.interface.parseLog(log); + return parsed && parsed.name === "PreLaunched"; + } catch (e) { + return false; + } + }); + + const parsedEvent = bondingV2.interface.parseLog(event); + tokenAddress = parsedEvent.args.token; + + // Launch and graduate token to get agentId + await time.increase(START_TIME_DELAY + 1); + await bondingV2.connect(user1).launch(tokenAddress); + + // Buy tokens to graduate + await time.increase(100 * 60); // Wait for anti-sniper tax to expire + const buyAmount = ethers.parseEther("202020.2044906205"); + await virtualToken + .connect(accounts.user2) + .approve(await bondingV2.getAddress(), buyAmount); + await bondingV2 + .connect(accounts.user2) + .buy( + buyAmount, + tokenAddress, + 0, + (await time.latest()) + 300 + ); + + // Find agentId from agentNft + const nextVirtualId = await agentNftV2.nextVirtualId(); + for (let i = 1; i < nextVirtualId; i++) { + try { + const virtualInfo = await agentNftV2.virtualInfo(i); + const tokenInfo = await bondingV2.tokenInfo(tokenAddress); + if (virtualInfo.token === tokenInfo.agentToken) { + agentId = BigInt(i); + break; + } + } catch (e) { + continue; + } + } + + expect(agentId).to.not.be.undefined; + }); + + it("Should update tax recipient for Project60days agent", async function () { + const { admin } = accounts; + const newTba = ethers.Wallet.createRandom().address; + const newCreator = ethers.Wallet.createRandom().address; + + // Verify token allows tax recipient updates + const allowUpdate = await bondingV2.allowTaxRecipientUpdate(tokenAddress); + expect(allowUpdate).to.be.true; + + // Update tax recipient + const tx = await agentTax + .connect(admin) + .updateCreatorForProject60daysAgents(agentId, newTba, newCreator); + + await expect(tx) + .to.emit(agentTax, "CreatorUpdated") + .withArgs( + agentId, + (oldCreator) => oldCreator !== ethers.ZeroAddress, + newCreator + ); + }); + + it("Should revert if token does not allow tax recipient updates", async function () { + const { user1, admin } = accounts; + + // Create a regular token (not Project60days) + const tokenName = "Regular Token"; + const tokenTicker = "REG"; + const cores = [0, 1]; + const description = "Regular token"; + const image = "https://example.com/image.png"; + const urls = [ + "https://twitter.com/test", + "https://t.me/test", + "https://youtube.com/test", + "https://example.com", + ]; + const purchaseAmount = ethers.parseEther("1000"); + const startTime = (await time.latest()) + START_TIME_DELAY + 1; + + await virtualToken + .connect(user1) + .approve(await bondingV2.getAddress(), purchaseAmount); + + const tx = await bondingV2 + .connect(user1) + .preLaunch( + tokenName, + tokenTicker, + cores, + description, + image, + urls, + purchaseAmount, + startTime + ); + + const receipt = await tx.wait(); + const event = receipt.logs.find((log) => { + try { + const parsed = bondingV2.interface.parseLog(log); + return parsed && parsed.name === "PreLaunched"; + } catch (e) { + return false; + } + }); + + const parsedEvent = bondingV2.interface.parseLog(event); + const regularTokenAddress = parsedEvent.args.token; + + // Verify regular token does NOT allow tax recipient updates + const allowUpdate = await bondingV2.allowTaxRecipientUpdate( + regularTokenAddress + ); + expect(allowUpdate).to.be.false; + + // Launch and graduate to get agentId + await time.increase(START_TIME_DELAY + 1); + await bondingV2.connect(user1).launch(regularTokenAddress); + await time.increase(100 * 60); + const buyAmount = ethers.parseEther("202020.2044906205"); + await virtualToken + .connect(accounts.user2) + .approve(await bondingV2.getAddress(), buyAmount); + await bondingV2 + .connect(accounts.user2) + .buy( + buyAmount, + regularTokenAddress, + 0, + (await time.latest()) + 300 + ); + + // Find agentId + let regularAgentId; + const nextVirtualId = await agentNftV2.nextVirtualId(); + for (let i = 1; i < nextVirtualId; i++) { + try { + const virtualInfo = await agentNftV2.virtualInfo(i); + const tokenInfo = await bondingV2.tokenInfo(regularTokenAddress); + if (virtualInfo.token === tokenInfo.agentToken) { + regularAgentId = BigInt(i); + break; + } + } catch (e) { + continue; + } + } + + expect(regularAgentId).to.not.be.undefined; + + // Try to update tax recipient - should revert + const newTba = ethers.Wallet.createRandom().address; + const newCreator = ethers.Wallet.createRandom().address; + + await expect( + agentTax + .connect(admin) + .updateCreatorForProject60daysAgents( + regularAgentId, + newTba, + newCreator + ) + ).to.be.revertedWith("Token does not allow tax recipient updates"); + }); + + it("Should revert if called without EXECUTOR_V2_ROLE", async function () { + const { user1 } = accounts; + const newTba = ethers.Wallet.createRandom().address; + const newCreator = ethers.Wallet.createRandom().address; + + await expect( + agentTax + .connect(user1) + .updateCreatorForProject60daysAgents(agentId, newTba, newCreator) + ).to.be.revertedWithCustomError( + agentTax, + "AccessControlUnauthorizedAccount" + ); + }); + + it("Should revert if BondingV2 is not set", async function () { + const { admin } = accounts; + + // Create a new AgentTax without setting BondingV2 + const AgentTax = await ethers.getContractFactory("AgentTax"); + const newAgentTax = await upgrades.deployProxy( + AgentTax, + [ + accounts.owner.address, + await virtualToken.getAddress(), + await virtualToken.getAddress(), + addresses.fRouterV2, + accounts.owner.address, + ethers.parseEther("100"), + ethers.parseEther("10000"), + await agentNftV2.getAddress(), + ], + { initializer: "initialize" } + ); + await newAgentTax.waitForDeployment(); + + const EXECUTOR_V2_ROLE = await newAgentTax.EXECUTOR_V2_ROLE(); + await newAgentTax.grantRole(EXECUTOR_V2_ROLE, admin.address); + + const newTba = ethers.Wallet.createRandom().address; + const newCreator = ethers.Wallet.createRandom().address; + + await expect( + newAgentTax + .connect(admin) + .updateCreatorForProject60daysAgents(agentId, newTba, newCreator) + ).to.be.revertedWith("BondingV2 not set"); + }); + + it("Should revert with invalid TBA or creator address", async function () { + const { admin } = accounts; + + await expect( + agentTax + .connect(admin) + .updateCreatorForProject60daysAgents( + agentId, + ethers.ZeroAddress, + accounts.user1.address + ) + ).to.be.revertedWith("Invalid TBA"); + + await expect( + agentTax + .connect(admin) + .updateCreatorForProject60daysAgents( + agentId, + accounts.user1.address, + ethers.ZeroAddress + ) + ).to.be.revertedWith("Invalid creator"); + }); + }); + + describe("Regression Tests - preLaunch backward compatibility", function () { + it("Should create token with allowTaxRecipientUpdate set to false (backward compatible)", async function () { + const { user1 } = accounts; + + const tokenName = "Regular Token"; + const tokenTicker = "REG"; + const cores = [0, 1, 2]; + const description = "Regular token description"; + const image = "https://example.com/image.png"; + const urls = [ + "https://twitter.com/test", + "https://t.me/test", + "https://youtube.com/test", + "https://example.com", + ]; + const purchaseAmount = ethers.parseEther("1000"); + const startTime = (await time.latest()) + START_TIME_DELAY + 1; + + await virtualToken + .connect(user1) + .approve(await bondingV2.getAddress(), purchaseAmount); + + const tx = await bondingV2 + .connect(user1) + .preLaunch( + tokenName, + tokenTicker, + cores, + description, + image, + urls, + purchaseAmount, + startTime + ); + + const receipt = await tx.wait(); + const event = receipt.logs.find((log) => { + try { + const parsed = bondingV2.interface.parseLog(log); + return parsed && parsed.name === "PreLaunched"; + } catch (e) { + return false; + } + }); + + expect(event).to.not.be.undefined; + const parsedEvent = bondingV2.interface.parseLog(event); + const tokenAddress = parsedEvent.args.token; + + // Verify allowTaxRecipientUpdate is set to false (backward compatible) + const allowUpdate = await bondingV2.allowTaxRecipientUpdate(tokenAddress); + expect(allowUpdate).to.be.false; + + // Verify token info + const tokenInfo = await bondingV2.tokenInfo(tokenAddress); + expect(tokenInfo.token).to.equal(tokenAddress); + expect(tokenInfo.creator).to.equal(user1.address); + }); + + it("Should maintain same function signature for preLaunch", async function () { + // Verify that preLaunch function signature hasn't changed + const preLaunchFragment = bondingV2.interface.getFunction("preLaunch"); + expect(preLaunchFragment.inputs.length).to.equal(8); // 8 parameters + expect(preLaunchFragment.inputs[0].name).to.equal("_name"); + expect(preLaunchFragment.inputs[7].name).to.equal("startTime"); + // Should NOT have allowTaxRecipientUpdate_ parameter + expect( + preLaunchFragment.inputs.find( + (input) => input.name === "allowTaxRecipientUpdate_" + ) + ).to.be.undefined; + }); + + it("Should allow both preLaunch and preLaunchProject60days to coexist", async function () { + const { user1 } = accounts; + + const purchaseAmount = ethers.parseEther("1000"); + const startTime = (await time.latest()) + START_TIME_DELAY + 1; + + await virtualToken + .connect(user1) + .approve(await bondingV2.getAddress(), purchaseAmount * 2n); + + // Create regular token + const tx1 = await bondingV2 + .connect(user1) + .preLaunch( + "Regular Token", + "REG", + [0, 1], + "Description", + "https://example.com/image.png", + ["", "", "", ""], + purchaseAmount, + startTime + ); + + // Create Project60days token + const tx2 = await bondingV2 + .connect(user1) + .preLaunchProject60days( + "Project60days Token", + "P60", + [0, 1], + "Description", + "https://example.com/image.png", + ["", "", "", ""], + purchaseAmount, + startTime + 1 + ); + + await expect(tx1).to.emit(bondingV2, "PreLaunched"); + await expect(tx2).to.emit(bondingV2, "PreLaunched"); + + const receipt1 = await tx1.wait(); + const receipt2 = await tx2.wait(); + + const event1 = receipt1.logs.find((log) => { + try { + const parsed = bondingV2.interface.parseLog(log); + return parsed && parsed.name === "PreLaunched"; + } catch (e) { + return false; + } + }); + + const event2 = receipt2.logs.find((log) => { + try { + const parsed = bondingV2.interface.parseLog(log); + return parsed && parsed.name === "PreLaunched"; + } catch (e) { + return false; + } + }); + + const parsedEvent1 = bondingV2.interface.parseLog(event1); + const parsedEvent2 = bondingV2.interface.parseLog(event2); + + const regularToken = parsedEvent1.args.token; + const project60daysToken = parsedEvent2.args.token; + + expect(await bondingV2.allowTaxRecipientUpdate(regularToken)).to.be.false; + expect( + await bondingV2.allowTaxRecipientUpdate(project60daysToken) + ).to.be.true; + }); + }); + + describe("setBondingV2", function () { + it("Should allow admin to set BondingV2 address", async function () { + const { owner } = accounts; + const newBondingV2Address = ethers.Wallet.createRandom().address; + + await agentTax.connect(owner).setBondingV2(newBondingV2Address); + expect(await agentTax.bondingV2()).to.equal(newBondingV2Address); + }); + + it("Should revert if non-admin tries to set BondingV2", async function () { + const { user1 } = accounts; + const newBondingV2Address = ethers.Wallet.createRandom().address; + + await expect( + agentTax.connect(user1).setBondingV2(newBondingV2Address) + ).to.be.revertedWithCustomError( + agentTax, + "AccessControlUnauthorizedAccount" + ); + }); + + it("Should revert if setting zero address", async function () { + const { owner } = accounts; + + await expect( + agentTax.connect(owner).setBondingV2(ethers.ZeroAddress) + ).to.be.revertedWith("Invalid BondingV2 address"); + }); + }); +}); From dd524de31890a0d7fa48a15847fd61f02eba85ca Mon Sep 17 00:00:00 2001 From: koo-virtuals Date: Fri, 23 Jan 2026 18:45:09 +0800 Subject: [PATCH 2/8] add project60days test cases --- test/project60days/project60days.js | 134 +++++++++++++++++++++++----- 1 file changed, 112 insertions(+), 22 deletions(-) diff --git a/test/project60days/project60days.js b/test/project60days/project60days.js index beba44d..cb47716 100644 --- a/test/project60days/project60days.js +++ b/test/project60days/project60days.js @@ -31,15 +31,31 @@ describe("Project60days - AgentTax Integration", function () { virtualToken = contracts.virtualToken; agentNftV2 = contracts.agentNftV2; + // Deploy CBBTC token (assetToken - token to swap to) + // Note: cbbtc is deployed in setup but not exposed, so we deploy it here + console.log("\n--- Deploying MockERC20 for CBBTC ---"); + const MockERC20 = await ethers.getContractFactory("MockERC20"); + const cbbtc = await MockERC20.deploy( + "CBBTC", + "CBBTC", + accounts.owner.address, + ethers.parseEther("10000000000") + ); + await cbbtc.waitForDeployment(); + const cbbtcAddress = await cbbtc.getAddress(); + console.log("CBBTC deployed at:", cbbtcAddress); + // Deploy AgentTax contract + // assetToken is cbbtc (the token to swap to) + // taxToken is virtualToken (the token collected as tax) console.log("\n--- Deploying AgentTax ---"); const AgentTax = await ethers.getContractFactory("AgentTax"); agentTax = await upgrades.deployProxy( AgentTax, [ accounts.owner.address, // defaultAdmin_ - await virtualToken.getAddress(), // assetToken_ - await virtualToken.getAddress(), // taxToken_ + cbbtcAddress, // assetToken_ (CBBTC - token to swap to) + await virtualToken.getAddress(), // taxToken_ (Virtual Token - tax collected) addresses.fRouterV2, // router_ accounts.owner.address, // treasury_ ethers.parseEther("100"), // minSwapThreshold_ @@ -77,9 +93,27 @@ describe("Project60days - AgentTax Integration", function () { "https://example.com", ]; const purchaseAmount = ethers.parseEther("1000"); - const startTime = (await time.latest()) + START_TIME_DELAY + 1; - - // Approve virtual tokens + + // Verify cores array is not empty (required by _preLaunch) + expect(cores.length).to.be.greaterThan(0); + + // Check fee and ensure purchaseAmount is sufficient + const fee = await bondingV2.fee(); + expect(purchaseAmount).to.be.greaterThanOrEqual(fee); + + // Get launchParams.startTimeDelay from contract (not constant) + const launchParams = await bondingV2.launchParams(); + const startTimeDelay = BigInt(launchParams.startTimeDelay.toString()); + // Add a larger buffer to account for block.timestamp potentially being ahead of time.latest() + // Contract requires: startTime >= block.timestamp + launchParams.startTimeDelay + const currentTime = BigInt((await time.latest()).toString()); + const startTime = currentTime + startTimeDelay + 100n; + + // Check user balance + const userBalance = await virtualToken.balanceOf(user1.address); + expect(userBalance).to.be.greaterThanOrEqual(purchaseAmount); + + // Approve virtual tokens (assetToken is virtualToken from router) await virtualToken .connect(user1) .approve(await bondingV2.getAddress(), purchaseAmount); @@ -137,7 +171,11 @@ describe("Project60days - AgentTax Integration", function () { "https://example2.com", ]; const purchaseAmount = ethers.parseEther("2000"); - const startTime = (await time.latest()) + START_TIME_DELAY + 1; + const launchParams = await bondingV2.launchParams(); + const startTimeDelay = BigInt(launchParams.startTimeDelay.toString()); + // Add a larger buffer to account for block.timestamp potentially being ahead of time.latest() + const currentTime = BigInt((await time.latest()).toString()); + const startTime = currentTime + startTimeDelay + 100n; await virtualToken .connect(user1) @@ -187,7 +225,11 @@ describe("Project60days - AgentTax Integration", function () { "https://example.com", ]; const purchaseAmount = ethers.parseEther("1000"); - const startTime = (await time.latest()) + START_TIME_DELAY + 1; + const launchParamsData = await bondingV2.launchParams(); + const startTimeDelay = BigInt(launchParamsData.startTimeDelay.toString()); + // Add a larger buffer to account for block.timestamp potentially being ahead of time.latest() + const currentTime = BigInt((await time.latest()).toString()); + const startTime = currentTime + startTimeDelay + 100n; await virtualToken .connect(user1) @@ -220,15 +262,24 @@ describe("Project60days - AgentTax Integration", function () { tokenAddress = parsedEvent.args.token; // Launch and graduate token to get agentId - await time.increase(START_TIME_DELAY + 1); + // Need to wait until pair.startTime() has passed + const pair = await ethers.getContractAt("FPairV2", parsedEvent.args.pair); + const pairStartTime = await pair.startTime(); + const currentTimeForLaunch = await time.latest(); + if (currentTimeForLaunch < pairStartTime) { + const waitTime = BigInt(pairStartTime.toString()) - BigInt(currentTimeForLaunch.toString()) + 1n; + await time.increase(waitTime); + } await bondingV2.connect(user1).launch(tokenAddress); // Buy tokens to graduate await time.increase(100 * 60); // Wait for anti-sniper tax to expire const buyAmount = ethers.parseEther("202020.2044906205"); + // Need to approve fRouterV2, not bondingV2, because buy() calls router.buy() + const fRouterV2Address = addresses.fRouterV2; await virtualToken .connect(accounts.user2) - .approve(await bondingV2.getAddress(), buyAmount); + .approve(fRouterV2Address, buyAmount); await bondingV2 .connect(accounts.user2) .buy( @@ -244,6 +295,8 @@ describe("Project60days - AgentTax Integration", function () { try { const virtualInfo = await agentNftV2.virtualInfo(i); const tokenInfo = await bondingV2.tokenInfo(tokenAddress); + // console.log("virtualInfo", virtualInfo); + // console.log("tokenInfo", tokenInfo); if (virtualInfo.token === tokenInfo.agentToken) { agentId = BigInt(i); break; @@ -270,13 +323,19 @@ describe("Project60days - AgentTax Integration", function () { .connect(admin) .updateCreatorForProject60daysAgents(agentId, newTba, newCreator); + // Get old creator before update (if exists) + let oldCreator = ethers.ZeroAddress; + try { + const taxRecipient = await agentTax._agentRecipients(agentId); + oldCreator = taxRecipient.creator; + } catch (e) { + // If recipient doesn't exist yet, oldCreator remains ZeroAddress + } + await expect(tx) .to.emit(agentTax, "CreatorUpdated") - .withArgs( - agentId, - (oldCreator) => oldCreator !== ethers.ZeroAddress, - newCreator - ); + .withArgs(agentId, oldCreator, newCreator); + console.log("Creator updated for agent", agentId, "from", oldCreator, "to", newCreator); }); it("Should revert if token does not allow tax recipient updates", async function () { @@ -295,7 +354,11 @@ describe("Project60days - AgentTax Integration", function () { "https://example.com", ]; const purchaseAmount = ethers.parseEther("1000"); - const startTime = (await time.latest()) + START_TIME_DELAY + 1; + const launchParamsData2 = await bondingV2.launchParams(); + const startTimeDelayForToken2 = BigInt(launchParamsData2.startTimeDelay.toString()); + // Add a larger buffer to account for block.timestamp potentially being ahead of time.latest() + const currentTime = BigInt((await time.latest()).toString()); + const startTime = currentTime + startTimeDelayForToken2 + 100n; await virtualToken .connect(user1) @@ -334,13 +397,21 @@ describe("Project60days - AgentTax Integration", function () { expect(allowUpdate).to.be.false; // Launch and graduate to get agentId - await time.increase(START_TIME_DELAY + 1); + // Need to wait until pair.startTime() has passed + const regularPair = await ethers.getContractAt("FPairV2", parsedEvent.args.pair); + const regularPairStartTime = await regularPair.startTime(); + const currentTimeForRegularLaunch = await time.latest(); + if (currentTimeForRegularLaunch < regularPairStartTime) { + const waitTime = BigInt(regularPairStartTime.toString()) - BigInt(currentTimeForRegularLaunch.toString()) + 1n; + await time.increase(waitTime); + } await bondingV2.connect(user1).launch(regularTokenAddress); await time.increase(100 * 60); const buyAmount = ethers.parseEther("202020.2044906205"); + // Need to approve fRouterV2, not bondingV2, because buy() calls router.buy() await virtualToken .connect(accounts.user2) - .approve(await bondingV2.getAddress(), buyAmount); + .approve(addresses.fRouterV2, buyAmount); await bondingV2 .connect(accounts.user2) .buy( @@ -367,6 +438,7 @@ describe("Project60days - AgentTax Integration", function () { } expect(regularAgentId).to.not.be.undefined; + console.log("regularAgentId", regularAgentId); // Try to update tax recipient - should revert const newTba = ethers.Wallet.createRandom().address; @@ -402,13 +474,23 @@ describe("Project60days - AgentTax Integration", function () { const { admin } = accounts; // Create a new AgentTax without setting BondingV2 + // Need to use different tokens for assetToken and taxToken (contract requirement) + const MockERC20 = await ethers.getContractFactory("MockERC20"); + const testAssetToken = await MockERC20.deploy( + "Test Asset", + "TASSET", + accounts.owner.address, + ethers.parseEther("10000000000") + ); + await testAssetToken.waitForDeployment(); + const AgentTax = await ethers.getContractFactory("AgentTax"); const newAgentTax = await upgrades.deployProxy( AgentTax, [ accounts.owner.address, - await virtualToken.getAddress(), - await virtualToken.getAddress(), + await testAssetToken.getAddress(), // assetToken_ (different from taxToken) + await virtualToken.getAddress(), // taxToken_ addresses.fRouterV2, accounts.owner.address, ethers.parseEther("100"), @@ -473,7 +555,11 @@ describe("Project60days - AgentTax Integration", function () { "https://example.com", ]; const purchaseAmount = ethers.parseEther("1000"); - const startTime = (await time.latest()) + START_TIME_DELAY + 1; + const launchParamsData = await bondingV2.launchParams(); + const startTimeDelay = BigInt(launchParamsData.startTimeDelay.toString()); + // Add a larger buffer to account for block.timestamp potentially being ahead of time.latest() + const currentTime = BigInt((await time.latest()).toString()); + const startTime = currentTime + startTimeDelay + 100n; await virtualToken .connect(user1) @@ -534,7 +620,11 @@ describe("Project60days - AgentTax Integration", function () { const { user1 } = accounts; const purchaseAmount = ethers.parseEther("1000"); - const startTime = (await time.latest()) + START_TIME_DELAY + 1; + const launchParamsData = await bondingV2.launchParams(); + const startTimeDelay = BigInt(launchParamsData.startTimeDelay.toString()); + // Add a larger buffer to account for block.timestamp potentially being ahead of time.latest() + const currentTime = BigInt((await time.latest()).toString()); + const startTime = currentTime + startTimeDelay + 100n; await virtualToken .connect(user1) @@ -565,7 +655,7 @@ describe("Project60days - AgentTax Integration", function () { "https://example.com/image.png", ["", "", "", ""], purchaseAmount, - startTime + 1 + startTime + 1n ); await expect(tx1).to.emit(bondingV2, "PreLaunched"); From 3497865f6b6c66b5d160650576b820ed68f1d587 Mon Sep 17 00:00:00 2001 From: koo-virtuals Date: Sat, 24 Jan 2026 11:12:02 +0800 Subject: [PATCH 3/8] update var position --- contracts/tax/AgentTax.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/tax/AgentTax.sol b/contracts/tax/AgentTax.sol index 77d1bb8..5e6fa60 100644 --- a/contracts/tax/AgentTax.sol +++ b/contracts/tax/AgentTax.sol @@ -38,7 +38,6 @@ contract AgentTax is Initializable, AccessControlUpgradeable { uint256 public minSwapThreshold; uint256 public maxSwapThreshold; IAgentNft public agentNft; - BondingV2 public bondingV2; event SwapThresholdUpdated( uint256 oldMinThreshold, @@ -86,6 +85,7 @@ contract AgentTax is Initializable, AccessControlUpgradeable { ); ITBABonus public tbaBonus; + BondingV2 public bondingV2; /// @custom:oz-upgrades-unsafe-allow constructor constructor() { From 8821b44d54c27c4c61eaee9b62b5eabecf6e2296 Mon Sep 17 00:00:00 2001 From: koo-virtuals Date: Sat, 24 Jan 2026 12:03:01 +0800 Subject: [PATCH 4/8] update var name to isProject60days --- contracts/launchpadv2/BondingV2.sol | 35 +++++++++++++++-------------- contracts/tax/AgentTax.sol | 6 ++--- test/project60days/project60days.js | 32 +++++++++++++------------- 3 files changed, 37 insertions(+), 36 deletions(-) diff --git a/contracts/launchpadv2/BondingV2.sol b/contracts/launchpadv2/BondingV2.sol index d378700..82bfd2d 100644 --- a/contracts/launchpadv2/BondingV2.sol +++ b/contracts/launchpadv2/BondingV2.sol @@ -94,8 +94,8 @@ contract BondingV2 is // this is for BE to separate with old virtualId from bondingV1, but this field is not used yet uint256 public constant VirtualIdBase = 20_000_000_000; - // Mapping to mark tokens that allow tax recipient updates - mapping(address => bool) public allowTaxRecipientUpdate; + // Mapping to mark Project60days tokens (affects tax recipient updates and liquidity drain permissions) + mapping(address => bool) public isProject60days; event PreLaunched( address indexed token, @@ -215,7 +215,7 @@ contract BondingV2 is urls, purchaseAmount, startTime, - false // allowTaxRecipientUpdate_ defaults to false for backward compatibility + false // isProject60days_ defaults to false for backward compatibility ); } @@ -239,7 +239,7 @@ contract BondingV2 is urls, purchaseAmount, startTime, - true // allowTaxRecipientUpdate_ defaults to true for Project60days + true // isProject60days_ defaults to true for Project60days ); } @@ -252,7 +252,7 @@ contract BondingV2 is string[4] memory urls, uint256 purchaseAmount, uint256 startTime, - bool allowTaxRecipientUpdate_ + bool isProject60days_ ) internal returns (address, address, uint, uint256) { if (purchaseAmount < fee || cores.length <= 0) { revert InvalidInput(); @@ -276,15 +276,16 @@ contract BondingV2 is .createNewAgentTokenAndApplication( _name, // without "fun " prefix _ticker, - abi.encode( // tokenSupplyParams - initialSupply, - 0, // lpSupply, will mint to agentTokenAddress - initialSupply, // vaultSupply, will mint to vault - initialSupply, - initialSupply, - 0, - address(this) // vault, is the bonding contract itself - ), + abi.encode( + // tokenSupplyParams + initialSupply, + 0, // lpSupply, will mint to agentTokenAddress + initialSupply, // vaultSupply, will mint to vault + initialSupply, + initialSupply, + 0, + address(this) // vault, is the bonding contract itself + ), cores, _deployParams.tbaSalt, _deployParams.tbaImplementation, @@ -344,9 +345,9 @@ contract BondingV2 is newToken.applicationId = applicationId; newToken.initialPurchase = initialPurchase; newToken.virtualId = VirtualIdBase + tokenInfos.length; - // Mark token if it allows tax recipient updates - if (allowTaxRecipientUpdate_) { - allowTaxRecipientUpdate[token] = true; + // Mark token as Project60days token (affects tax recipient updates and liquidity drain permissions) + if (isProject60days_) { + isProject60days[token] = true; } newToken.launchExecuted = false; diff --git a/contracts/tax/AgentTax.sol b/contracts/tax/AgentTax.sol index 5e6fa60..7cb8393 100644 --- a/contracts/tax/AgentTax.sol +++ b/contracts/tax/AgentTax.sol @@ -371,10 +371,10 @@ contract AgentTax is Initializable, AccessControlUpgradeable { address token = info.token; require(token != address(0), "Token not found"); - // Check if this token allows tax recipient updates + // Check if this is a Project60days token require( - bondingV2.allowTaxRecipientUpdate(token), - "Token does not allow tax recipient updates" + bondingV2.isProject60days(token), + "Token is not a Project60days token" ); require(tba != address(0), "Invalid TBA"); diff --git a/test/project60days/project60days.js b/test/project60days/project60days.js index cb47716..b1fa143 100644 --- a/test/project60days/project60days.js +++ b/test/project60days/project60days.js @@ -78,7 +78,7 @@ describe("Project60days - AgentTax Integration", function () { }); describe("preLaunchProject60days", function () { - it("Should create a token with allowTaxRecipientUpdate set to true", async function () { + it("Should create a token with isProject60days set to true", async function () { const { user1 } = accounts; const tokenName = "Project60days Token"; @@ -146,9 +146,9 @@ describe("Project60days - AgentTax Integration", function () { const parsedEvent = bondingV2.interface.parseLog(event); const tokenAddress = parsedEvent.args.token; - // Verify allowTaxRecipientUpdate is set to true - const allowUpdate = await bondingV2.allowTaxRecipientUpdate(tokenAddress); - expect(allowUpdate).to.be.true; + // Verify isProject60days is set to true + const isProject60days = await bondingV2.isProject60days(tokenAddress); + expect(isProject60days).to.be.true; // Verify token info const tokenInfo = await bondingV2.tokenInfo(tokenAddress); @@ -315,8 +315,8 @@ describe("Project60days - AgentTax Integration", function () { const newCreator = ethers.Wallet.createRandom().address; // Verify token allows tax recipient updates - const allowUpdate = await bondingV2.allowTaxRecipientUpdate(tokenAddress); - expect(allowUpdate).to.be.true; + const isProject60days = await bondingV2.isProject60days(tokenAddress); + expect(isProject60days).to.be.true; // Update tax recipient const tx = await agentTax @@ -391,10 +391,10 @@ describe("Project60days - AgentTax Integration", function () { const regularTokenAddress = parsedEvent.args.token; // Verify regular token does NOT allow tax recipient updates - const allowUpdate = await bondingV2.allowTaxRecipientUpdate( + const isProject60days = await bondingV2.isProject60days( regularTokenAddress ); - expect(allowUpdate).to.be.false; + expect(isProject60days).to.be.false; // Launch and graduate to get agentId // Need to wait until pair.startTime() has passed @@ -452,7 +452,7 @@ describe("Project60days - AgentTax Integration", function () { newTba, newCreator ) - ).to.be.revertedWith("Token does not allow tax recipient updates"); + ).to.be.revertedWith("Token is not a Project60days token"); }); it("Should revert if called without EXECUTOR_V2_ROLE", async function () { @@ -540,7 +540,7 @@ describe("Project60days - AgentTax Integration", function () { }); describe("Regression Tests - preLaunch backward compatibility", function () { - it("Should create token with allowTaxRecipientUpdate set to false (backward compatible)", async function () { + it("Should create token with isProject60days set to false (backward compatible)", async function () { const { user1 } = accounts; const tokenName = "Regular Token"; @@ -592,9 +592,9 @@ describe("Project60days - AgentTax Integration", function () { const parsedEvent = bondingV2.interface.parseLog(event); const tokenAddress = parsedEvent.args.token; - // Verify allowTaxRecipientUpdate is set to false (backward compatible) - const allowUpdate = await bondingV2.allowTaxRecipientUpdate(tokenAddress); - expect(allowUpdate).to.be.false; + // Verify isProject60days is set to false (backward compatible) + const isProject60days = await bondingV2.isProject60days(tokenAddress); + expect(isProject60days).to.be.false; // Verify token info const tokenInfo = await bondingV2.tokenInfo(tokenAddress); @@ -611,7 +611,7 @@ describe("Project60days - AgentTax Integration", function () { // Should NOT have allowTaxRecipientUpdate_ parameter expect( preLaunchFragment.inputs.find( - (input) => input.name === "allowTaxRecipientUpdate_" + (input) => input.name === "isProject60days_" ) ).to.be.undefined; }); @@ -688,9 +688,9 @@ describe("Project60days - AgentTax Integration", function () { const regularToken = parsedEvent1.args.token; const project60daysToken = parsedEvent2.args.token; - expect(await bondingV2.allowTaxRecipientUpdate(regularToken)).to.be.false; + expect(await bondingV2.isProject60days(regularToken)).to.be.false; expect( - await bondingV2.allowTaxRecipientUpdate(project60daysToken) + await bondingV2.isProject60days(project60daysToken) ).to.be.true; }); }); From 74afc1d80244717d56b29cc8a69d4bc248923608 Mon Sep 17 00:00:00 2001 From: koo-virtuals Date: Mon, 26 Jan 2026 12:09:52 +0800 Subject: [PATCH 5/8] support project60daysLaunchFee for bondingV2 --- contracts/launchpadv2/BondingV2.sol | 14 +- test/project60days/project60days.js | 238 ++++++++++++++++++++++++++++ 2 files changed, 249 insertions(+), 3 deletions(-) diff --git a/contracts/launchpadv2/BondingV2.sol b/contracts/launchpadv2/BondingV2.sol index 82bfd2d..36f8e4f 100644 --- a/contracts/launchpadv2/BondingV2.sol +++ b/contracts/launchpadv2/BondingV2.sol @@ -96,6 +96,7 @@ contract BondingV2 is // Mapping to mark Project60days tokens (affects tax recipient updates and liquidity drain permissions) mapping(address => bool) public isProject60days; + uint256 public project60daysLaunchFee; event PreLaunched( address indexed token, @@ -187,6 +188,12 @@ contract BondingV2 is _feeTo = newFeeTo; } + function setProject60daysLaunchFee( + uint256 newProject60daysLaunchFee + ) public onlyOwner { + project60daysLaunchFee = newProject60daysLaunchFee; + } + function setDeployParams(DeployParams memory params) public onlyOwner { _deployParams = params; } @@ -254,7 +261,8 @@ contract BondingV2 is uint256 startTime, bool isProject60days_ ) internal returns (address, address, uint, uint256) { - if (purchaseAmount < fee || cores.length <= 0) { + uint256 launchFee = isProject60days_ ? project60daysLaunchFee : fee; + if (purchaseAmount < launchFee || cores.length <= 0) { revert InvalidInput(); } // startTime must be at least startTimeDelay in the future @@ -264,8 +272,8 @@ contract BondingV2 is address assetToken = router.assetToken(); - uint256 initialPurchase = (purchaseAmount - fee); - IERC20(assetToken).safeTransferFrom(msg.sender, _feeTo, fee); + uint256 initialPurchase = (purchaseAmount - launchFee); + IERC20(assetToken).safeTransferFrom(msg.sender, _feeTo, launchFee); IERC20(assetToken).safeTransferFrom( msg.sender, address(this), diff --git a/test/project60days/project60days.js b/test/project60days/project60days.js index b1fa143..b61f39f 100644 --- a/test/project60days/project60days.js +++ b/test/project60days/project60days.js @@ -695,6 +695,244 @@ describe("Project60days - AgentTax Integration", function () { }); }); + describe("Project60days Launch Fee", function () { + it("Should use project60daysLaunchFee for Project60days tokens", async function () { + const { owner, user1 } = accounts; + + // Set a different fee for Project60days + const project60daysFee = ethers.parseEther("2000"); // 2000 tokens + await bondingV2.connect(owner).setProject60daysLaunchFee(project60daysFee); + + // Verify fee is set + const setFee = await bondingV2.project60daysLaunchFee(); + expect(setFee).to.equal(project60daysFee); + + // Get regular fee for comparison + const regularFee = await bondingV2.fee(); + expect(regularFee).to.not.equal(project60daysFee); + + const tokenName = "Project60days Fee Test"; + const tokenTicker = "P60F"; + const cores = [0, 1, 2]; + const description = "Test fee"; + const image = "https://example.com/image.png"; + const urls = ["", "", "", ""]; + const purchaseAmount = ethers.parseEther("5000"); // Enough to cover both fees + + // Get launchParams.startTimeDelay from contract + const launchParams = await bondingV2.launchParams(); + const startTimeDelay = BigInt(launchParams.startTimeDelay.toString()); + const currentTime = BigInt((await time.latest()).toString()); + const startTime = currentTime + startTimeDelay + 100n; + + // Check feeTo balance before + const feeTo = await bondingV2.owner(); + const feeToBalanceBefore = await virtualToken.balanceOf(feeTo); + + // Approve tokens + await virtualToken + .connect(user1) + .approve(await bondingV2.getAddress(), purchaseAmount); + + // Launch Project60days token + const tx = await bondingV2 + .connect(user1) + .preLaunchProject60days( + tokenName, + tokenTicker, + cores, + description, + image, + urls, + purchaseAmount, + startTime + ); + + await tx.wait(); + + // Verify feeTo received project60daysLaunchFee, not regular fee + const feeToBalanceAfter = await virtualToken.balanceOf(feeTo); + const feeReceived = feeToBalanceAfter - feeToBalanceBefore; + expect(feeReceived).to.equal(project60daysFee); + expect(feeReceived).to.not.equal(regularFee); + }); + + it("Should use regular fee for non-Project60days tokens", async function () { + const { owner, user1 } = accounts; + + // Set project60daysLaunchFee + const project60daysFee = ethers.parseEther("2000"); + await bondingV2.connect(owner).setProject60daysLaunchFee(project60daysFee); + + // Get regular fee + const regularFee = await bondingV2.fee(); + + const tokenName = "Regular Token Fee Test"; + const tokenTicker = "REG"; + const cores = [0, 1, 2]; + const description = "Test fee"; + const image = "https://example.com/image.png"; + const urls = ["", "", "", ""]; + const purchaseAmount = ethers.parseEther("5000"); + + // Get launchParams.startTimeDelay from contract + const launchParams = await bondingV2.launchParams(); + const startTimeDelay = BigInt(launchParams.startTimeDelay.toString()); + const currentTime = BigInt((await time.latest()).toString()); + const startTime = currentTime + startTimeDelay + 100n; + + // Check feeTo balance before + const feeTo = await bondingV2.owner(); + const feeToBalanceBefore = await virtualToken.balanceOf(feeTo); + + // Approve tokens + await virtualToken + .connect(user1) + .approve(await bondingV2.getAddress(), purchaseAmount); + + // Launch regular token + const tx = await bondingV2 + .connect(user1) + .preLaunch( + tokenName, + tokenTicker, + cores, + description, + image, + urls, + purchaseAmount, + startTime + ); + + await tx.wait(); + + // Verify feeTo received regular fee, not project60daysLaunchFee + const feeToBalanceAfter = await virtualToken.balanceOf(feeTo); + const feeReceived = feeToBalanceAfter - feeToBalanceBefore; + expect(feeReceived).to.equal(regularFee); + expect(feeReceived).to.not.equal(project60daysFee); + }); + + it("Should revert if purchaseAmount is less than project60daysLaunchFee", async function () { + const { owner, user1 } = accounts; + + // Set a high fee for Project60days + const project60daysFee = ethers.parseEther("5000"); + await bondingV2.connect(owner).setProject60daysLaunchFee(project60daysFee); + + const tokenName = "Project60days Insufficient Fee"; + const tokenTicker = "P60IF"; + const cores = [0, 1, 2]; + const description = "Test"; + const image = "https://example.com/image.png"; + const urls = ["", "", "", ""]; + const purchaseAmount = ethers.parseEther("1000"); // Less than fee + + // Get launchParams.startTimeDelay from contract + const launchParams = await bondingV2.launchParams(); + const startTimeDelay = BigInt(launchParams.startTimeDelay.toString()); + const currentTime = BigInt((await time.latest()).toString()); + const startTime = currentTime + startTimeDelay + 100n; + + // Approve tokens + await virtualToken + .connect(user1) + .approve(await bondingV2.getAddress(), purchaseAmount); + + // Should revert with InvalidInput + await expect( + bondingV2 + .connect(user1) + .preLaunchProject60days( + tokenName, + tokenTicker, + cores, + description, + image, + urls, + purchaseAmount, + startTime + ) + ).to.be.revertedWithCustomError(bondingV2, "InvalidInput"); + }); + + it("Should allow owner to set project60daysLaunchFee", async function () { + const { owner } = accounts; + + const newFee = ethers.parseEther("3000"); + await bondingV2.connect(owner).setProject60daysLaunchFee(newFee); + + const setFee = await bondingV2.project60daysLaunchFee(); + expect(setFee).to.equal(newFee); + }); + + it("Should revert if non-owner tries to set project60daysLaunchFee", async function () { + const { user1 } = accounts; + + const newFee = ethers.parseEther("3000"); + await expect( + bondingV2.connect(user1).setProject60daysLaunchFee(newFee) + ).to.be.revertedWithCustomError( + bondingV2, + "OwnableUnauthorizedAccount" + ); + }); + + it("Should handle zero project60daysLaunchFee (allows free launch)", async function () { + const { owner, user1 } = accounts; + + // Set fee to 0 + await bondingV2.connect(owner).setProject60daysLaunchFee(0); + + const tokenName = "Free Project60days"; + const tokenTicker = "FREE"; + const cores = [0, 1, 2]; + const description = "Test"; + const image = "https://example.com/image.png"; + const urls = ["", "", "", ""]; + const purchaseAmount = ethers.parseEther("100"); // Small amount, but >= 0 + + // Get launchParams.startTimeDelay from contract + const launchParams = await bondingV2.launchParams(); + const startTimeDelay = BigInt(launchParams.startTimeDelay.toString()); + const currentTime = BigInt((await time.latest()).toString()); + const startTime = currentTime + startTimeDelay + 100n; + + // Approve tokens + await virtualToken + .connect(user1) + .approve(await bondingV2.getAddress(), purchaseAmount); + + // Should succeed with zero fee + const tx = await bondingV2 + .connect(user1) + .preLaunchProject60days( + tokenName, + tokenTicker, + cores, + description, + image, + urls, + purchaseAmount, + startTime + ); + + await tx.wait(); + + // Verify token was created + const receipt = await tx.wait(); + const event = receipt.logs.find((log) => { + try { + const parsed = bondingV2.interface.parseLog(log); + return parsed && parsed.name === "PreLaunched"; + } catch (e) { + return false; + } + }); + expect(event).to.not.be.undefined; + }); + }); + describe("setBondingV2", function () { it("Should allow admin to set BondingV2 address", async function () { const { owner } = accounts; From 4d4f011cfcfbd7f239f3796e6d97722c31d45df7 Mon Sep 17 00:00:00 2001 From: Weixiong Tay Date: Mon, 26 Jan 2026 20:30:53 +0800 Subject: [PATCH 6/8] meta: add base-sepolia.json --- .openzeppelin/base-sepolia.json | 893 +++++++++++++++++++++++++++++++- 1 file changed, 872 insertions(+), 21 deletions(-) diff --git a/.openzeppelin/base-sepolia.json b/.openzeppelin/base-sepolia.json index 04aba5a..245f9a3 100644 --- a/.openzeppelin/base-sepolia.json +++ b/.openzeppelin/base-sepolia.json @@ -210,11 +210,6 @@ "txHash": "0x60595c62362e4ca94cfda55cef56c0cdb35b0cae6624269f757838962a54a1a7", "kind": "transparent" }, - { - "address": "0xb06E76aaA4d81Eda444DC28C564D5B8B2aD3F530", - "txHash": "0x6bfad44a29e6c631c2432fbaff13a296c99672ebc5eaada2f93b181a721b37ff", - "kind": "transparent" - }, { "address": "0x21A5E9424B3de7600d1EfC13122dd6833ef7fC96", "txHash": "0x8b6f9e4a20e029beb51f9219c096b6030a3de56ef855ac97e3ddb2860cca8425", @@ -732,6 +727,15 @@ "address": "0x219a009f0eEc7953ee74347ba7820fF8Ce3AF369", "txHash": "0x18ef0341889d4d200bd775b895f9bfbda0649aaf9b744da8055e0c7b07f034eb", "kind": "transparent" + }, + { + "address": "0x368C508d0D1de43B25b00775CC47a64f5aA39B09", + "txHash": "0x97fd1d1284d427b2cd700cfdd78a91e3c0a4860b5fddba1dacd8e1de9fa95318", + "kind": "transparent" + }, + { + "address": "0xb06E76aaA4d81Eda444DC28C564D5B8B2aD3F530", + "kind": "transparent" } ], "impls": { @@ -39728,7 +39732,7 @@ "label": "factory", "offset": 0, "slot": "1", - "type": "t_contract(FFactoryV2)15472", + "type": "t_contract(FFactoryV2)8399", "contract": "BondingV2", "src": "contracts/launchpadv2/BondingV2.sol:26" }, @@ -39736,7 +39740,7 @@ "label": "router", "offset": 0, "slot": "2", - "type": "t_contract(FRouterV2)16862", + "type": "t_contract(FRouterV2)9771", "contract": "BondingV2", "src": "contracts/launchpadv2/BondingV2.sol:27" }, @@ -39792,7 +39796,7 @@ "label": "_deployParams", "offset": 0, "slot": "9", - "type": "t_struct(DeployParams)10770_storage", + "type": "t_struct(DeployParams)3521_storage", "contract": "BondingV2", "src": "contracts/launchpadv2/BondingV2.sol:83" }, @@ -39800,7 +39804,7 @@ "label": "tokenInfo", "offset": 0, "slot": "12", - "type": "t_mapping(t_address,t_struct(Token)10736_storage)", + "type": "t_mapping(t_address,t_struct(Token)3487_storage)", "contract": "BondingV2", "src": "contracts/launchpadv2/BondingV2.sol:85" }, @@ -39816,7 +39820,7 @@ "label": "launchParams", "offset": 0, "slot": "14", - "type": "t_struct(LaunchParams)10788_storage", + "type": "t_struct(LaunchParams)3539_storage", "contract": "BondingV2", "src": "contracts/launchpadv2/BondingV2.sol:93" } @@ -39830,7 +39834,7 @@ "label": "bool", "numberOfBytes": "1" }, - "t_struct(InitializableStorage)498_storage": { + "t_struct(InitializableStorage)211_storage": { "label": "struct Initializable.InitializableStorage", "members": [ { @@ -39848,7 +39852,7 @@ ], "numberOfBytes": "32" }, - "t_struct(OwnableStorage)206_storage": { + "t_struct(OwnableStorage)151_storage": { "label": "struct OwnableUpgradeable.OwnableStorage", "members": [ { @@ -39860,7 +39864,7 @@ ], "numberOfBytes": "32" }, - "t_struct(ReentrancyGuardStorage)957_storage": { + "t_struct(ReentrancyGuardStorage)296_storage": { "label": "struct ReentrancyGuardUpgradeable.ReentrancyGuardStorage", "members": [ { @@ -39892,15 +39896,15 @@ "label": "bytes32", "numberOfBytes": "32" }, - "t_contract(FFactoryV2)15472": { + "t_contract(FFactoryV2)8399": { "label": "contract FFactoryV2", "numberOfBytes": "20" }, - "t_contract(FRouterV2)16862": { + "t_contract(FRouterV2)9771": { "label": "contract FRouterV2", "numberOfBytes": "20" }, - "t_mapping(t_address,t_struct(Token)10736_storage)": { + "t_mapping(t_address,t_struct(Token)3487_storage)": { "label": "mapping(address => struct BondingV2.Token)", "numberOfBytes": "32" }, @@ -39908,7 +39912,7 @@ "label": "string", "numberOfBytes": "32" }, - "t_struct(Data)10761_storage": { + "t_struct(Data)3512_storage": { "label": "struct BondingV2.Data", "members": [ { @@ -39986,7 +39990,7 @@ ], "numberOfBytes": "384" }, - "t_struct(DeployParams)10770_storage": { + "t_struct(DeployParams)3521_storage": { "label": "struct BondingV2.DeployParams", "members": [ { @@ -40016,7 +40020,7 @@ ], "numberOfBytes": "96" }, - "t_struct(LaunchParams)10788_storage": { + "t_struct(LaunchParams)3539_storage": { "label": "struct BondingV2.LaunchParams", "members": [ { @@ -40040,7 +40044,7 @@ ], "numberOfBytes": "96" }, - "t_struct(Token)10736_storage": { + "t_struct(Token)3487_storage": { "label": "struct BondingV2.Token", "members": [ { @@ -40069,7 +40073,7 @@ }, { "label": "data", - "type": "t_struct(Data)10761_storage", + "type": "t_struct(Data)3512_storage", "offset": 0, "slot": "4" }, @@ -42472,6 +42476,853 @@ ] } } + }, + "157fc4c2e85f80c9495cce9af8f1e5ec2860fd03e6ef17cd4a2c0b11c6f9bdcc": { + "address": "0x01A215d92D1242571772cD78c0fE82A7086236ab", + "txHash": "0x2ef600ddb5d8d3f8bbc03d36765aba995ce4e571251eed7bcaef4403c8dba642", + "layout": { + "solcVersion": "0.8.26", + "storage": [ + { + "label": "_feeTo", + "offset": 0, + "slot": "0", + "type": "t_address", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:24" + }, + { + "label": "factory", + "offset": 0, + "slot": "1", + "type": "t_contract(FFactoryV2)5405", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:26" + }, + { + "label": "router", + "offset": 0, + "slot": "2", + "type": "t_contract(FRouterV2)6777", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:27" + }, + { + "label": "initialSupply", + "offset": 0, + "slot": "3", + "type": "t_uint256", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:28" + }, + { + "label": "fee", + "offset": 0, + "slot": "4", + "type": "t_uint256", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:29" + }, + { + "label": "assetRate", + "offset": 0, + "slot": "5", + "type": "t_uint256", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:31" + }, + { + "label": "gradThreshold", + "offset": 0, + "slot": "6", + "type": "t_uint256", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:32" + }, + { + "label": "maxTx", + "offset": 0, + "slot": "7", + "type": "t_uint256", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:33" + }, + { + "label": "agentFactory", + "offset": 0, + "slot": "8", + "type": "t_address", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:34" + }, + { + "label": "_deployParams", + "offset": 0, + "slot": "9", + "type": "t_struct(DeployParams)3521_storage", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:83" + }, + { + "label": "tokenInfo", + "offset": 0, + "slot": "12", + "type": "t_mapping(t_address,t_struct(Token)3487_storage)", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:85" + }, + { + "label": "tokenInfos", + "offset": 0, + "slot": "13", + "type": "t_array(t_address)dyn_storage", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:86" + }, + { + "label": "launchParams", + "offset": 0, + "slot": "14", + "type": "t_struct(LaunchParams)3539_storage", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:93" + }, + { + "label": "isProject60days", + "offset": 0, + "slot": "17", + "type": "t_mapping(t_address,t_bool)", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:98" + }, + { + "label": "project60daysLaunchFee", + "offset": 0, + "slot": "18", + "type": "t_uint256", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:99" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_struct(InitializableStorage)211_storage": { + "label": "struct Initializable.InitializableStorage", + "members": [ + { + "label": "_initialized", + "type": "t_uint64", + "offset": 0, + "slot": "0" + }, + { + "label": "_initializing", + "type": "t_bool", + "offset": 8, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_struct(OwnableStorage)151_storage": { + "label": "struct OwnableUpgradeable.OwnableStorage", + "members": [ + { + "label": "_owner", + "type": "t_address", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_struct(ReentrancyGuardStorage)296_storage": { + "label": "struct ReentrancyGuardUpgradeable.ReentrancyGuardStorage", + "members": [ + { + "label": "_status", + "type": "t_uint256", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint64": { + "label": "uint64", + "numberOfBytes": "8" + }, + "t_array(t_address)dyn_storage": { + "label": "address[]", + "numberOfBytes": "32" + }, + "t_array(t_uint8)dyn_storage": { + "label": "uint8[]", + "numberOfBytes": "32" + }, + "t_bytes32": { + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_contract(FFactoryV2)5405": { + "label": "contract FFactoryV2", + "numberOfBytes": "20" + }, + "t_contract(FRouterV2)6777": { + "label": "contract FRouterV2", + "numberOfBytes": "20" + }, + "t_mapping(t_address,t_bool)": { + "label": "mapping(address => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_struct(Token)3487_storage)": { + "label": "mapping(address => struct BondingV2.Token)", + "numberOfBytes": "32" + }, + "t_string_storage": { + "label": "string", + "numberOfBytes": "32" + }, + "t_struct(Data)3512_storage": { + "label": "struct BondingV2.Data", + "members": [ + { + "label": "token", + "type": "t_address", + "offset": 0, + "slot": "0" + }, + { + "label": "name", + "type": "t_string_storage", + "offset": 0, + "slot": "1" + }, + { + "label": "_name", + "type": "t_string_storage", + "offset": 0, + "slot": "2" + }, + { + "label": "ticker", + "type": "t_string_storage", + "offset": 0, + "slot": "3" + }, + { + "label": "supply", + "type": "t_uint256", + "offset": 0, + "slot": "4" + }, + { + "label": "price", + "type": "t_uint256", + "offset": 0, + "slot": "5" + }, + { + "label": "marketCap", + "type": "t_uint256", + "offset": 0, + "slot": "6" + }, + { + "label": "liquidity", + "type": "t_uint256", + "offset": 0, + "slot": "7" + }, + { + "label": "volume", + "type": "t_uint256", + "offset": 0, + "slot": "8" + }, + { + "label": "volume24H", + "type": "t_uint256", + "offset": 0, + "slot": "9" + }, + { + "label": "prevPrice", + "type": "t_uint256", + "offset": 0, + "slot": "10" + }, + { + "label": "lastUpdated", + "type": "t_uint256", + "offset": 0, + "slot": "11" + } + ], + "numberOfBytes": "384" + }, + "t_struct(DeployParams)3521_storage": { + "label": "struct BondingV2.DeployParams", + "members": [ + { + "label": "tbaSalt", + "type": "t_bytes32", + "offset": 0, + "slot": "0" + }, + { + "label": "tbaImplementation", + "type": "t_address", + "offset": 0, + "slot": "1" + }, + { + "label": "daoVotingPeriod", + "type": "t_uint32", + "offset": 20, + "slot": "1" + }, + { + "label": "daoThreshold", + "type": "t_uint256", + "offset": 0, + "slot": "2" + } + ], + "numberOfBytes": "96" + }, + "t_struct(LaunchParams)3539_storage": { + "label": "struct BondingV2.LaunchParams", + "members": [ + { + "label": "startTimeDelay", + "type": "t_uint256", + "offset": 0, + "slot": "0" + }, + { + "label": "teamTokenReservedSupply", + "type": "t_uint256", + "offset": 0, + "slot": "1" + }, + { + "label": "teamTokenReservedWallet", + "type": "t_address", + "offset": 0, + "slot": "2" + } + ], + "numberOfBytes": "96" + }, + "t_struct(Token)3487_storage": { + "label": "struct BondingV2.Token", + "members": [ + { + "label": "creator", + "type": "t_address", + "offset": 0, + "slot": "0" + }, + { + "label": "token", + "type": "t_address", + "offset": 0, + "slot": "1" + }, + { + "label": "pair", + "type": "t_address", + "offset": 0, + "slot": "2" + }, + { + "label": "agentToken", + "type": "t_address", + "offset": 0, + "slot": "3" + }, + { + "label": "data", + "type": "t_struct(Data)3512_storage", + "offset": 0, + "slot": "4" + }, + { + "label": "description", + "type": "t_string_storage", + "offset": 0, + "slot": "16" + }, + { + "label": "cores", + "type": "t_array(t_uint8)dyn_storage", + "offset": 0, + "slot": "17" + }, + { + "label": "image", + "type": "t_string_storage", + "offset": 0, + "slot": "18" + }, + { + "label": "twitter", + "type": "t_string_storage", + "offset": 0, + "slot": "19" + }, + { + "label": "telegram", + "type": "t_string_storage", + "offset": 0, + "slot": "20" + }, + { + "label": "youtube", + "type": "t_string_storage", + "offset": 0, + "slot": "21" + }, + { + "label": "website", + "type": "t_string_storage", + "offset": 0, + "slot": "22" + }, + { + "label": "trading", + "type": "t_bool", + "offset": 0, + "slot": "23" + }, + { + "label": "tradingOnUniswap", + "type": "t_bool", + "offset": 1, + "slot": "23" + }, + { + "label": "applicationId", + "type": "t_uint256", + "offset": 0, + "slot": "24" + }, + { + "label": "initialPurchase", + "type": "t_uint256", + "offset": 0, + "slot": "25" + }, + { + "label": "virtualId", + "type": "t_uint256", + "offset": 0, + "slot": "26" + }, + { + "label": "launchExecuted", + "type": "t_bool", + "offset": 0, + "slot": "27" + } + ], + "numberOfBytes": "896" + }, + "t_uint32": { + "label": "uint32", + "numberOfBytes": "4" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + }, + "namespaces": { + "erc7201:openzeppelin.storage.Ownable": [ + { + "contract": "OwnableUpgradeable", + "label": "_owner", + "type": "t_address", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:24", + "offset": 0, + "slot": "0" + } + ], + "erc7201:openzeppelin.storage.ReentrancyGuard": [ + { + "contract": "ReentrancyGuardUpgradeable", + "label": "_status", + "type": "t_uint256", + "src": "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol:40", + "offset": 0, + "slot": "0" + } + ], + "erc7201:openzeppelin.storage.Initializable": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_uint64", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:69", + "offset": 0, + "slot": "0" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:73", + "offset": 8, + "slot": "0" + } + ] + } + } + }, + "935e0c57efb7e2bb17c48942b9c66dd91623fca08349018eadce3af3efe345b3": { + "address": "0xeAFAE1cd6aC95c6a3De788be4765AB6E9270E0b9", + "layout": { + "solcVersion": "0.8.26", + "storage": [ + { + "label": "assetToken", + "offset": 0, + "slot": "0", + "type": "t_address", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:33" + }, + { + "label": "taxToken", + "offset": 0, + "slot": "1", + "type": "t_address", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:34" + }, + { + "label": "router", + "offset": 0, + "slot": "2", + "type": "t_contract(IRouter)6900", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:35" + }, + { + "label": "treasury", + "offset": 0, + "slot": "3", + "type": "t_address", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:36" + }, + { + "label": "feeRate", + "offset": 20, + "slot": "3", + "type": "t_uint16", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:37" + }, + { + "label": "minSwapThreshold", + "offset": 0, + "slot": "4", + "type": "t_uint256", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:38" + }, + { + "label": "maxSwapThreshold", + "offset": 0, + "slot": "5", + "type": "t_uint256", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:39" + }, + { + "label": "agentNft", + "offset": 0, + "slot": "6", + "type": "t_contract(IAgentNft)8413", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:40" + }, + { + "label": "_agentTba", + "offset": 0, + "slot": "7", + "type": "t_mapping(t_uint256,t_address)", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:57" + }, + { + "label": "taxHistory", + "offset": 0, + "slot": "8", + "type": "t_mapping(t_bytes32,t_struct(TaxHistory)6925_storage)", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:58" + }, + { + "label": "agentTaxAmounts", + "offset": 0, + "slot": "9", + "type": "t_mapping(t_uint256,t_struct(TaxAmounts)6930_storage)", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:59" + }, + { + "label": "_agentRecipients", + "offset": 0, + "slot": "10", + "type": "t_mapping(t_uint256,t_struct(TaxRecipient)7032_storage)", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:78" + }, + { + "label": "creatorFeeRate", + "offset": 0, + "slot": "11", + "type": "t_uint16", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:79" + }, + { + "label": "tbaBonus", + "offset": 2, + "slot": "11", + "type": "t_contract(ITBABonus)8190", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:87" + }, + { + "label": "bondingV2", + "offset": 0, + "slot": "12", + "type": "t_contract(BondingV2)5080", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:88" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes32": { + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_bool)": { + "label": "mapping(address => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_struct(RoleData)24_storage)": { + "label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)", + "numberOfBytes": "32" + }, + "t_struct(AccessControlStorage)34_storage": { + "label": "struct AccessControlUpgradeable.AccessControlStorage", + "members": [ + { + "label": "_roles", + "type": "t_mapping(t_bytes32,t_struct(RoleData)24_storage)", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_struct(InitializableStorage)211_storage": { + "label": "struct Initializable.InitializableStorage", + "members": [ + { + "label": "_initialized", + "type": "t_uint64", + "offset": 0, + "slot": "0" + }, + { + "label": "_initializing", + "type": "t_bool", + "offset": 8, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_struct(RoleData)24_storage": { + "label": "struct AccessControlUpgradeable.RoleData", + "members": [ + { + "label": "hasRole", + "type": "t_mapping(t_address,t_bool)", + "offset": 0, + "slot": "0" + }, + { + "label": "adminRole", + "type": "t_bytes32", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_uint64": { + "label": "uint64", + "numberOfBytes": "8" + }, + "t_contract(BondingV2)5080": { + "label": "contract BondingV2", + "numberOfBytes": "20" + }, + "t_contract(IAgentNft)8413": { + "label": "contract IAgentNft", + "numberOfBytes": "20" + }, + "t_contract(IRouter)6900": { + "label": "contract IRouter", + "numberOfBytes": "20" + }, + "t_contract(ITBABonus)8190": { + "label": "contract ITBABonus", + "numberOfBytes": "20" + }, + "t_mapping(t_bytes32,t_struct(TaxHistory)6925_storage)": { + "label": "mapping(bytes32 => struct AgentTax.TaxHistory)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_address)": { + "label": "mapping(uint256 => address)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_struct(TaxAmounts)6930_storage)": { + "label": "mapping(uint256 => struct AgentTax.TaxAmounts)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_struct(TaxRecipient)7032_storage)": { + "label": "mapping(uint256 => struct AgentTax.TaxRecipient)", + "numberOfBytes": "32" + }, + "t_struct(TaxAmounts)6930_storage": { + "label": "struct AgentTax.TaxAmounts", + "members": [ + { + "label": "amountCollected", + "type": "t_uint256", + "offset": 0, + "slot": "0" + }, + { + "label": "amountSwapped", + "type": "t_uint256", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(TaxHistory)6925_storage": { + "label": "struct AgentTax.TaxHistory", + "members": [ + { + "label": "agentId", + "type": "t_uint256", + "offset": 0, + "slot": "0" + }, + { + "label": "amount", + "type": "t_uint256", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(TaxRecipient)7032_storage": { + "label": "struct AgentTax.TaxRecipient", + "members": [ + { + "label": "tba", + "type": "t_address", + "offset": 0, + "slot": "0" + }, + { + "label": "creator", + "type": "t_address", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_uint16": { + "label": "uint16", + "numberOfBytes": "2" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + } + }, + "namespaces": { + "erc7201:openzeppelin.storage.AccessControl": [ + { + "contract": "AccessControlUpgradeable", + "label": "_roles", + "type": "t_mapping(t_bytes32,t_struct(RoleData)24_storage)", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:61", + "offset": 0, + "slot": "0" + } + ], + "erc7201:openzeppelin.storage.Initializable": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_uint64", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:69", + "offset": 0, + "slot": "0" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:73", + "offset": 8, + "slot": "0" + } + ] + } + }, + "allAddresses": [ + "0xeAFAE1cd6aC95c6a3De788be4765AB6E9270E0b9", + "0xeB6eD8A4a4D3F658114F88f02cC992c5B875C1Dd", + "0x3dbE2DD9959A6650256d7338B5d8AbaC1ad1F9D3", + "0x242B77DF9bc1Faf4256B6E82b866FF3Cf661D3D1" + ] } } } From 63517b88b2a1fda84c977c8f8934f2773cb2d353 Mon Sep 17 00:00:00 2001 From: Weixiong Tay Date: Tue, 27 Jan 2026 13:56:09 +0800 Subject: [PATCH 7/8] fix: fixed old creator event emission --- .openzeppelin/base-sepolia.json | 331 ++++++++++++++++++++++++++++++++ contracts/tax/AgentTax.sol | 10 +- 2 files changed, 340 insertions(+), 1 deletion(-) diff --git a/.openzeppelin/base-sepolia.json b/.openzeppelin/base-sepolia.json index 245f9a3..a88afb7 100644 --- a/.openzeppelin/base-sepolia.json +++ b/.openzeppelin/base-sepolia.json @@ -43323,6 +43323,337 @@ "0x3dbE2DD9959A6650256d7338B5d8AbaC1ad1F9D3", "0x242B77DF9bc1Faf4256B6E82b866FF3Cf661D3D1" ] + }, + "01e2c48fd3e4c5df3ba2bada8759707d3e70e6ab4a63e78378f97cbee3975c9f": { + "address": "0x59b397d14825A8268bFD04a9588A1AaFF277234E", + "txHash": "0xe5377fa6f28e848a15397bf549156620d55c08c7ae76bd93ec4d391ad70bdd40", + "layout": { + "solcVersion": "0.8.26", + "storage": [ + { + "label": "assetToken", + "offset": 0, + "slot": "0", + "type": "t_address", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:33" + }, + { + "label": "taxToken", + "offset": 0, + "slot": "1", + "type": "t_address", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:34" + }, + { + "label": "router", + "offset": 0, + "slot": "2", + "type": "t_contract(IRouter)6900", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:35" + }, + { + "label": "treasury", + "offset": 0, + "slot": "3", + "type": "t_address", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:36" + }, + { + "label": "feeRate", + "offset": 20, + "slot": "3", + "type": "t_uint16", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:37" + }, + { + "label": "minSwapThreshold", + "offset": 0, + "slot": "4", + "type": "t_uint256", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:38" + }, + { + "label": "maxSwapThreshold", + "offset": 0, + "slot": "5", + "type": "t_uint256", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:39" + }, + { + "label": "agentNft", + "offset": 0, + "slot": "6", + "type": "t_contract(IAgentNft)8451", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:40" + }, + { + "label": "_agentTba", + "offset": 0, + "slot": "7", + "type": "t_mapping(t_uint256,t_address)", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:57" + }, + { + "label": "taxHistory", + "offset": 0, + "slot": "8", + "type": "t_mapping(t_bytes32,t_struct(TaxHistory)6925_storage)", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:58" + }, + { + "label": "agentTaxAmounts", + "offset": 0, + "slot": "9", + "type": "t_mapping(t_uint256,t_struct(TaxAmounts)6930_storage)", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:59" + }, + { + "label": "_agentRecipients", + "offset": 0, + "slot": "10", + "type": "t_mapping(t_uint256,t_struct(TaxRecipient)7032_storage)", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:78" + }, + { + "label": "creatorFeeRate", + "offset": 0, + "slot": "11", + "type": "t_uint16", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:79" + }, + { + "label": "tbaBonus", + "offset": 2, + "slot": "11", + "type": "t_contract(ITBABonus)8228", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:87" + }, + { + "label": "bondingV2", + "offset": 0, + "slot": "12", + "type": "t_contract(BondingV2)5080", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:88" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes32": { + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_bool)": { + "label": "mapping(address => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_struct(RoleData)24_storage)": { + "label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)", + "numberOfBytes": "32" + }, + "t_struct(AccessControlStorage)34_storage": { + "label": "struct AccessControlUpgradeable.AccessControlStorage", + "members": [ + { + "label": "_roles", + "type": "t_mapping(t_bytes32,t_struct(RoleData)24_storage)", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_struct(InitializableStorage)211_storage": { + "label": "struct Initializable.InitializableStorage", + "members": [ + { + "label": "_initialized", + "type": "t_uint64", + "offset": 0, + "slot": "0" + }, + { + "label": "_initializing", + "type": "t_bool", + "offset": 8, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_struct(RoleData)24_storage": { + "label": "struct AccessControlUpgradeable.RoleData", + "members": [ + { + "label": "hasRole", + "type": "t_mapping(t_address,t_bool)", + "offset": 0, + "slot": "0" + }, + { + "label": "adminRole", + "type": "t_bytes32", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_uint64": { + "label": "uint64", + "numberOfBytes": "8" + }, + "t_contract(BondingV2)5080": { + "label": "contract BondingV2", + "numberOfBytes": "20" + }, + "t_contract(IAgentNft)8451": { + "label": "contract IAgentNft", + "numberOfBytes": "20" + }, + "t_contract(IRouter)6900": { + "label": "contract IRouter", + "numberOfBytes": "20" + }, + "t_contract(ITBABonus)8228": { + "label": "contract ITBABonus", + "numberOfBytes": "20" + }, + "t_mapping(t_bytes32,t_struct(TaxHistory)6925_storage)": { + "label": "mapping(bytes32 => struct AgentTax.TaxHistory)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_address)": { + "label": "mapping(uint256 => address)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_struct(TaxAmounts)6930_storage)": { + "label": "mapping(uint256 => struct AgentTax.TaxAmounts)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_struct(TaxRecipient)7032_storage)": { + "label": "mapping(uint256 => struct AgentTax.TaxRecipient)", + "numberOfBytes": "32" + }, + "t_struct(TaxAmounts)6930_storage": { + "label": "struct AgentTax.TaxAmounts", + "members": [ + { + "label": "amountCollected", + "type": "t_uint256", + "offset": 0, + "slot": "0" + }, + { + "label": "amountSwapped", + "type": "t_uint256", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(TaxHistory)6925_storage": { + "label": "struct AgentTax.TaxHistory", + "members": [ + { + "label": "agentId", + "type": "t_uint256", + "offset": 0, + "slot": "0" + }, + { + "label": "amount", + "type": "t_uint256", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(TaxRecipient)7032_storage": { + "label": "struct AgentTax.TaxRecipient", + "members": [ + { + "label": "tba", + "type": "t_address", + "offset": 0, + "slot": "0" + }, + { + "label": "creator", + "type": "t_address", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_uint16": { + "label": "uint16", + "numberOfBytes": "2" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + } + }, + "namespaces": { + "erc7201:openzeppelin.storage.AccessControl": [ + { + "contract": "AccessControlUpgradeable", + "label": "_roles", + "type": "t_mapping(t_bytes32,t_struct(RoleData)24_storage)", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:61", + "offset": 0, + "slot": "0" + } + ], + "erc7201:openzeppelin.storage.Initializable": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_uint64", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:69", + "offset": 0, + "slot": "0" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:73", + "offset": 8, + "slot": "0" + } + ] + } + }, + "allAddresses": [ + "0x59b397d14825A8268bFD04a9588A1AaFF277234E", + "0xb3B98801af60f28522Efa2199840Dd622AdF1B5F" + ] } } } diff --git a/contracts/tax/AgentTax.sol b/contracts/tax/AgentTax.sol index 7cb8393..4b7e265 100644 --- a/contracts/tax/AgentTax.sol +++ b/contracts/tax/AgentTax.sol @@ -339,9 +339,17 @@ contract AgentTax is Initializable, AccessControlUpgradeable { require(creator != address(0), "Invalid creator"); TaxRecipient storage recipient = _agentRecipients[agentId]; + + if (recipient.tba == address(0)) { + IAgentNft.VirtualInfo memory info = agentNft.virtualInfo(agentId); + recipient.tba = info.tba; + recipient.creator = info.founder; + } + + address oldCreator = recipient.creator; recipient.tba = tba; recipient.creator = creator; - emit CreatorUpdated(agentId, tba, creator); + emit CreatorUpdated(agentId, oldCreator, creator); } /** From 34836ad9797a8719aaafeefe7ed012398859188a Mon Sep 17 00:00:00 2001 From: Weixiong Tay Date: Mon, 2 Feb 2026 09:33:09 +0800 Subject: [PATCH 8/8] meta: add upgrade metadata --- .openzeppelin/base.json | 879 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 865 insertions(+), 14 deletions(-) diff --git a/.openzeppelin/base.json b/.openzeppelin/base.json index 1c400eb..cf571ea 100644 --- a/.openzeppelin/base.json +++ b/.openzeppelin/base.json @@ -132,6 +132,10 @@ "address": "0xA31bD6A0EdBC4Da307b8FA92bD6Cf39E0faE262c", "txHash": "0x2ef80d7057b21dd1087323b2c1d1e923d231f85458c96d24a9dc0f7fc6f0e06c", "kind": "transparent" + }, + { + "address": "0x7e26173192d72fd6d75a759f888d61c2cdbb64b1", + "kind": "transparent" } ], "impls": { @@ -14113,7 +14117,7 @@ "label": "factory", "offset": 0, "slot": "1", - "type": "t_contract(FFactoryV2)6296", + "type": "t_contract(FFactoryV2)8399", "contract": "BondingV2", "src": "contracts/launchpadv2/BondingV2.sol:26" }, @@ -14121,7 +14125,7 @@ "label": "router", "offset": 0, "slot": "2", - "type": "t_contract(FRouterV2)7710", + "type": "t_contract(FRouterV2)9771", "contract": "BondingV2", "src": "contracts/launchpadv2/BondingV2.sol:27" }, @@ -14177,7 +14181,7 @@ "label": "_deployParams", "offset": 0, "slot": "9", - "type": "t_struct(DeployParams)3153_storage", + "type": "t_struct(DeployParams)3521_storage", "contract": "BondingV2", "src": "contracts/launchpadv2/BondingV2.sol:83" }, @@ -14185,7 +14189,7 @@ "label": "tokenInfo", "offset": 0, "slot": "12", - "type": "t_mapping(t_address,t_struct(Token)3119_storage)", + "type": "t_mapping(t_address,t_struct(Token)3487_storage)", "contract": "BondingV2", "src": "contracts/launchpadv2/BondingV2.sol:85" }, @@ -14201,7 +14205,7 @@ "label": "launchParams", "offset": 0, "slot": "14", - "type": "t_struct(LaunchParams)3171_storage", + "type": "t_struct(LaunchParams)3539_storage", "contract": "BondingV2", "src": "contracts/launchpadv2/BondingV2.sol:93" } @@ -14245,7 +14249,7 @@ ], "numberOfBytes": "32" }, - "t_struct(ReentrancyGuardStorage)358_storage": { + "t_struct(ReentrancyGuardStorage)296_storage": { "label": "struct ReentrancyGuardUpgradeable.ReentrancyGuardStorage", "members": [ { @@ -14277,15 +14281,15 @@ "label": "bytes32", "numberOfBytes": "32" }, - "t_contract(FFactoryV2)6296": { + "t_contract(FFactoryV2)8399": { "label": "contract FFactoryV2", "numberOfBytes": "20" }, - "t_contract(FRouterV2)7710": { + "t_contract(FRouterV2)9771": { "label": "contract FRouterV2", "numberOfBytes": "20" }, - "t_mapping(t_address,t_struct(Token)3119_storage)": { + "t_mapping(t_address,t_struct(Token)3487_storage)": { "label": "mapping(address => struct BondingV2.Token)", "numberOfBytes": "32" }, @@ -14293,7 +14297,7 @@ "label": "string", "numberOfBytes": "32" }, - "t_struct(Data)3144_storage": { + "t_struct(Data)3512_storage": { "label": "struct BondingV2.Data", "members": [ { @@ -14371,7 +14375,7 @@ ], "numberOfBytes": "384" }, - "t_struct(DeployParams)3153_storage": { + "t_struct(DeployParams)3521_storage": { "label": "struct BondingV2.DeployParams", "members": [ { @@ -14401,7 +14405,7 @@ ], "numberOfBytes": "96" }, - "t_struct(LaunchParams)3171_storage": { + "t_struct(LaunchParams)3539_storage": { "label": "struct BondingV2.LaunchParams", "members": [ { @@ -14425,7 +14429,7 @@ ], "numberOfBytes": "96" }, - "t_struct(Token)3119_storage": { + "t_struct(Token)3487_storage": { "label": "struct BondingV2.Token", "members": [ { @@ -14454,7 +14458,7 @@ }, { "label": "data", - "type": "t_struct(Data)3144_storage", + "type": "t_struct(Data)3512_storage", "offset": 0, "slot": "4" }, @@ -16974,6 +16978,853 @@ ] } } + }, + "157fc4c2e85f80c9495cce9af8f1e5ec2860fd03e6ef17cd4a2c0b11c6f9bdcc": { + "address": "0xAe0702Ecb8c28D63BEBb7465B16379EB9c1451aB", + "txHash": "0x8b98060bf6f175fed39cdfe05b0302d42cab98ba4bcec4b6b732f7b4d23e02a7", + "layout": { + "solcVersion": "0.8.26", + "storage": [ + { + "label": "_feeTo", + "offset": 0, + "slot": "0", + "type": "t_address", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:24" + }, + { + "label": "factory", + "offset": 0, + "slot": "1", + "type": "t_contract(FFactoryV2)5405", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:26" + }, + { + "label": "router", + "offset": 0, + "slot": "2", + "type": "t_contract(FRouterV2)6777", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:27" + }, + { + "label": "initialSupply", + "offset": 0, + "slot": "3", + "type": "t_uint256", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:28" + }, + { + "label": "fee", + "offset": 0, + "slot": "4", + "type": "t_uint256", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:29" + }, + { + "label": "assetRate", + "offset": 0, + "slot": "5", + "type": "t_uint256", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:31" + }, + { + "label": "gradThreshold", + "offset": 0, + "slot": "6", + "type": "t_uint256", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:32" + }, + { + "label": "maxTx", + "offset": 0, + "slot": "7", + "type": "t_uint256", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:33" + }, + { + "label": "agentFactory", + "offset": 0, + "slot": "8", + "type": "t_address", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:34" + }, + { + "label": "_deployParams", + "offset": 0, + "slot": "9", + "type": "t_struct(DeployParams)3521_storage", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:83" + }, + { + "label": "tokenInfo", + "offset": 0, + "slot": "12", + "type": "t_mapping(t_address,t_struct(Token)3487_storage)", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:85" + }, + { + "label": "tokenInfos", + "offset": 0, + "slot": "13", + "type": "t_array(t_address)dyn_storage", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:86" + }, + { + "label": "launchParams", + "offset": 0, + "slot": "14", + "type": "t_struct(LaunchParams)3539_storage", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:93" + }, + { + "label": "isProject60days", + "offset": 0, + "slot": "17", + "type": "t_mapping(t_address,t_bool)", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:98" + }, + { + "label": "project60daysLaunchFee", + "offset": 0, + "slot": "18", + "type": "t_uint256", + "contract": "BondingV2", + "src": "contracts/launchpadv2/BondingV2.sol:99" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_struct(InitializableStorage)211_storage": { + "label": "struct Initializable.InitializableStorage", + "members": [ + { + "label": "_initialized", + "type": "t_uint64", + "offset": 0, + "slot": "0" + }, + { + "label": "_initializing", + "type": "t_bool", + "offset": 8, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_struct(OwnableStorage)151_storage": { + "label": "struct OwnableUpgradeable.OwnableStorage", + "members": [ + { + "label": "_owner", + "type": "t_address", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_struct(ReentrancyGuardStorage)296_storage": { + "label": "struct ReentrancyGuardUpgradeable.ReentrancyGuardStorage", + "members": [ + { + "label": "_status", + "type": "t_uint256", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint64": { + "label": "uint64", + "numberOfBytes": "8" + }, + "t_array(t_address)dyn_storage": { + "label": "address[]", + "numberOfBytes": "32" + }, + "t_array(t_uint8)dyn_storage": { + "label": "uint8[]", + "numberOfBytes": "32" + }, + "t_bytes32": { + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_contract(FFactoryV2)5405": { + "label": "contract FFactoryV2", + "numberOfBytes": "20" + }, + "t_contract(FRouterV2)6777": { + "label": "contract FRouterV2", + "numberOfBytes": "20" + }, + "t_mapping(t_address,t_bool)": { + "label": "mapping(address => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_struct(Token)3487_storage)": { + "label": "mapping(address => struct BondingV2.Token)", + "numberOfBytes": "32" + }, + "t_string_storage": { + "label": "string", + "numberOfBytes": "32" + }, + "t_struct(Data)3512_storage": { + "label": "struct BondingV2.Data", + "members": [ + { + "label": "token", + "type": "t_address", + "offset": 0, + "slot": "0" + }, + { + "label": "name", + "type": "t_string_storage", + "offset": 0, + "slot": "1" + }, + { + "label": "_name", + "type": "t_string_storage", + "offset": 0, + "slot": "2" + }, + { + "label": "ticker", + "type": "t_string_storage", + "offset": 0, + "slot": "3" + }, + { + "label": "supply", + "type": "t_uint256", + "offset": 0, + "slot": "4" + }, + { + "label": "price", + "type": "t_uint256", + "offset": 0, + "slot": "5" + }, + { + "label": "marketCap", + "type": "t_uint256", + "offset": 0, + "slot": "6" + }, + { + "label": "liquidity", + "type": "t_uint256", + "offset": 0, + "slot": "7" + }, + { + "label": "volume", + "type": "t_uint256", + "offset": 0, + "slot": "8" + }, + { + "label": "volume24H", + "type": "t_uint256", + "offset": 0, + "slot": "9" + }, + { + "label": "prevPrice", + "type": "t_uint256", + "offset": 0, + "slot": "10" + }, + { + "label": "lastUpdated", + "type": "t_uint256", + "offset": 0, + "slot": "11" + } + ], + "numberOfBytes": "384" + }, + "t_struct(DeployParams)3521_storage": { + "label": "struct BondingV2.DeployParams", + "members": [ + { + "label": "tbaSalt", + "type": "t_bytes32", + "offset": 0, + "slot": "0" + }, + { + "label": "tbaImplementation", + "type": "t_address", + "offset": 0, + "slot": "1" + }, + { + "label": "daoVotingPeriod", + "type": "t_uint32", + "offset": 20, + "slot": "1" + }, + { + "label": "daoThreshold", + "type": "t_uint256", + "offset": 0, + "slot": "2" + } + ], + "numberOfBytes": "96" + }, + "t_struct(LaunchParams)3539_storage": { + "label": "struct BondingV2.LaunchParams", + "members": [ + { + "label": "startTimeDelay", + "type": "t_uint256", + "offset": 0, + "slot": "0" + }, + { + "label": "teamTokenReservedSupply", + "type": "t_uint256", + "offset": 0, + "slot": "1" + }, + { + "label": "teamTokenReservedWallet", + "type": "t_address", + "offset": 0, + "slot": "2" + } + ], + "numberOfBytes": "96" + }, + "t_struct(Token)3487_storage": { + "label": "struct BondingV2.Token", + "members": [ + { + "label": "creator", + "type": "t_address", + "offset": 0, + "slot": "0" + }, + { + "label": "token", + "type": "t_address", + "offset": 0, + "slot": "1" + }, + { + "label": "pair", + "type": "t_address", + "offset": 0, + "slot": "2" + }, + { + "label": "agentToken", + "type": "t_address", + "offset": 0, + "slot": "3" + }, + { + "label": "data", + "type": "t_struct(Data)3512_storage", + "offset": 0, + "slot": "4" + }, + { + "label": "description", + "type": "t_string_storage", + "offset": 0, + "slot": "16" + }, + { + "label": "cores", + "type": "t_array(t_uint8)dyn_storage", + "offset": 0, + "slot": "17" + }, + { + "label": "image", + "type": "t_string_storage", + "offset": 0, + "slot": "18" + }, + { + "label": "twitter", + "type": "t_string_storage", + "offset": 0, + "slot": "19" + }, + { + "label": "telegram", + "type": "t_string_storage", + "offset": 0, + "slot": "20" + }, + { + "label": "youtube", + "type": "t_string_storage", + "offset": 0, + "slot": "21" + }, + { + "label": "website", + "type": "t_string_storage", + "offset": 0, + "slot": "22" + }, + { + "label": "trading", + "type": "t_bool", + "offset": 0, + "slot": "23" + }, + { + "label": "tradingOnUniswap", + "type": "t_bool", + "offset": 1, + "slot": "23" + }, + { + "label": "applicationId", + "type": "t_uint256", + "offset": 0, + "slot": "24" + }, + { + "label": "initialPurchase", + "type": "t_uint256", + "offset": 0, + "slot": "25" + }, + { + "label": "virtualId", + "type": "t_uint256", + "offset": 0, + "slot": "26" + }, + { + "label": "launchExecuted", + "type": "t_bool", + "offset": 0, + "slot": "27" + } + ], + "numberOfBytes": "896" + }, + "t_uint32": { + "label": "uint32", + "numberOfBytes": "4" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + }, + "namespaces": { + "erc7201:openzeppelin.storage.Ownable": [ + { + "contract": "OwnableUpgradeable", + "label": "_owner", + "type": "t_address", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:24", + "offset": 0, + "slot": "0" + } + ], + "erc7201:openzeppelin.storage.ReentrancyGuard": [ + { + "contract": "ReentrancyGuardUpgradeable", + "label": "_status", + "type": "t_uint256", + "src": "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol:40", + "offset": 0, + "slot": "0" + } + ], + "erc7201:openzeppelin.storage.Initializable": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_uint64", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:69", + "offset": 0, + "slot": "0" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:73", + "offset": 8, + "slot": "0" + } + ] + } + } + }, + "01e2c48fd3e4c5df3ba2bada8759707d3e70e6ab4a63e78378f97cbee3975c9f": { + "address": "0x2894B761A80DEA70620408a972650B759162472E", + "layout": { + "solcVersion": "0.8.26", + "storage": [ + { + "label": "assetToken", + "offset": 0, + "slot": "0", + "type": "t_address", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:33" + }, + { + "label": "taxToken", + "offset": 0, + "slot": "1", + "type": "t_address", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:34" + }, + { + "label": "router", + "offset": 0, + "slot": "2", + "type": "t_contract(IRouter)6900", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:35" + }, + { + "label": "treasury", + "offset": 0, + "slot": "3", + "type": "t_address", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:36" + }, + { + "label": "feeRate", + "offset": 20, + "slot": "3", + "type": "t_uint16", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:37" + }, + { + "label": "minSwapThreshold", + "offset": 0, + "slot": "4", + "type": "t_uint256", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:38" + }, + { + "label": "maxSwapThreshold", + "offset": 0, + "slot": "5", + "type": "t_uint256", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:39" + }, + { + "label": "agentNft", + "offset": 0, + "slot": "6", + "type": "t_contract(IAgentNft)8451", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:40" + }, + { + "label": "_agentTba", + "offset": 0, + "slot": "7", + "type": "t_mapping(t_uint256,t_address)", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:57" + }, + { + "label": "taxHistory", + "offset": 0, + "slot": "8", + "type": "t_mapping(t_bytes32,t_struct(TaxHistory)6925_storage)", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:58" + }, + { + "label": "agentTaxAmounts", + "offset": 0, + "slot": "9", + "type": "t_mapping(t_uint256,t_struct(TaxAmounts)6930_storage)", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:59" + }, + { + "label": "_agentRecipients", + "offset": 0, + "slot": "10", + "type": "t_mapping(t_uint256,t_struct(TaxRecipient)7032_storage)", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:78" + }, + { + "label": "creatorFeeRate", + "offset": 0, + "slot": "11", + "type": "t_uint16", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:79" + }, + { + "label": "tbaBonus", + "offset": 2, + "slot": "11", + "type": "t_contract(ITBABonus)8228", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:87" + }, + { + "label": "bondingV2", + "offset": 0, + "slot": "12", + "type": "t_contract(BondingV2)5080", + "contract": "AgentTax", + "src": "contracts/tax/AgentTax.sol:88" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes32": { + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_bool)": { + "label": "mapping(address => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_struct(RoleData)24_storage)": { + "label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)", + "numberOfBytes": "32" + }, + "t_struct(AccessControlStorage)34_storage": { + "label": "struct AccessControlUpgradeable.AccessControlStorage", + "members": [ + { + "label": "_roles", + "type": "t_mapping(t_bytes32,t_struct(RoleData)24_storage)", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_struct(InitializableStorage)211_storage": { + "label": "struct Initializable.InitializableStorage", + "members": [ + { + "label": "_initialized", + "type": "t_uint64", + "offset": 0, + "slot": "0" + }, + { + "label": "_initializing", + "type": "t_bool", + "offset": 8, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_struct(RoleData)24_storage": { + "label": "struct AccessControlUpgradeable.RoleData", + "members": [ + { + "label": "hasRole", + "type": "t_mapping(t_address,t_bool)", + "offset": 0, + "slot": "0" + }, + { + "label": "adminRole", + "type": "t_bytes32", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_uint64": { + "label": "uint64", + "numberOfBytes": "8" + }, + "t_contract(BondingV2)5080": { + "label": "contract BondingV2", + "numberOfBytes": "20" + }, + "t_contract(IAgentNft)8451": { + "label": "contract IAgentNft", + "numberOfBytes": "20" + }, + "t_contract(IRouter)6900": { + "label": "contract IRouter", + "numberOfBytes": "20" + }, + "t_contract(ITBABonus)8228": { + "label": "contract ITBABonus", + "numberOfBytes": "20" + }, + "t_mapping(t_bytes32,t_struct(TaxHistory)6925_storage)": { + "label": "mapping(bytes32 => struct AgentTax.TaxHistory)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_address)": { + "label": "mapping(uint256 => address)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_struct(TaxAmounts)6930_storage)": { + "label": "mapping(uint256 => struct AgentTax.TaxAmounts)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_struct(TaxRecipient)7032_storage)": { + "label": "mapping(uint256 => struct AgentTax.TaxRecipient)", + "numberOfBytes": "32" + }, + "t_struct(TaxAmounts)6930_storage": { + "label": "struct AgentTax.TaxAmounts", + "members": [ + { + "label": "amountCollected", + "type": "t_uint256", + "offset": 0, + "slot": "0" + }, + { + "label": "amountSwapped", + "type": "t_uint256", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(TaxHistory)6925_storage": { + "label": "struct AgentTax.TaxHistory", + "members": [ + { + "label": "agentId", + "type": "t_uint256", + "offset": 0, + "slot": "0" + }, + { + "label": "amount", + "type": "t_uint256", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(TaxRecipient)7032_storage": { + "label": "struct AgentTax.TaxRecipient", + "members": [ + { + "label": "tba", + "type": "t_address", + "offset": 0, + "slot": "0" + }, + { + "label": "creator", + "type": "t_address", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_uint16": { + "label": "uint16", + "numberOfBytes": "2" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + } + }, + "namespaces": { + "erc7201:openzeppelin.storage.AccessControl": [ + { + "contract": "AccessControlUpgradeable", + "label": "_roles", + "type": "t_mapping(t_bytes32,t_struct(RoleData)24_storage)", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:61", + "offset": 0, + "slot": "0" + } + ], + "erc7201:openzeppelin.storage.Initializable": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_uint64", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:69", + "offset": 0, + "slot": "0" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:73", + "offset": 8, + "slot": "0" + } + ] + } + }, + "allAddresses": [ + "0x2894B761A80DEA70620408a972650B759162472E", + "0xb844b0E04416973b4d40dc3c89b265496cE22b0E", + "0xc37A4BdCed74c34971abB340582685A6aC1b5430", + "0x8A101b1833F8F4F0feAf663F3081f5241eBd9d57" + ] } } }