112 lines
3.1 KiB
Solidity
112 lines
3.1 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
|
|
pragma solidity 0.8.28;
|
|
|
|
import "./lib/forge-std/src/interfaces/IERC20.sol";
|
|
|
|
contract knlWallet {
|
|
struct Transaction {
|
|
address destination;
|
|
uint256 value;
|
|
bool executed;
|
|
address tokenAddress;
|
|
}
|
|
|
|
address[] public owners;
|
|
uint256 public required;
|
|
|
|
uint256 public transactionCount;
|
|
mapping(uint256 => Transaction) public transactions;
|
|
mapping(uint256 => mapping(address => bool)) public confirmations;
|
|
|
|
constructor(address[] memory _owners, uint256 _confirmations) {
|
|
require(_owners.length > 0, "need to have owners");
|
|
require(_confirmations > 0, "need to have a number of confirmation");
|
|
require(
|
|
_confirmations <= _owners.length,
|
|
"need to have more or equals owners than confirmation"
|
|
);
|
|
|
|
owners = _owners;
|
|
required = _confirmations;
|
|
}
|
|
|
|
modifier isOwner() {
|
|
for (uint256 i = 0; i < owners.length; i++) {
|
|
if (owners[i] == msg.sender) {
|
|
_;
|
|
return;
|
|
}
|
|
}
|
|
revert("need to be owner");
|
|
}
|
|
|
|
receive() external payable { }
|
|
|
|
function isConfirmed(uint256 transactionId) public view returns (bool) {
|
|
return getConfirmationsCount(transactionId) >= required;
|
|
}
|
|
|
|
function getConfirmationsCount(uint256 transactionId)
|
|
public
|
|
view
|
|
returns (uint256)
|
|
{
|
|
uint256 count;
|
|
for (uint256 i = 0; i < owners.length; i++) {
|
|
if (confirmations[transactionId][owners[i]]) {
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
function addTransaction(
|
|
address destination,
|
|
uint256 value,
|
|
address tokenAddress
|
|
) public isOwner returns (uint256) {
|
|
transactions[transactionCount] =
|
|
Transaction(destination, value, false, tokenAddress);
|
|
|
|
transactionCount += 1;
|
|
return transactionCount - 1;
|
|
}
|
|
|
|
function confirmTransaction(uint256 transactionId) public isOwner {
|
|
confirmations[transactionId][msg.sender] = true;
|
|
if (isConfirmed(transactionId)) {
|
|
executeTransaction(transactionId);
|
|
}
|
|
}
|
|
|
|
function submitTransaction(
|
|
address payable dest,
|
|
uint256 value,
|
|
address tokenAddress
|
|
) external isOwner {
|
|
uint256 id = addTransaction(dest, value, tokenAddress);
|
|
confirmTransaction(id);
|
|
}
|
|
|
|
function executeTransaction(uint256 transactionId) public isOwner {
|
|
require(isConfirmed(transactionId), "transaction has not been confirmed");
|
|
Transaction storage _tx = transactions[transactionId];
|
|
|
|
if (_tx.tokenAddress == address(0)) {
|
|
// Handle Ether transfer
|
|
(bool success,) = _tx.destination.call{ value: _tx.value }("");
|
|
require(success);
|
|
} else {
|
|
// Handle ERC-20 token transfer
|
|
IERC20 token = IERC20(_tx.tokenAddress);
|
|
require(
|
|
token.transfer(_tx.destination, _tx.value),
|
|
"Failed to transfer ERC20 tokens"
|
|
);
|
|
}
|
|
|
|
_tx.executed = true;
|
|
}
|
|
}
|