Skip to content

Conversation

@jmack33
Copy link

@jmack33 jmack33 commented Jan 26, 2025

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@thirdweb-dev/src/Module.sol";
import "@thirdweb-dev/src/Role.sol";

contract BOPToken is Module {
// --- State Variables ---
address public charityWallet;
uint256 public transactionFeePercentage; // In basis points (e.g., 250 = 2.5%)
uint256 public burnPercentage; // In basis points
uint256 public charityPercentage; // In basis points
uint256 public maxTransactionAmount; // Maximum tokens allowed per transaction

event Transfer(address indexed from, address indexed to, uint256 value);
event FeeDistributed(address indexed charity, uint256 amountBurned, uint256 amountToCharity);

// --- Storage Library ---
library BOPStorage {
    bytes32 public constant BOP_STORAGE_POSITION =
        keccak256(abi.encode(uint256(keccak256("bop.token.storage"))) - 1) & 
        ~bytes32(uint256(0xff));

    struct Data {
        uint256 totalSupply;
        mapping(address => uint256) balances;
    }

    function data() internal pure returns (Data storage data_) {
        bytes32 position = BOP_STORAGE_POSITION;
        assembly {
            data_.slot := position
        }
    }
}

// --- Constructor ---
constructor(
    address _charityWallet,
    uint256 _transactionFeePercentage,
    uint256 _burnPercentage,
    uint256 _charityPercentage,
    uint256 _maxTransactionAmount
) {
    require(_charityWallet != address(0), "Invalid charity wallet address");
    require(_transactionFeePercentage + _burnPercentage + _charityPercentage <= 10000, "Invalid fee percentages");

    charityWallet = _charityWallet;
    transactionFeePercentage = _transactionFeePercentage;
    burnPercentage = _burnPercentage;
    charityPercentage = _charityPercentage;
    maxTransactionAmount = _maxTransactionAmount;
}

// --- Callback Functions ---
function beforeTransfer(
    address from,
    address to,
    uint256 amount
) external view returns (uint256) {
    require(from != address(0), "Invalid sender address");
    require(to != address(0), "Invalid recipient address");
    require(amount > 0, "Transfer amount must be greater than zero");
    require(amount <= maxTransactionAmount, "Transfer exceeds max transaction amount");
    return amount;
}

// --- Fallback Functions ---
function getTransactionFee() external view returns (uint256) {
    return transactionFeePercentage;
}

function getCharityPercentage() external view returns (uint256) {
    return charityPercentage;
}

function getBurnPercentage() external view returns (uint256) {
    return burnPercentage;
}

function getMaxTransactionAmount() external view returns (uint256) {
    return maxTransactionAmount;
}

function setCharityWallet(address _charityWallet) external onlyRole(Role._MANAGER_ROLE) {
    require(_charityWallet != address(0), "Invalid charity wallet address");
    charityWallet = _charityWallet;
}

// --- Token Transfer Logic ---
function transfer(
    address from,
    address to,
    uint256 amount
) external {
    uint256 finalAmount = beforeTransfer(from, to, amount);

    BOPStorage.Data storage store = BOPStorage.data();
    require(store.balances[from] >= finalAmount, "Insufficient balance");

    // Calculate fees
    uint256 transactionFee = (finalAmount * transactionFeePercentage) / 10000;
    uint256 burnAmount = (transactionFee * burnPercentage) / 10000;
    uint256 charityAmount = (transactionFee * charityPercentage) / 10000;

    // Apply fees
    uint256 netAmount = finalAmount - transactionFee;

    // Update balances
    store.balances[from] -= finalAmount;
    store.balances[to] += netAmount;

    // Burn and distribute fees
    store.totalSupply -= burnAmount;
    store.balances[charityWallet] += charityAmount;

    emit Transfer(from, to, netAmount);
    emit FeeDistributed(charityWallet, burnAmount, charityAmount);
}

// --- Module Configuration ---
function getModuleConfig()
    public
    pure
    override
    returns (ModuleConfig memory config)
{
    config.callbackFunctions = new CallbackFunction      config.fallbackFunctions = new FallbackFunction ;

// Register callback functions
config.callbackFunctions[0] = CallbackFunction(this.beforeTransfer.selector);

    // Register fallback functions
    config.fallbackFunctions[0] = FallbackFunction({
        selector: this.getTransactionFee.selector,
        permissionBits: 0
    });
    config.fallbackFunctions[1] = FallbackFunction({
        selector: this.getCharityPercentage.selector,
        permissionBits: 0
    });
    config.fallbackFunctions[2] = FallbackFunction({
        selector: this.getBurnPercentage.selector,
        permissionBits: 0
    });
    config.fallbackFunctions[3] = FallbackFunction({
        selector: this.getMaxTransactionAmount.selector,
        permissionBits: Role._MANAGER_ROLE
    });
    config.fallbackFunctions[4] = FallbackFunction({
        selector: this.setCharityWallet.selector,
        permissionBits: Role._MANAGER_ROLE
    });

    config.requiredInterfaces = new bytes4 ;
 nfig.requiredInterfaces[0] = 0x00000001;

    config.registerInstallationCallback = true;
}

}


PR-Codex overview

This PR focuses on refactoring the CounterModule contract into a more versatile BOPTokenModule, enhancing its functionality with a new storage library and additional features for managing token transactions, fees, and governance.

Detailed summary

  • Renamed CounterModule to BOPTokenModule.
  • Introduced TokenStorage library for token-related data management.
  • Added events for Transfer and Approval.
  • Implemented functions for setting transaction fees, charity wallet, and percentages.
  • Enhanced token transfer logic with fee deduction and charity allocation.
  • Added balanceOf and allowance functions for token balance management.
  • Updated getModuleConfig to include new callback and fallback functions.

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

Signed-off-by: jmack33 <[email protected]>
@jmack33 jmack33 requested review from a team as code owners January 26, 2025 00:59
@changeset-bot
Copy link

changeset-bot bot commented Jan 26, 2025

⚠️ No Changeset found

Latest commit: db68a45

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link

vercel bot commented Jan 26, 2025

@jmack33 is attempting to deploy a commit to the thirdweb Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions github-actions bot added the Portal Involves changes to the Portal (docs) codebase. label Jan 26, 2025
@graphite-app
Copy link
Contributor

graphite-app bot commented Jan 26, 2025

How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • merge-queue - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Portal Involves changes to the Portal (docs) codebase.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants