Hi thecil,
After playing in remix with variants A and B, I realized that B is impossible to implement. I mean, if you keep funds in an external address, you cannot retrieve value from that external address.
For this reason, all funds are kept in the smart contractâs address itself. Every payable function adds funds to this smart contract address. address(this)
is the current smart contractâs address, msg.value
represents how much value was sent, msg.sender
represents the sender address.
Iâve programed, in Remix, a basic ContractA implementation to solidify the knowledge.
pragma solidity 0.7.5;
contract ContractA {
uint256 balance;
function addFunds() public payable {
balance += msg.value;
}
function getFunds() public view returns(uint256) {
return balance;
}
function getContractFunds() public view returns(uint256) {
return address(this).balance;
}
function withdrawFunds() public {
msg.sender.transfer(balance);
balance = 0;
}
}
Basically, getFunds
and getContractFunds
retrieve the same result, so you donât need the balance
variable.
With these observations in mind, Iâve come to the following version:
pragma solidity 0.7.5;
contract MultiSignWallet {
uint transferId = 0;
address[] ownwers;
uint nrApprovals;
struct TransferRequest {
uint ammount;
address payable sendAddress;
bool paid;
}
mapping(uint => TransferRequest) transferRequests;
mapping(uint => address[]) signaturesPerTransferRequest;
constructor(address[] memory _owners, uint _nrApprovals) {
require(_owners.length > 0, "Please add at least one owner");
require(_nrApprovals > 0, "There should be at least one approval.");
require(_nrApprovals <= _owners.length, "The number of approvals should be at most the number of owners");
ownwers = _owners;
nrApprovals = _nrApprovals;
}
// returns transfer id
function createTransferRequest(uint _ammount, address payable _toAddress) public returns (uint) {
require(addressIsOwner(msg.sender), "A transfer request can only be performed by an owner");
TransferRequest memory transfer = TransferRequest(_ammount, _toAddress, false);
uint currentTransferId = transferId;
transferId++;
transferRequests[currentTransferId] = transfer;
signaturesPerTransferRequest[currentTransferId].push(msg.sender);
if (signaturesPerTransferRequest[currentTransferId].length >= nrApprovals) {
payTransferRequest(currentTransferId);
}
return currentTransferId;
}
function retrieveTransferRequest(uint _id) public view returns (uint, address, bool) {
return (transferRequests[_id].ammount, transferRequests[_id].sendAddress, transferRequests[_id].paid);
}
// returns true if the necessy number of sgnatures has been reached
function signTransferRequest(uint _transferId) public returns(bool) {
require(transferRequests[_transferId].ammount > 0, "Cound not find a transfer with this id.");
require(!transferRequests[_transferId].paid, "This transfer request is already paid.");
require(addressIsOwner(msg.sender), "Only an owner can sign the transfer request");
bool callerAlreadySigned = false;
for(uint i = 0; i < signaturesPerTransferRequest[_transferId].length; i++) {
if (msg.sender == signaturesPerTransferRequest[_transferId][i]) {
callerAlreadySigned = true;
break;
}
}
require(!callerAlreadySigned, "A owner cannot sign a transfer request multiple times.");
signaturesPerTransferRequest[_transferId].push(msg.sender);
if (signaturesPerTransferRequest[_transferId].length >= nrApprovals) {
payTransferRequest(_transferId);
return true;
}
return false;
}
function addFunds() public payable {
require(addressIsOwner(msg.sender), "Only an owner can add funds.");
}
function getAvailableFunds() public view returns (uint256) {
return address(this).balance;
}
function addressIsOwner(address _caller) private view returns(bool) {
for(uint i = 0; i < ownwers.length; i++) {
if (_caller == ownwers[i]) {
return true;
}
}
return false;
}
function payTransferRequest(uint _id) private {
require(address(this).balance >= transferRequests[_id].ammount, "The contract does not as enough funds to complete the transfer.");
transferRequests[_id].sendAddress.transfer(transferRequests[_id].ammount);
transferRequests[_id].paid = true;
}
}
Thank you again for your patience in examining my code! I could not do the âFlipCoinâ homework (or probably any other app) without figuring out this basic idea.