Arbiswap Rugpull Investigation

Arbiswap ($ARBI) rug pull about $132,000 worth of losses.

SECURI LAB incidence response and red team experts have been following up on the Arbiswap ($ARBI) rug pull incident.

The event occurred around March 3, 2023 (UTC+7).
The creator of the Arbiswap Contract (contract creator 0x8a60f91178da2F9de3D7a825380B7CE03933724F) performed a rug pull (details under investigation) and transferred ~84.6 anyETH to the Multichain Router for a bridge to Ethereum, delivered to the destination. 0x8a60f91178da2F9de3D7a825380B7CE03933724F This is the same address that was on Ethereum, after which money was laundered through the Tornado Cash Router.

[Confirmation Scenario] Arbiswap rugpull with mint function

Contract Creator has used the mint function that’s our Warning Avoidance System (WAS) has detected a critical function on ArbiToken Contract on Lines: 847–850, 888–894, 959–962

    function mint(uint256 amount) public onlyOwner returns (bool) {
_mint(_msgSender(), amount);
return true;
    function _mint(address account, uint256 amount) internal {
require(account != address(0), 'BEP20: mint to the zero address');

_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
    function mint(address _to, uint256 _amount) public onlyOwner {
_mint(_to, _amount);
_moveDelegates(address(0), _delegates[_to], _amount);

This is a scenario for minting around 1,000,000,000,000 ARBI and then dump with swap exact

and In SmartChefInitializable Our SECURI LAB Auto WAS (Warning Avoidance System) We find the existence of a function solidity-upgradable-constructor [High Severity] This may be the reason why constructor data can be changed.


function initialize(
IERC20Metadata _stakedToken,
IERC20Metadata _rewardToken,
uint256 _rewardPerSec,
uint256 _startTime,
uint256 _bonusEndTime
) external onlyOwner {
require(!isInitialized, "Already initialized");

// Make this contract initialized
isInitialized = true;

stakedToken = _stakedToken;
rewardToken = _rewardToken;
rewardPerSec = _rewardPerSec;
startTime = _startTime;
bonusEndTime = _bonusEndTime;

uint256 decimalsRewardToken = uint256(rewardToken.decimals());
require(decimalsRewardToken < 30, "Must be less than 30");

PRECISION_FACTOR = uint256(10**(uint256(30) - decimalsRewardToken));

// Set the lastRewardTime as the startTime
lastRewardTime = startTime;

And we found the presence of the DelegateCall function, which may cause the function to be called somewhere non-contract SmartChefInitializable.sol:424

function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");

(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);

About SECURI LAB (Thailand)

As a team of highly skilled cybersecurity experts, SECURI LAB was established in 2018. Our team of security researchers has amassed over three years of expertise, and we have a strong foundation as consultants for organizations seeking to enhance their cybersecurity measures. Utilizing only the most reliable and industry-leading inspection tools, we strive to deliver the most comprehensive and effective solutions for our clients.