Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 117 additions & 1 deletion contracts/tax/AgentTaxV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ contract AgentTaxV2 is Initializable, AccessControlUpgradeable {
uint256 amountSwapped;
}

struct TokenPartnerConfig {
bytes32 partnerId;
uint16 partnerFeeRate;
}

bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
bytes32 public constant REGISTER_ROLE = keccak256("REGISTER_ROLE");
bytes32 public constant SWAP_ROLE = keccak256("SWAP_ROLE");
Expand All @@ -58,6 +63,9 @@ contract AgentTaxV2 is Initializable, AccessControlUpgradeable {

IBondingV5ForTaxV2 public bondingV5;

mapping(bytes32 partnerId => address recipient) public partnerRecipients;
mapping(address tokenAddress => TokenPartnerConfig) public tokenPartnerConfigs;

event TokenRegistered(address indexed tokenAddress, address indexed creator, address tba);
event TaxDeposited(address indexed tokenAddress, uint256 amount);
event SwapExecuted(address indexed tokenAddress, uint256 taxTokenAmount, uint256 assetTokenAmount);
Expand All @@ -78,6 +86,23 @@ contract AgentTaxV2 is Initializable, AccessControlUpgradeable {
uint256 newMaxThreshold
);
event TreasuryUpdated(address oldTreasury, address newTreasury);
event PartnerRecipientUpdated(
bytes32 indexed partnerId,
address oldRecipient,
address newRecipient
);
event TokenPartnerConfigUpdated(
address indexed tokenAddress,
bytes32 indexed partnerId,
uint16 partnerFeeRate
);
event PartnerFeeDistributed(
address indexed tokenAddress,
bytes32 indexed partnerId,
address indexed recipient,
uint256 amount
);
event FeeSplitUpdated(uint16 oldProtocolFeeRate, uint16 newProtocolFeeRate);

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
Expand Down Expand Up @@ -307,7 +332,21 @@ contract AgentTaxV2 is Initializable, AccessControlUpgradeable {
emit SwapExecuted(tokenAddress, amountToSwap, assetReceived);

uint256 protocolFee = (assetReceived * feeRate) / DENOM;
uint256 creatorFee = assetReceived - protocolFee;

TokenPartnerConfig memory partnerConfig = tokenPartnerConfigs[tokenAddress];
uint256 partnerFee = (assetReceived * partnerConfig.partnerFeeRate) / DENOM;
uint256 creatorFee = assetReceived - protocolFee - partnerFee;

if (partnerFee > 0) {
address partnerRecipient = partnerRecipients[partnerConfig.partnerId];
Comment thread
koo-virtuals marked this conversation as resolved.
IERC20(assetToken).safeTransfer(partnerRecipient, partnerFee);
emit PartnerFeeDistributed(
tokenAddress,
partnerConfig.partnerId,
partnerRecipient,
partnerFee
);
}

if (creatorFee > 0) {
IERC20(assetToken).safeTransfer(recipient.creator, creatorFee);
Expand Down Expand Up @@ -345,6 +384,16 @@ contract AgentTaxV2 is Initializable, AccessControlUpgradeable {
return (amounts.amountCollected, amounts.amountSwapped);
}

/**
* @notice Get partner fee configuration for an agent token
*/
function getTokenPartnerConfig(
address tokenAddress
) external view returns (bytes32 partnerId, uint16 partnerFeeRate) {
TokenPartnerConfig memory config = tokenPartnerConfigs[tokenAddress];
return (config.partnerId, config.partnerFeeRate);
}

// ============ Creator Functions ============

/**
Expand Down Expand Up @@ -373,6 +422,73 @@ contract AgentTaxV2 is Initializable, AccessControlUpgradeable {

// ============ Admin Functions ============

/**
* @notice Set the receiving address for a partner / referrer integration
* @param partnerId Unique partner identifier
* @param recipient Address that receives partner fee shares
*/
function setPartnerRecipient(
bytes32 partnerId,
address recipient
) external onlyRole(ADMIN_ROLE) {
require(partnerId != bytes32(0), "Invalid partner ID");
require(recipient != address(0), "Invalid recipient");

address oldRecipient = partnerRecipients[partnerId];
partnerRecipients[partnerId] = recipient;

emit PartnerRecipientUpdated(partnerId, oldRecipient, recipient);
}

/**
* @notice Configure partner fee split for an agent token
* @dev Partner fee is deducted from the total swapped asset amount alongside the
* protocol fee. Remaining amount goes to the token creator.
* `feeRate + partnerFeeRate` must not exceed DENOM (100%).
* @param tokenAddress The agent token address
* @param partnerId Partner identifier (must have recipient configured if rate > 0)
* @param partnerFeeRate Partner share in basis points (out of 10000)
*/
function setTokenPartnerConfig(
address tokenAddress,
bytes32 partnerId,
uint16 partnerFeeRate
) external onlyRole(ADMIN_ROLE) {
require(tokenAddress != address(0), "Invalid token address");
require(feeRate + partnerFeeRate <= DENOM, "Fee split exceeds 100%");

if (partnerFeeRate > 0) {
require(partnerId != bytes32(0), "Partner ID required");
require(
partnerRecipients[partnerId] != address(0),
"Partner recipient not set"
);
}

tokenPartnerConfigs[tokenAddress] = TokenPartnerConfig({
partnerId: partnerId,
partnerFeeRate: partnerFeeRate
});

emit TokenPartnerConfigUpdated(tokenAddress, partnerId, partnerFeeRate);
}

/**
* @notice Update the global protocol (treasury) fee rate
* @dev Per-token partner fees are configured via setTokenPartnerConfig().
* After updating, ensure `feeRate + partnerFeeRate <= DENOM` for all tokens
* with an active partner split.
* @param protocolFeeRate_ Protocol share in basis points (out of 10000)
*/
function updateFeeSplit(uint16 protocolFeeRate_) external onlyRole(ADMIN_ROLE) {
Comment thread
koo-virtuals marked this conversation as resolved.
Outdated
require(protocolFeeRate_ <= DENOM, "Fee rate too high");

uint16 oldFeeRate = feeRate;
feeRate = protocolFeeRate_;

emit FeeSplitUpdated(oldFeeRate, protocolFeeRate_);
}
Comment thread
twx-virtuals marked this conversation as resolved.

/**
* @notice Set BondingV5 contract address
* @param bondingV5_ The address of the BondingV5 contract
Expand Down