0x840b0dc64dbb91e8aba524f67189f639a0bc94ee9256c57d79083bb3fd46ec910x326FB70eF9e70f8f4c38CFbfaF39F960A5C252faBSCMatez Staking Program on BSC allows stake() to downcast the requested stake amount to uint128 for transfer amount calculation while recording the original uint256 amount in multiple accounting fields. This mismatch can be abused with amnt = 2^128, producing zero token movement into the contract but very large accounting values, satisfying reward gates and enabling unauthorized MATEZ payouts from protocol reserves.
MatezStakingProgram at 0x326fb70ef9e70f8f4c38cfbfaf39f960a5c252fa (chain 56).MATEZ at 0x010c0d77055a26d09bb474ef8d81975f55bd8fc9.0x840b0dc64dbb91e8aba524f67189f639a0bc94ee9256c57d79083bb3fd46ec91 on BSC.The root cause is a type-conversion mismatch between the token transfer amount and reward gating/accounting amount. In stake, estimateAmountOut(..., uint128(amnt), 1) is called before transferFrom, but all of the accounting updates use full-width uint256 amnt. For amnt = 2^128, Solidity truncates to zero in the conversion, so actual transfer is zero while the contract records a large synthetic value.
uint128This synthetic accounting inflates:
selfInvestdirectInvestteamInvestamountupdateDisThat synthetic state is later used by claim(3, pkgid, amount) gating checks, allowing reward release with almost no real capital input.
stake(uint256 amnt) requires the caller to be registered and then computes:uint256 amntin = estimateAmountOut(address(token1), uint128(amnt), 1);
depositToken.transferFrom(msg.sender, address(this), amntin);
When amnt is 2^128, uint128(amnt) == 0, so transferFrom moves 0 MATEZ.
amnt (full width):users[msg.sender].selfInvest += amnt;
users[sponsor].directInvest += amnt;
orders[msg.sender][o_id].amount = amnt;
updateDis(msg.sender, amnt);
This creates a mismatch between external token balance and internal accounting.
updateDis propagates that inflated amnt across sponsor lineage:users[sponsor].teamInvest += _pkg;
for up to 30 levels, widening the synthetic qualification footprint.
claim(3, pkgid, amount) requires threshold checks such as:require(users[msg.sender].selfInvest >= selfBrequired[pkgid], ...);
require(users[msg.sender].teamInvest >= businessTrequired[pkgid], ...);
require(users[msg.sender].directs >= teamDrequired[pkgid], ...);
require(users[msg.sender].directInvest >= businessDrequired[pkgid], ...);
The zero-asset stakers can still satisfy these due to inflated internal values.
uint256 amntin = estimateAmountOut(address(token1),uint128(reward[pkgid]),1);
depositToken.transfer(msg.sender, amntin);
The seed trace and balance diff confirm this: protocol reserve balance decreases by
67492228104191350600 MATEZ units.
0xd4f04374385341da7333b82b230cd223143c4d62) orchestrates deployment/interaction through contract 0x0ad02ce1b8eb978fd8dc4abec5bf92dfa81ed705.0x10ed...02fec06.stake(2^128) on root and many direct children.claim(3, 4, 0) and receive payout from MATEZ reserve.67,492,227,104,191,350,600 MATEZ (18 decimals) in the seed transaction./workspace/session/artifacts/collector/seed/56/0x840b0dc64dbb91e8aba524f67189f639a0bc94ee9256c57d79083bb3fd46ec91/metadata.json/workspace/session/artifacts/collector/seed/56/0x840b0dc64dbb91e8aba524f67189f639a0bc94ee9256c57d79083bb3fd46ec91/trace.cast.log/workspace/session/artifacts/collector/seed/56/0x840b0dc64dbb91e8aba524f67189f639a0bc94ee9256c57d79083bb3fd46ec91/balance_diff.json/tmp/MatezStakingProgram.sol/workspace/session/artifacts/auditor/oracle_definition.json/workspace/session/poc/test/Exploit.sol and /workspace/session/artifacts/validator/forge-test.log (post-validator run)