Overview

Scratch_Engine
Rank #N/A

Skynet Trust Score

58%
  • Project is Relatively Decentralized
  • Large Market Cap (top 25%)
  • Long-running Project
  • Trust Score is #1 amongst all projects
Security Score
58 / 100
Market & Community
82 / 100

Scratch_Engine Info

Etherscan, born in 2015, is Ethereum's premier explorer and analytics hub, committed to democratizing blockchain data access.

Audits

Onboarded Date

14/Dec/2022

Contracts

0x570...972d7

How do you feel about this project's security?
Documents
File Name Type Size Upload Date Action
Zip File 4.57 MB 12 Dec 2021
PDF File 8.89 MB 24 Nov 2021
MP4 File 14.62 MB 19 Nov 2021
XSL File 2.38 KB 14 Nov 2021
Floder File 87.24 MB 08 Nov 2021
PNG File 879 KB 02 Nov 2021
Activities
Oliver Phillips New

We talked about a project on linkedin.

Today
N
Nancy Martino In Progress

Create new project Buildng product

Yesterday
Natasha Carey Completed

Adding a new event with attachments

25 Nov
Bethany Johnson

added a new member to velzon dashboard

19 Nov
Your order is placed Out of Delivery

These customers can rest assured their order has been placed.

16 Nov
Lewis Pratt

They all have something to say beyond the words on the page. They can come across as casual or neutral, exotic or graphic.

22 Oct
Monthly sales report

2 days left notification to submit the monthly sales report. Reports Builder

15 Oct
New ticket received Completed

User Erica245 submitted a ticket.

26 Aug
Nancy Martino

Team Leader & HR

225

Projects

197

Tasks

HB
Henry Baird

Full Stack Developer

352

Projects

376

Tasks

Frank Hook

Project Manager

164

Projects

182

Tasks

Jennifer Carter

UI/UX Designer

225

Projects

197

Tasks

ME
Megan Elmore

Team Leader & Web Developer

201

Projects

263

Tasks

Alexis Clarke

Backend Developer

132

Projects

147

Tasks

NC
Nathan Cole

Front-End Developer

352

Projects

376

Tasks

Joseph Parker

Team Leader & HR

64

Projects

93

Tasks

Erica Kernan

Web Designer

345

Projects

298

Tasks

DP
Donald Palmer

Wed Developer

97

Projects

135

Tasks

Showing 1 to 10 of 12 entries

Code Audit History

1 Audit available
Last Audit was delivered on 14 December 2022

Scratch_Engine -Audit

View Findings
5

All Findings

0

Acknowledge

0

Partially

5

Resolved

1
Critical privilege
2
Major privilege
1
Medium privilege
1
Minor privilege
0
Optimization none
0
Informational none
0
Discussion none

Method

Audited Files/SHA256

Contracts

0x570db9a388...972d7

Manual Review Static Analysis
Audit Timeline
Requested on
14 December 2022
Revisioned on
14 December 2022

Formal Verification Result

9 / 38 Properties True
80%

Token Standard

ERC-20

Functions

6

Verified Contract

Scratch_Engine (scratchengine.sol) 1

Scratch_Engine Smart Contract Code

                        
                        

// SPDX-License-Identifier: UNLICENSED

/** 

 *   Copyright © 2022 Scratch Engine LLC. All rights reserved.

 *   Limited license is afforded to Etherscan, in accordance with its Terms of Use, 

 *   in order to publish this material.

 *   In connection with the foregoing, redistribution and use on the part of Etherscan,

 *   in source and binary forms, without modification, are permitted, 

 *   provided that such redistributions of source code retain the foregoing copyright notice

 *   and this disclaimer.

 */


pragma solidity 0.8.4;


// import "hardhat/console.sol";


// Openzeppelin

import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";

import "@openzeppelin/contracts/access/Ownable.sol";

import "@openzeppelin/contracts/utils/Address.sol";


// Uniswap

import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";

import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol";

import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";


import "./FoundersTimelock.sol";


/**

 * @title ScratchToken

 * @dev An ERC20 token featuring fees-on-transfer for buy/sell transactions

 * and increased fees on larger sell transactions.

 */

contract ScratchToken is Context, IERC20, Ownable {


    using Address for address;


    // ERC20

    string private constant _NAME = "ScratchToken";

    string private constant _SYMBOL = "SCRATCH";

    uint8 private constant _DECIMALS = 9;

    uint256 private constant _MAX_SUPPLY = 100 * 10**15 * 10 ** _DECIMALS;

    uint256 private _totalSupply;

    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;


    // All percentages are relative to this value (1/10,000)

    uint256 private constant _PERCENTAGE_RELATIVE_TO = 10000;


    /// Distribution

    uint256 private constant _DIST_BURN_PERCENTAGE = 1850;

    uint256 private constant _DIST_FOUNDER1_PERCENTAGE = 250;

    uint256 private constant _DIST_FOUNDER2_PERCENTAGE = 250;

    uint256 private constant _DIST_FOUNDER3_PERCENTAGE = 250;

    uint256 private constant _DIST_FOUNDER4_PERCENTAGE = 250;

    uint256 private constant _DIST_FOUNDER5_PERCENTAGE = 250;

    uint256 private constant _DIST_EXCHANGE_PERCENTAGE = 750;

    uint256 private constant _DIST_DEV_PERCENTAGE = 500;

    uint256 private constant _DIST_OPS_PERCENTAGE = 150;


    // Founders TimeLock

    uint256 private constant _FOUNDERS_CLIFF_DURATION = 30 days * 6; // 6 months

    uint256 private constant _FOUNDERS_VESTING_PERIOD = 30 days; // Release every 30 days

    uint8 private constant _FOUNDERS_VESTING_DURATION = 10; // Linear release 10 times every 30 days

    mapping(address => FoundersTimelock) public foundersTimelocks;

    event FounderLiquidityLocked (

        address wallet,

        address timelockContract,

        uint256 tokensAmount

    );


    // Fees

    uint256 private constant _TAX_NORMAL_DEV_PERCENTAGE = 200;

    uint256 private constant _TAX_NORMAL_LIQUIDITY_PERCENTAGE = 200;

    uint256 private constant _TAX_NORMAL_OPS_PERCENTAGE = 100;

    uint256 private constant _TAX_NORMAL_ARCHA_PERCENTAGE = 100;

    uint256 private constant _TAX_EXTRA_LIQUIDITY_PERCENTAGE = 1000;

    uint256 private constant _TAX_EXTRA_BURN_PERCENTAGE = 500;

    uint256 private constant _TAX_EXTRA_DEV_PERCENTAGE = 500;

    uint256 private constant _TOKEN_STABILITY_PROTECTION_THRESHOLD_PERCENTAGE = 200;


    bool private _devFeeEnabled = true;

    bool private _opsFeeEnabled = true;

    bool private _liquidityFeeEnabled = true;

    bool private _archaFeeEnabled = true;

    bool private _burnFeeEnabled = true;

    bool private _tokenStabilityProtectionEnabled = true;


    mapping (address => bool) private _isExcludedFromFee;

    address private immutable _developmentWallet;

    address private _operationsWallet;

    address private _archaWallet;

    // Accumulated unswaped tokens from fee

    uint256 private _devFeePendingSwap = 0;

    uint256 private _opsFeePendingSwap = 0;

    uint256 private _liquidityFeePendingSwap = 0;


    // Uniswap

    uint256 private constant _UNISWAP_DEADLINE_DELAY = 60; // in seconds

    IUniswapV2Router02 private _uniswapV2Router;

    IUniswapV2Pair private _uniswapV2Pair;

    address private immutable _lpTokensWallet;

    bool private _inSwap = false; // Whether a previous call of swap process is still in process.

    bool private _swapAndLiquifyEnabled = true;

    uint256 private _minTokensBeforeSwapAndLiquify = 1 * 10 ** _DECIMALS;

    address private _liquidityWallet = 0x0000000000000000000000000000000000000000;


    // Events

    event SwapAndLiquify(

        uint256 tokensSwapped,

        uint256 ethReceived,

        uint256 tokensAddedToLiquidity

    );

    event SwapAndLiquifyEnabledUpdated(bool enabled);

    event MinTokensBeforeSwapUpdated(uint256 minTokensBeforeSwap);

    event DevFeeEnabledUpdated(bool enabled);

    event OpsFeeEnabledUpdated(bool enabled);

    event LiquidityFeeEnabledUpdated(bool enabled);

    event ArchaFeeEnabledUpdated(bool enabled);

    event BurnFeeEnabledUpdated(bool enabled);

    event TokenStabilityProtectionEnabledUpdated(bool enabled);


    event ExclusionFromFeesUpdated(address account, bool isExcluded);

    event ArchaWalletUpdated(address newWallet);

    event LiquidityWalletUpdated(address newWallet);



    // Modifiers

    modifier lockTheSwap {

        require(!_inSwap, "Currently in swap.");

        _inSwap = true;

        _;

        _inSwap = false;

    }


    // Fallback function to recieve ETH from uniswapV2Router when swaping

    receive() external payable {}

    

    constructor (

        address owner,

        address founder1Wallet_,

        address founder2Wallet_,

        address founder3Wallet_,

        address founder4Wallet_,

        address founder5Wallet_,

        address developmentWallet_,

        address exchangeWallet_,

        address operationsWallet_,

        address archaWallet_,

        address uniswapV2RouterAddress_

    ) {


        require(developmentWallet_ != address(0), "ScratchToken: set wallet to the zero address");

        require(exchangeWallet_ != address(0), "ScratchToken: set wallet to the zero address");

        require(operationsWallet_ != address(0), "ScratchToken: set wallet to the zero address");

        require(archaWallet_ != address(0), "ScratchToken: set wallet to the zero address");


        // Exclude addresses from fee

        _isExcludedFromFee[owner] = true;

        _isExcludedFromFee[address(this)] = true;

        _isExcludedFromFee[founder1Wallet_] = true;

        _isExcludedFromFee[founder2Wallet_] = true;

        _isExcludedFromFee[founder3Wallet_] = true;

        _isExcludedFromFee[founder4Wallet_] = true;

        _isExcludedFromFee[founder5Wallet_] = true;

        _isExcludedFromFee[developmentWallet_] = true;

        _isExcludedFromFee[exchangeWallet_] = true;

        _isExcludedFromFee[operationsWallet_] = true;

        _isExcludedFromFee[archaWallet_] = true;


        /// Perform initial distribution 

        // Founders

        _lockFounderLiquidity(founder1Wallet_, _DIST_FOUNDER1_PERCENTAGE);

        _lockFounderLiquidity(founder2Wallet_, _DIST_FOUNDER2_PERCENTAGE);

        _lockFounderLiquidity(founder3Wallet_, _DIST_FOUNDER3_PERCENTAGE);

        _lockFounderLiquidity(founder4Wallet_, _DIST_FOUNDER4_PERCENTAGE);

        _lockFounderLiquidity(founder5Wallet_, _DIST_FOUNDER5_PERCENTAGE);

        // Exchange

        _mint(exchangeWallet_, _getAmountToDistribute(_DIST_EXCHANGE_PERCENTAGE));

        _lpTokensWallet = exchangeWallet_;

        // Dev

        _mint(developmentWallet_, _getAmountToDistribute(_DIST_DEV_PERCENTAGE));

        _developmentWallet = developmentWallet_;

        // Operations

        _mint(operationsWallet_, _getAmountToDistribute(_DIST_OPS_PERCENTAGE));

        _operationsWallet = operationsWallet_;

        // Archa (used later for taxes)

        _archaWallet = archaWallet_;

        // Burn

        uint256 burnAmount = _getAmountToDistribute(_DIST_BURN_PERCENTAGE);

        emit Transfer(address(0), address(0), burnAmount);

        // Send the rest of supply minus burn to owner

        _mint(owner, _MAX_SUPPLY - totalSupply() - burnAmount);


        // Initialize uniswap

        _initSwap(uniswapV2RouterAddress_);


        // Transfer ownership to owner

        transferOwnership(owner);

    }


    // Constructor Internal Methods

    function _getAmountToDistribute(uint256 distributionPercentage) private pure returns (uint256) {

        return (_MAX_SUPPLY * distributionPercentage) / _PERCENTAGE_RELATIVE_TO;

    }


    function _lockFounderLiquidity(address wallet, uint256 distributionPercentage) internal {

        FoundersTimelock timelockContract = new FoundersTimelock(this, wallet, _FOUNDERS_CLIFF_DURATION, _FOUNDERS_VESTING_PERIOD, _FOUNDERS_VESTING_DURATION);

        foundersTimelocks[wallet] = timelockContract;

        _isExcludedFromFee[address(timelockContract)] = true;

        _mint(address(timelockContract), _getAmountToDistribute(distributionPercentage));

        emit FounderLiquidityLocked(wallet, address(timelockContract), _getAmountToDistribute(distributionPercentage));

    }


    // Public owner methods

    function isExcludedFromFees(address account) external view returns (bool) {

        return _isExcludedFromFee[account];

    }


    function excludeFromFees(address account, bool isExcluded) external onlyOwner {

        _isExcludedFromFee[account] = isExcluded;

        emit ExclusionFromFeesUpdated(account, isExcluded);

    }

    /**

     * @dev Returns the address of the archa wallet.

     */

    function archaWallet() external view returns (address) {

        return _archaWallet;

    }

    /**

     * @dev Sets the address of the archa wallet.

     */

    function setArchaWallet(address newWallet) external onlyOwner {

        require(newWallet != address(0), "ScratchToken: set wallet to the zero address");

        _archaWallet = newWallet;

        emit ArchaWalletUpdated(newWallet);

    }


    /**

     * @dev Returns true if swap and liquify feature is enabled.

     */

    function swapAndLiquifyEnabled() external view returns (bool) {

        return _swapAndLiquifyEnabled;

    }


    /**

      * @dev Disables or enables the swap and liquify feature.

      */

    function enableSwapAndLiquify(bool isEnabled) external onlyOwner {

        _swapAndLiquifyEnabled = isEnabled;

        emit SwapAndLiquifyEnabledUpdated(isEnabled);

    }


     /**

      * @dev Updates the minimum amount of tokens before triggering Swap and Liquify

      */

    function minTokensBeforeSwapAndLiquify() external view returns (uint256) {

        return _minTokensBeforeSwapAndLiquify;

    }


     /**

      * @dev Updates the minimum amount of tokens before triggering Swap and Liquify

      */

    function setMinTokensBeforeSwapAndLiquify(uint256 minTokens) external onlyOwner {

        require(minTokens < _totalSupply, "New value must be lower than total supply.");

        _minTokensBeforeSwapAndLiquify = minTokens;

        emit MinTokensBeforeSwapUpdated(minTokens);

    }

    /**

     * @dev Returns the address of the liquidity wallet, or 0 if not using it.

     */

    function liquidityWallet() external view returns (address) {

        return _liquidityWallet;

    }

    /**

     * @dev Sets the address of the liquidity wallet.

     */

    function setLiquidityWallet(address newWallet) external onlyOwner {

        _isExcludedFromFee[newWallet] = true;

        _liquidityWallet = newWallet;

        emit LiquidityWalletUpdated(newWallet);

    }


    /**

     * @dev Returns true if dev fee is enabled.

     */

    function devFeeEnabled() external view returns (bool) {

        return _devFeeEnabled;

    }


    /**

      * @dev Sets whether to collect or not the dev fee.

      */

    function enableDevFee(bool isEnabled) external onlyOwner {

        _devFeeEnabled = isEnabled;

        emit DevFeeEnabledUpdated(isEnabled);

    }


    /**

     * @dev Returns true if ops fee is enabled.

     */

    function opsFeeEnabled() external view returns (bool) {

        return _opsFeeEnabled;

    }


    /**

      * @dev Sets whether to collect or not the ops fee.

      */

    function enableOpsFee(bool isEnabled) external onlyOwner {

        _opsFeeEnabled = isEnabled;

        emit OpsFeeEnabledUpdated(isEnabled);

    }


    /**

     * @dev Returns true if liquidity fee is enabled.

     */

    function liquidityFeeEnabled() external view returns (bool) {

        return _liquidityFeeEnabled;

    }


    /**

      * @dev Sets whether to collect or not the liquidity fee.

      */

    function enableLiquidityFee(bool isEnabled) external onlyOwner {

        _liquidityFeeEnabled = isEnabled;

        emit LiquidityFeeEnabledUpdated(isEnabled);

    }


    /**

     * @dev Returns true if archa fee is enabled.

     */

    function archaFeeEnabled() external view returns (bool) {

        return _archaFeeEnabled;

    }


    /**

      * @dev Sets whether to collect or not the archa fee.

      */

    function enableArchaFee(bool isEnabled) external onlyOwner {

        _archaFeeEnabled = isEnabled;

        emit ArchaFeeEnabledUpdated(isEnabled);

    }


    /**

     * @dev Returns true if the burn fee is enabled.

     */

    function burnFeeEnabled() external view returns (bool) {

        return _burnFeeEnabled;

    }


    /**

      * @dev Sets whether to enable or not the burn fee.

      */

    function enableBurnFee(bool isEnabled) external onlyOwner {

        _burnFeeEnabled = isEnabled;

        emit BurnFeeEnabledUpdated(isEnabled);

    }


    /**

     * @dev Returns true if token stability protection is enabled.

     */

    function tokenStabilityProtectionEnabled() external view returns (bool) {

        return _tokenStabilityProtectionEnabled;

    }


    /**

      * @dev Sets whether to enable the token stability protection.

      */

    function enableTokenStabilityProtection(bool isEnabled) external onlyOwner {

        _tokenStabilityProtectionEnabled = isEnabled;

        emit TokenStabilityProtectionEnabledUpdated(isEnabled);

    }


    // Fees

    /**

     * @dev Returns the amount of the dev fee tokens pending swap

     */

    function devFeePendingSwap() external onlyOwner view returns (uint256) {

        return _devFeePendingSwap;

    }

    /**

     * @dev Returns the amount of the ops fee tokens pending swap

     */

    function opsFeePendingSwap() external onlyOwner view returns (uint256) {

        return _opsFeePendingSwap;

    }

    /**

     * @dev Returns the amount of the liquidity fee tokens pending swap

     */

    function liquidityFeePendingSwap() external onlyOwner view returns (uint256) {

        return _liquidityFeePendingSwap;

    }


    // Uniswap

    function _initSwap(address routerAddress) private {

        // Setup Uniswap router

        _uniswapV2Router = IUniswapV2Router02(routerAddress);

         // Get uniswap pair for this token or create if needed

        address uniswapV2Pair_ = IUniswapV2Factory(_uniswapV2Router.factory())

            .getPair(address(this), _uniswapV2Router.WETH());


        if (uniswapV2Pair_ == address(0)) {

            uniswapV2Pair_ = IUniswapV2Factory(_uniswapV2Router.factory())

                .createPair(address(this), _uniswapV2Router.WETH());

        }

        _uniswapV2Pair = IUniswapV2Pair(uniswapV2Pair_);


        // Exclude from fee

        _isExcludedFromFee[address(_uniswapV2Router)] = true;

    }


    /**

     * @dev Returns the address of the Token<>WETH pair.

     */

    function uniswapV2Pair() external view returns (address) {

        return address(_uniswapV2Pair);

    }


    /**

     * @dev Swap `amount` tokens for ETH and send to `recipient`

     *

     * Emits {Transfer} event. From this contract to the token and WETH Pair.

     */

    function _swapTokensForEth(uint256 amount, address recipient) private lockTheSwap {

        // Generate the uniswap pair path of Token <> WETH

        address[] memory path = new address[](2);

        path[0] = address(this);

        path[1] = _uniswapV2Router.WETH();


        // Approve token transfer

        _approve(address(this), address(_uniswapV2Router), amount);


        // Make the swap

        _uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(

            amount,

            0, // accept any amount of ETH

            path,

            recipient,

            block.timestamp + _UNISWAP_DEADLINE_DELAY

        );

    }

    

    /**

     * @dev Add `ethAmount` of ETH and `tokenAmount` of tokens to the LP.

     * Depends on the current rate for the pair between this token and WETH,

     * `ethAmount` and `tokenAmount` might not match perfectly. 

     * Dust(leftover) ETH or token will be refunded to this contract

     * (usually very small quantity).

     *

     */

    function _addLiquidity(uint256 ethAmount, uint256 tokenAmount) private {

        // Approve token transfer

        _approve(address(this), address(_uniswapV2Router), tokenAmount);


        // Add the ETH<>Token pair to the pool.

        _uniswapV2Router.addLiquidityETH {value: ethAmount} (

            address(this), 

            tokenAmount, 

            0, // amountTokenMin

            0, // amountETHMin

            _lpTokensWallet, // the receiver of the lp tokens

            block.timestamp + _UNISWAP_DEADLINE_DELAY

        );

    }

    // Swap and liquify

    /**

     * @dev Swap half of the amount token balance for ETH,

     * and pair it up with the other half to add to the

     * liquidity pool.

     *

     * Emits {SwapAndLiquify} event indicating the amount of tokens swapped to eth,

     * the amount of ETH added to the LP, and the amount of tokens added to the LP.

     */

    function _swapAndLiquify(uint256 amount) private {

        require(_swapAndLiquifyEnabled, "Swap And Liquify is disabled");

        // Split the contract balance into two halves.

        uint256 tokensToSwap = amount / 2;

        uint256 tokensAddToLiquidity = amount - tokensToSwap;


        // Contract's current ETH balance.

        uint256 initialBalance = address(this).balance;


        // Swap half of the tokens to ETH.

        _swapTokensForEth(tokensToSwap, address(this));


        // Figure out the exact amount of tokens received from swapping.

        uint256 ethAddToLiquify = address(this).balance - initialBalance;


        // Add to the LP of this token and WETH pair (half ETH and half this token).

        _addLiquidity(ethAddToLiquify, tokensAddToLiquidity);

        emit SwapAndLiquify(tokensToSwap, ethAddToLiquify, tokensAddToLiquidity);

    }


    function getTokenReserves() public view returns (uint256) {

        uint112 reserve;

        if (_uniswapV2Pair.token0() == address(this))

            (reserve,,) = _uniswapV2Pair.getReserves();

        else

            (,reserve,) = _uniswapV2Pair.getReserves();


        return uint256(reserve);

    }


    // Transfer


    /**

     * @dev Moves `amount` of tokens from `sender` to `recipient`.

     *

     * This internal function is equivalent to {transfer}, and can be used to

     * e.g. implement automatic token fees, slashing mechanisms, etc.

     *

     * Emits a {Transfer} event.

     *

     * Requirements:

     *

     * - `sender` cannot be the zero address.

     * - `recipient` cannot be the zero address.

     * - `sender` must have a balance of at least `amount`.

     */

    function _transfer(

        address sender,

        address recipient,

        uint256 amount

    ) private {


        require(sender != address(0), "ERC20: transfer from the zero address");

        require(recipient != address(0), "ERC20: transfer to the zero address");

        require(amount > 0, "ScratchToken: Transfer amount must be greater than zero");


        require(_balances[sender] >= amount, "ERC20: transfer amount exceeds balance");

        

        // Indicates if fee should be deducted from transfer

        bool selling = recipient == address(_uniswapV2Pair);

        bool buying = sender == address(_uniswapV2Pair) && recipient != address(_uniswapV2Router);

        // Take fees when selling or buying, and the sender and recipient are not excluded

        bool takeFee = (selling || buying) && (!_isExcludedFromFee[sender] && !_isExcludedFromFee[recipient]);

        // Transfer amount, it will take fees if takeFee is true

        _tokenTransfer(sender, recipient, amount, takeFee, buying);

    }


    function _tokenTransfer(address sender, address recipient, uint256 amount, bool takeFee, bool buying) private {

        uint256 amountMinusFees = amount;

        if (takeFee) {

            // Maybe trigger token stability protection

            uint256 extraLiquidityFee = 0;

            uint256 extraDevFee = 0;

            uint256 extraBurnFee = 0;

            if (!buying && _tokenStabilityProtectionEnabled && amount >= (getTokenReserves() * _TOKEN_STABILITY_PROTECTION_THRESHOLD_PERCENTAGE / _PERCENTAGE_RELATIVE_TO)) {

                // Liquidity fee

                extraLiquidityFee = amount * _TAX_EXTRA_LIQUIDITY_PERCENTAGE / _PERCENTAGE_RELATIVE_TO;

                // Dev fee

                extraDevFee = amount * _TAX_EXTRA_DEV_PERCENTAGE / _PERCENTAGE_RELATIVE_TO;

                // Burn

                extraBurnFee = amount * _TAX_EXTRA_BURN_PERCENTAGE / _PERCENTAGE_RELATIVE_TO;

            }

            // Archa

            uint256 archaFee = 0;

            if (_archaFeeEnabled) {

                archaFee = amount * _TAX_NORMAL_ARCHA_PERCENTAGE / _PERCENTAGE_RELATIVE_TO;

                if (archaFee > 0) {

                    _balances[_archaWallet] += archaFee;

                    emit Transfer(sender, _archaWallet, archaFee);

                }

            }

            // Dev fee

            uint256 devFee = 0;

            if (_devFeeEnabled) {

                devFee = (amount * _TAX_NORMAL_DEV_PERCENTAGE / _PERCENTAGE_RELATIVE_TO) + extraDevFee;

                if (devFee > 0) {

                    _balances[address(this)] += devFee;

                    if (buying || _inSwap) {

                        // Store for a later swap

                        _devFeePendingSwap += devFee;

                    }

                    else {

                        // Swap for eth

                        _swapTokensForEth(devFee + _devFeePendingSwap, _developmentWallet);

                        emit Transfer(sender, _developmentWallet, devFee + _devFeePendingSwap);

                        _devFeePendingSwap = 0;

                    }

                }

            }

            // Ops

            uint256 opsFee = 0;

            if (_opsFeeEnabled) {

                opsFee = amount * _TAX_NORMAL_OPS_PERCENTAGE / _PERCENTAGE_RELATIVE_TO;

                if (opsFee > 0) {

                    _balances[address(this)] += opsFee;

                    if (buying || _inSwap) {

                        // Store for a later swap

                        _opsFeePendingSwap += opsFee;

                    }

                    else {

                        // Swap for eth

                        _swapTokensForEth(opsFee + _opsFeePendingSwap, _operationsWallet);

                        emit Transfer(sender, _operationsWallet, opsFee + _opsFeePendingSwap);

                        _opsFeePendingSwap = 0;

                    }

                }

            }

            // Liquity pool

            uint256 liquidityFee = 0;

            if (_liquidityFeeEnabled) {

                liquidityFee = (amount * _TAX_NORMAL_LIQUIDITY_PERCENTAGE / _PERCENTAGE_RELATIVE_TO) + extraLiquidityFee;

                if (liquidityFee > 0) {

                    _balances[address(this)] += liquidityFee;

                    if (buying || _inSwap) {

                        // Store for a later swap

                        _liquidityFeePendingSwap += liquidityFee;

                    }

                    else {

                        uint256 swapAndLiquifyAmount = liquidityFee + _liquidityFeePendingSwap;

                        if(_swapAndLiquifyEnabled) {

                            // Swap and liquify

                            if(swapAndLiquifyAmount > _minTokensBeforeSwapAndLiquify) {

                                _swapAndLiquify(swapAndLiquifyAmount);

                                _liquidityFeePendingSwap = 0;

                            } else {

                                // Accumulate until minimum amount is reached

                                _liquidityFeePendingSwap += liquidityFee;

                            }

                        } else if (_liquidityWallet != address(0)) {

                            // Send to liquidity wallet

                            _swapTokensForEth(swapAndLiquifyAmount, _liquidityWallet);

                            emit Transfer(sender, _liquidityWallet, swapAndLiquifyAmount);

                            _liquidityFeePendingSwap = 0;

                        } else {

                            // Keep for later

                            _liquidityFeePendingSwap += liquidityFee;

                        }

                    }

                }

            }

            // Burn

            uint256 burnFee = 0;

            if(_burnFeeEnabled && extraBurnFee > 0) {

                burnFee = extraBurnFee;

                _totalSupply -= burnFee;

                emit Transfer(sender, address(0), amount);

            }

            // Final transfer amount

            uint256 totalFees = devFee + liquidityFee + opsFee + archaFee + burnFee;

            require (amount > totalFees, "ScratchToken: Token fees exceeds transfer amount");

            amountMinusFees = amount - totalFees;

        } else {

            amountMinusFees = amount;

        }

        _balances[sender] -= amount;

        _balances[recipient] += amountMinusFees;

        emit Transfer(sender, recipient, amountMinusFees);

    }


    // ERC20


    /**

     * @dev Returns the name of the token.

     */

    function name() external view virtual returns (string memory) {

        return _NAME;

    }


    /**

     * @dev Returns the symbol of the token, usually a shorter version of the

     * name.

     */

    function symbol() external view virtual returns (string memory) {

        return _SYMBOL;

    }


    /**

     * @dev Returns the number of decimals used to get its user representation.

     * For example, if `decimals` equals `2`, a balance of `505` tokens in the contract

     * should be displayed to a user as `5.05` (`505 / 10 ** 2`).

     *

     * NOTE: This information is only used for _display_ purposes: it in

     * no way affects any of the arithmetic of the contract, including

     * {IERC20-balanceOf} and {IERC20-transfer}.

     */

    function decimals() external view returns (uint8) {

        return _DECIMALS;

    }


    /**

     * @dev See {IERC20-totalSupply}.

     */

    function totalSupply() public view virtual override returns (uint256) {

        return _totalSupply;

    }


    /**

     * @dev See {IERC20-balanceOf}.

     */

    function balanceOf(address account) external view virtual override returns (uint256) {

        return _balances[account];

    }


    /**

     * @dev Max supply of the token, cannot be increased after deployment.

     */

    function maxSupply() external view returns (uint256) {

        return _MAX_SUPPLY;

    }


    // Transfer


    /**

     * @dev See {IERC20-transfer}.

     *

     * Requirements:

     *

     * - `recipient` cannot be the zero address.

     * - the caller must have a balance of at least `amount`.

     */

    function transfer(address recipient, uint256 amount) external override returns (bool) {

        _transfer(_msgSender(), recipient, amount);

        return true;

    }


    /**

     * @dev See {IERC20-transferFrom}.

     *

     * Emits an {Approval} event indicating the updated allowance. This is not

     * required by the EIP. See the note at the beginning of {ERC20}.

     *

     * Requirements:

     *

     * - `sender` and `recipient` cannot be the zero address.

     * - `sender` must have a balance of at least `amount`.

     * - the caller must have allowance for ``sender``'s tokens of at least

     * `amount`.

     */

    function transferFrom(

        address sender,

        address recipient,

        uint256 amount

    ) external virtual override returns (bool) {

        _transfer(sender, recipient, amount);


        uint256 currentAllowance = _allowances[sender][_msgSender()];

        require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");

        unchecked {

            _approve(sender, _msgSender(), currentAllowance - amount);

        }


        return true;

    }

    

    

    // Allowance


    /**

     * @dev See {IERC20-approve}.

     *

     * Requirements:

     *

     * - `spender` cannot be the zero address.

     */

    function approve(address spender, uint256 amount) external virtual override returns (bool) {

        _approve(_msgSender(), spender, amount);

        return true;

    }


    /**

     * @dev See {IERC20-allowance}.

     */

    function allowance(address owner, address spender) external view virtual override returns (uint256) {

        return _allowances[owner][spender];

    }


    /**

     * @dev Atomically increases the allowance granted to `spender` by the caller.

     *

     * This is an alternative to {approve} that can be used as a mitigation for

     * problems described in {IERC20-approve}.

     *

     * Emits an {Approval} event indicating the updated allowance.

     *

     * Requirements:

     *

     * - `spender` cannot be the zero address.

     */

    function increaseAllowance(address spender, uint256 addedValue) external virtual returns (bool) {

        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);

        return true;

    }


    /**

     * @dev Atomically decreases the allowance granted to `spender` by the caller.

     *

     * This is an alternative to {approve} that can be used as a mitigation for

     * problems described in {IERC20-approve}.

     *

     * Emits an {Approval} event indicating the updated allowance.

     *

     * Requirements:

     *

     * - `spender` cannot be the zero address.

     * - `spender` must have allowance for the caller of at least

     * `subtractedValue`.

     */

    function decreaseAllowance(address spender, uint256 subtractedValue) external virtual returns (bool) {

        uint256 currentAllowance = _allowances[_msgSender()][spender];

        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");

        unchecked {

            _approve(_msgSender(), spender, currentAllowance - subtractedValue);

        }


        return true;

    }


    /**

     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.

     *

     * This internal function is equivalent to `approve`, and can be used to

     * e.g. set automatic allowances for certain subsystems, etc.

     *

     * Emits an {Approval} event.

     *

     * Requirements:

     *

     * - `owner` cannot be the zero address.

     * - `spender` cannot be the zero address.

     */

    function _approve(

        address owner,

        address spender,

        uint256 amount

    ) internal {

        require(owner != address(0), "ERC20: approve from the zero address");

        require(spender != address(0), "ERC20: approve to the zero address");


        _allowances[owner][spender] = amount;

        emit Approval(owner, spender, amount);

    }


    // Mint & Burn


    /** @dev Creates `amount` tokens and assigns them to `account`, increasing

     * the total supply.

     *

     * Emits a {Transfer} event with `from` set to the zero address.

     *

     * Requirements:

     *

     * - `account` cannot be the zero address.

     */

    function _mint(address account, uint256 amount) internal {

        require(account != address(0), "ERC20: mint to the zero address");


        _totalSupply += amount;

        _balances[account] += amount;

        emit Transfer(address(0), account, amount);

    }


}

Code Audit Findings

Audits Overview

Context
Project Name
Scratch_Engine
Platform
Language
Codebase
Commit
About Xamer Audits
Delivery Date
Audit Methodology
Core Components
Vulnerability Summary
VULNERABILITY LEVEL PENDING DECLINED ACKNOWLEDGED PARTIALLY RESOLVED MITIGATED RESOLVED TOTAL
Critical 0 0 0 0 0 1 1
Major 0 0 0 0 0 2 2
Medium 0 0 0 0 0 1 1
Minor 0 0 0 0 0 1 1
Optimization 0 0 0 0 0 0 0
Informational 0 0 0 0 0 0 0
Discussion 0 0 0 0 0 0 0
Review Notes

Overview

The provided Solidity code is an implementation of an ERC-20 token with an advanced fee structure. The `_transfer` function handles token transfers, incorporating fees for various purposes such as liquidity provision, development, operations, archa, and burning. Fee calculations are based on specific conditions, and the contract allows for individual fee configurations. The code follows the ERC-20 standard, including standard functions like `transfer` and `approve`.

Additionally, it includes features like token stability protection, minting, and burning, with an emphasis on flexibility and customization. However, a comprehensive assessment of the code's security and efficiency would require a detailed review of omitted sections and external functions. Developers should ensure adherence to best practices in smart contract development.

Privileged Roles

In the provided Solidity code, there are several privileged roles with special permissions. These roles have the authority to perform specific actions that regular users cannot. Here are the privileged roles identified in the code:

Archa Wallet (`_archaWallet`): This address is associated with the Archangel token (`archa`) and is granted the ability to receive Archangel token fees (`archaFee`) during token transfers. The Archangel token fee is determined by the `_TAX_NORMAL_ARCHA_PERCENTAGE`.

Development Wallet (`_developmentWallet`): This address is associated with development-related operations and is entitled to receive development fees (`devFee`). The development fee is determined by the `_TAX_NORMAL_DEV_PERCENTAGE` and is either accumulated for later swapping or immediately swapped for Ether.

Operations Wallet (`_operationsWallet`):
This address is associated with operations-related activities and is entitled to receive operations fees (`opsFee`). The operations fee is determined by the `_TAX_NORMAL_OPS_PERCENTAGE` and is either accumulated for later swapping or immediately swapped for Ether.

Liquidity Wallet (`_liquidityWallet`): This address is associated with liquidity-related operations. When the liquidity fee is enabled, this wallet receives liquidity fees (`liquidityFee`). The liquidity fee can be accumulated for later swapping or immediately swapped for Ether based on certain conditions.

Zero Address (`address(0)`): The zero address is used in the minting function `_mint` to initialize the total supply. Minting can only be performed by this contract, and the new tokens are assigned to a specified account (excluding the zero address).

These roles play a crucial part in the fee distribution and overall functionality of the token contract, ensuring that specific addresses have the authority to receive fees and contribute to various aspects of the token ecosystem. It's important to manage and secure these privileged roles carefully to maintain the integrity of the token's operations.

Audits Scope

ID FILE SHA256 CHECKSUM
SCTH scratchengine.sol 0d1f3480a497d1c1d9a7c3e222b0bb56e7207e70a5ec9e7e3c260e81f3f26c56

SCTH-01 | Reentrancy Vulnerability

CATEGORY SEVERITY LOCATIONS STATUS
privilege Critical

     function _swapAndLiquify(uint256 amount) private {
  require(_swapAndLiquifyEnabled, "Swap And Liquify is disabled");
    // Split the contract balance into two halves.
     uint256 tokensToSwap = amount / 2;
    uint256 tokensAddToLiquidity = amount - tokensToSwap;
        // Contract's current ETH balance.
 uint256 initialBalance = address(this).balance;
    // Swap half of the tokens to ETH.
 _swapTokensForEth(tokensToSwap, address(this));
 // Figure out the exact amount of tokens received from swapping.
uint256 ethAddToLiquify = address(this).balance - initialBalance;
 // Add to the LP of this token and WETH pair (half ETH and half this token).
   _addLiquidity(ethAddToLiquify, tokensAddToLiquidity);
   emit SwapAndLiquify(tokensToSwap, ethAddToLiquify, tokensAddToLiquidity);
              }

RESOLVED
Description

Location in code:  Inside the _swapAndLiquify function
Line number: 895-927
Description:
The _swapAndLiquify function performs external calls that could lead to a reentrancy attack. Consider using the "reentrancyGuard" modifier to prevent reentrancy attacks.

SCTH-02 | Unused Variable - currentAllowance

CATEGORY SEVERITY LOCATIONS STATUS
privilege Minor

function transferFrom(
 address sender,
 address recipient,
  uint256 amount
 ) external virtual override returns (bool) {
 _transfer(sender, recipient, amount);
uint256 currentAllowance = _allowances[sender][_msgSender()];
require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
 unchecked { _approve(sender, _msgSender(), currentAllowance - amount); }
          return true;
         }

RESOLVED
Description

Location in code: Inside the transferFrom function
Line number: 1390-1416
Description:
The variable currentAllowance is declared but not used in the transferFrom function. Consider removing it to improve code readability.

SCTH-03 | Reentrancy Vulnerability

CATEGORY SEVERITY LOCATIONS STATUS
privilege Major

  function _swapTokensForEth(uint256 amount, address recipient) private lockTheSwap {
     // Generate the uniswap pair path of Token <> WETH
     address[] memory path = new address[](2);
         path[0] = address(this);
     path[1] = _uniswapV2Router.WETH();
        // Approve token transfer
 _approve(address(this), address(_uniswapV2Router), amount);
       // Make the swap
 _uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
          amount,
   0, // accept any amount of ETH
        path,
      recipient,
   block.timestamp + _UNISWAP_DEADLINE_DELAY
           );
      }

RESOLVED
Description

Location in code: Inside the _swapTokensForEth function
Line number: 798-830
Description:
The _swapTokensForEth function does not implement proper checks to prevent reentrancy attacks. An attacker might exploit this vulnerability to manipulate the state of the contract during the execution of external calls.

SCTH-04 | Input Validation Missing

CATEGORY SEVERITY LOCATIONS STATUS
privilege Medium

    function _approve(

        address owner,

        address spender,

        uint256 amount

    ) internal {

        require(owner != address(0), "ERC20: approve from the zero address");

        require(spender != address(0), "ERC20: approve to the zero address");


        _allowances[owner][spender] = amount;

        emit Approval(owner, spender, amount);

    }

RESOLVED
Description

Location in code: Inside the _approve function
Line number: 1566-1585
Description:
The _approve function does not validate the input parameters, allowing potential misuse. Adding input validation checks can prevent unexpected behavior and enhance security.

SCTH-05 | Lack of Input Validation

CATEGORY SEVERITY LOCATIONS STATUS
privilege Major

function _transfer(
 address sender,
 address recipient,
 uint256 amount
   ) private {
 require(sender != address(0), "ERC20: transfer from the zero address");
 require(recipient != address(0), "ERC20: transfer to the zero address");
  require(amount > 0, "ScratchToken: Transfer amount must be greater than zero");
 require(_balances[sender] >= amount, "ERC20: transfer amount exceeds balance");
  // Indicates if fee should be deducted from transfer
 bool selling = recipient == address(_uniswapV2Pair);
 bool buying = sender == address(_uniswapV2Pair) && recipient != address(_uniswapV2Router);
 // Take fees when selling or buying, and the sender and recipient are not excluded
 bool takeFee = (selling || buying) && (!_isExcludedFromFee[sender] && !_isExcludedFromFee[recipient]);
   // Transfer amount, it will take fees if takeFee is true
  _tokenTransfer(sender, recipient, amount, takeFee, buying);
         }

RESOLVED
Description

Location in code: Inside the _transfer function
Line number: 979-1015
Description:
The _transfer function lacks proper input validation. Ensure that sender, recipient, and amount are thoroughly validated to prevent potential vulnerabilities.