Do you see the vulnerability inside this digital wallet?

Hello Hackers!

Last week I was analyzing a smart contract on behalf of a customer, which had already been scanned by an AI validator.

With my big surprise, there was a trivial and well exposed vulnerability, and I had hard time to believe the AI scanner missed it. So, I tried to pass it through ChatGPT 5 (and others generic LLMs)… and still, undetected.

I don’t know why, maybe there is lack of Solidity code in the dataset, compared to all the oldest and well known code.

Anyways, I thought it could be a nice exercise to share the code (anonymized, of course) and ask you: *can you spot the vulnerability?*

Even if you don’t know solidity… it’s a logic issue!

“` contract MultiSigWallet { address[] public members; uint public minApprovals;

struct Tx { address recipient; uint amount; bool done; uint approvals; } Tx[] public txs; mapping(uint => mapping(address => bool)) public approved; constructor(address[] memory _members, uint _minApprovals) { require(_members.length > 0); require(_minApprovals > 0 && _minApprovals <= _members.length); members = _members; minApprovals = _minApprovals; } modifier onlyMember() { bool found = false; for (uint i = 0; i < members.length; i++) { if(members[i] == msg.sender) { found = true; break; } } require(found, "Not a member"); _; } modifier noDuplicate(address _member) { for (uint i = 0; i < members.length; i++) { require(members[i] != _member, "Member exists"); } _; } modifier txExists(uint _txId) { require(_txId < txs.length); _; } modifier notDone(uint _txId) { require(!txs[_txId].done); _; } modifier notApproved(uint _txId) { require(!approved[_txId][msg.sender]); _; } receive() external payable {} function submitTx(address _recipient, uint _amount) external onlyMember { txs.push(Tx({ recipient: _recipient, amount: _amount, done: false, approvals: 0 })); } function approveTx(uint _txId) external onlyMember txExists(_txId) notDone(_txId) notApproved(_txId) { approved[_txId][msg.sender] = true; txs[_txId].approvals += 1; if (txs[_txId].approvals >= minApprovals) { executeTx(_txId); } } function executeTx(uint _txId) internal txExists(_txId) notDone(_txId) { Tx storage t = txs[_txId]; require(t.approvals >= minApprovals); t.done = true; (bool success, ) = t.recipient.call{value: t.amount}(""); require(success); } function changeMinApprovals(uint _minApprovals) external onlyMember { require(_minApprovals > 0 && _minApprovals <= members.length); minApprovals = _minApprovals; } function getMembers() external view returns (address[] memory) { return members; } 

}

“`

I’ll let you have fun, then reveal the solution in the comments! 😉

Enjoy,
Francesco

submitted by /u/fcarlucci
[link] [comments]

January 11, 2026
Read More >>