Here’s my contract. The biggest roadblock was figuring out how to have a mapping in a struct. More specifically creating instances of the struct when one of its elements is a mapping. I have it working after some research but the approach seems a little roundabout and could probably be improved. Is there a better way to handle that?
//SPDX-License-Identifier: MIT
pragma solidity 0.7.5.;
/**
We haven't gotten into actually signing transactions
so I've implemented this with a more simple approval pattern.
I hope that works.
*/
import "hardhat/console.sol";
contract MultiSigWallet {
mapping(address => bool) approvers;
uint private requiredApprovalCount;
struct TransferRequest {
uint amount;
address payable toAddress;
string description;
address creator;
mapping(address => bool) approvers;
uint approvalCount;
bool isComplete;
bool exists;
}
mapping(uint => TransferRequest) public transferRequests;
uint transferRequestCount;
modifier onlyApprover {
require(approvers[msg.sender], "address is not an approver");
_;
}
modifier requestIndexIsInBounds(uint _requestIndex) {
require(transferRequests[_requestIndex].exists, "transfer request doesn't exist");
_;
}
constructor(address[] memory _approvers, uint _requiredApprovalCount) {
console.log("Creating contract");
// Add approvers
for(uint i = 0; i < _approvers.length; i++) {
approvers[_approvers[i]] = true;
}
requiredApprovalCount = _requiredApprovalCount;
console.log("contract created");
}
function deposit() public payable {
console.log("deposit of %d complete", msg.value);
}
function createTransferRequest(uint _amount, address payable _toAddress, string memory _description) public onlyApprover {
// TODO: don't allow duplicate requests
TransferRequest storage request = transferRequests[transferRequestCount++];
request.amount = _amount;
request.toAddress = _toAddress;
request.description = _description;
request.creator = msg.sender;
request.approvers[msg.sender] = true;
request.approvalCount = 1;
request.isComplete = false;
request.exists = true;
console.log("transfer request created");
}
function approveRequest(uint _requestIndex) public onlyApprover requestIndexIsInBounds(_requestIndex) {
// Make sure request hasn't already been completed
require(! transferRequests[_requestIndex].isComplete, "request has already completed");
// Only allow one approval per approver
require(! transferRequests[_requestIndex].approvers[msg.sender], "address already approved request");
transferRequests[_requestIndex].approvers[msg.sender] = true;
transferRequests[_requestIndex].approvalCount++;
console.log("approval of request #$d complete", _requestIndex);
}
function completeTransfer(uint _requestIndex) public onlyApprover requestIndexIsInBounds(_requestIndex) {
require(transferRequests[_requestIndex].approvalCount >= requiredApprovalCount, "request doesn't have enough approvals" );
require(transferRequests[_requestIndex].amount <= address(this).balance, "insufficient balance");
transferRequests[_requestIndex].toAddress.transfer(transferRequests[_requestIndex].amount);
transferRequests[_requestIndex].isComplete = true;
console.log("transfer complete");
}
function getBalance() view public returns(uint) {
return address(this).balance;
}
}