great. glad i could help. and no worries.
My final version:
I’d love to have your opinion on it as I really did 100% myself without the help videos.
The differences I noticed comparing the solution from the video are the following :
- My Double Mapping is inversed : First you provide the Transaction ID, then you can check which owners have approved it yet
- My onlyOwner modifier structure is very different. Is it lighter/heavier to run ?
- other differences too
//SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.7;
pragma abicoder v2;
contract MultiSigWallet {
uint ApprovalRequired;
address[] ArrayOwners ;
mapping(address => uint) OwnerID ;
constructor (address[] memory _ArrayOwners, uint _ApprovalRequired) {
ApprovalRequired = _ApprovalRequired;
ArrayOwners= _ArrayOwners;
for (uint i=0;i<ArrayOwners.length;i++) {
OwnerID[ArrayOwners[i]] = i+1 ;
}
}
event TransferRequested (uint TransferID,address RequestedBy, address TransferTo, uint amount);
event TransferDone (uint AmountTransferred, address indexed SentTo) ;
event DepositDone (address indexed Depositer, uint AmountDeposited);
struct TransferRequest {
uint TransferID ;
address initiator ;
address TransferTo;
uint Amount;
bool TransferredSuccessfully;
}
TransferRequest[] public ListTransferRequests ; // public so that anyone can look for a transferID and see the request
modifier OnlyOwner {
require (IsOwner() == true);
_;
}
function IsOwner() public view returns (bool){
for (uint i=0 ; i < ArrayOwners.length ; i++) {
if (ArrayOwners[i] == msg.sender) {
return true ;
}
}
return false;
}
mapping (uint => mapping (address=> bool)) ApprovalMapping ; // we provide the Transaction ID first then the owner address
function CreateTransferRequest (uint amountToTransfer, address recipient) public OnlyOwner {
require( balance[recipient] >= amountToTransfer, "This account doesn't have enough balance for that transfer" ); // we check it before pushing the list AND after getting the approval (in the transfer function) as a double check
ListTransferRequests.push(TransferRequest(ListTransferRequests.length,msg.sender,recipient,amountToTransfer,false));
ApprovalMapping[ListTransferRequests.length-1][msg.sender] = true;
emit TransferRequested(ListTransferRequests.length-1,msg.sender,recipient,amountToTransfer);
}
function AcceptTransferRequest (uint TransferID) public OnlyOwner {
ApprovalMapping[TransferID][msg.sender] = true;
if (CheckApprovals(TransferID) == true && ListTransferRequests[TransferID].TransferredSuccessfully== false ) {
TransferTo(ListTransferRequests[TransferID].Amount,ListTransferRequests[TransferID].TransferTo,TransferID ); //make transfer
}
}
function CheckApprovals (uint TransferID) view public returns (bool){
uint Approvals = 0 ;
for (uint i=0 ; i < ArrayOwners.length ; i++) {
if (ApprovalMapping[TransferID][ArrayOwners[i]] == true) {Approvals += 1;}
}
if (Approvals >= ApprovalRequired) {return (true);}
return (false);
}
mapping (address => uint) balance ;
function MakeADeposit () public payable returns (uint) {
balance[msg.sender] = balance[msg.sender] +msg.value;
emit DepositDone (msg.sender, msg.value);
return balance[msg.sender] ;
}
function TransferTo (uint amountToTransfer, address recipient, uint TransferID) private returns(uint, address) {
require( balance[recipient] >= amountToTransfer);
uint previousBalance = balance[recipient];
balance[recipient] -= amountToTransfer;
payable(recipient).transfer(amountToTransfer);
// dire que c'est sent pour pas qu'on puisse le faire à double
ListTransferRequests[TransferID].TransferredSuccessfully = true ;
emit TransferDone (amountToTransfer, recipient) ;
assert(balance[recipient] == previousBalance - amountToTransfer);
return (amountToTransfer, recipient) ;
}
// extra helpful function
function GetBalance(address BalanceFrom) public view returns (uint) {
return balance[BalanceFrom] ;
}
function GetListOwners() public view returns ( uint, address[] memory){
return (ArrayOwners.length, ArrayOwners);
}
function GetOwnerID(address ToTest) public view returns(uint IfZeroNotOwner) {
return OwnerID[ToTest];
}
}
I’ve made changes in my previous solution (it was removed to prevent noise) for removing the redundant balance
map within MultisigWallet
contract and added the possibility to init the source wallet owner
within MultisigWallet constructor.
Ownable.sol
// Ownable.sol
pragma solidity 0.8.13;
contract Ownable {
mapping(address => bool) owners;
modifier onlyOwners {
require(owners[msg.sender] == true);
_;
}
event ownerAdded(address newOwnerId);
function addOwner(address _newOwner) public onlyOwners {
owners[_newOwner] = true;
emit ownerAdded(_newOwner);
}
}
MultisigWallet.sol
// MultisigWallet.sol
pragma solidity 0.8.13;
import "./Ownable.sol";
contract MultisigWallet is Ownable {
struct Transfer {
address from;
address payable to;
uint amount;
mapping(address => bool) approvals;
uint approvalsCounter;
uint id;
}
Transfer[] transfers;
uint approvalLimit;
event depositDone(address sender, uint amount, uint resultBalance);
event approvalLimitChanged(uint newApprovalLimit, address changedBy);
event transferInitialized(address from, address to, uint amount, uint createdTransferId);
event transferApproved(uint transferId, uint approvalsCount, uint currentApprovalLimit);
event transferFinished(address from, address to, uint amount);
constructor(address _sourceOwner) {
owners[_sourceOwner] = true;
}
function deposit() public payable {
emit depositDone(msg.sender, msg.value, address(this).balance);
}
function setApprovalLimit(uint _approvalLimit) public onlyOwners {
approvalLimit = _approvalLimit;
emit approvalLimitChanged(approvalLimit, msg.sender);
}
function initTransfer(address payable _to, uint _amount) public onlyOwners {
require(address(this).balance >= _amount);
Transfer storage newTransfer = _createTransfer(_to, _amount);
emit transferInitialized(msg.sender, _to, _amount, newTransfer.id);
if (approvalLimit == 0) {
_finishTransfer(msg.sender, _to, _amount);
}
}
function approveTransfer(uint _id) public onlyOwners {
Transfer storage selectedTransfer = transfers[_id];
require(selectedTransfer.approvalsCounter <= approvalLimit, "Required approvals already received.");
require(selectedTransfer.approvals[msg.sender] != true, "Approve must be unique.");
selectedTransfer.approvals[msg.sender] = true;
selectedTransfer.approvalsCounter++;
emit transferApproved(_id, selectedTransfer.approvalsCounter, approvalLimit);
if (selectedTransfer.approvalsCounter >= approvalLimit) {
_finishTransfer(selectedTransfer.from, selectedTransfer.to, selectedTransfer.amount);
}
}
function _finishTransfer(address _from, address payable _to, uint _amount) private {
require(address(this).balance >= _amount);
// The transfer solution used because of: https://ethereum.stackexchange.com/a/19343
(bool success, ) = _to.call{value:_amount}("");
assert(success);
emit transferFinished(_from, _to, _amount);
}
// This function is requred because the Transfer struct contains mapping
// and there are no possibility to construct it with empty mapping in a Transfer(args...) way
function _createTransfer(address payable _to, uint _amount) private returns (Transfer storage) {
Transfer storage newTransfer = transfers.push();
newTransfer.from = msg.sender;
newTransfer.to = _to;
newTransfer.amount = _amount;
newTransfer.id = transfers.length - 1;
return newTransfer;
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
// A multi sig wallet smart contract
// A use case might be a charity where anyone can send in funds, anyone can request funds and
// the charity trustees(referenced as owners in the smart contract) can approve or reject
// the request for funds
// This smart contract is configured for three(3) trustess with two(2) yay or nay votes
// required by the trustees to either approve or reject the request for funds
// A trustee can change their vote before the request for funds is either approved or rejected
// Once a request for funds is either approved or rejected it is marked as closed and
// voting will be closed for that particular 'transfer of funds' request
contract MultiSigWallet {
struct TransferRequests {
string transferReason;
address transferTo;
uint transferAmount;
bool transferApproved;
bool transferRejected;
bool transferRequestClosed;
mapping(uint => bytes1) vote;
}
mapping(uint => TransferRequests) public transferRequests;
address[3] public owners;
uint public approvalsRequired;
uint private id;
event DepositReceived(address indexed _depositor, uint _depositAmount);
event TransferRequestReceived(
string indexed _transferReason,
address indexed _transferTo,
uint _transferAmount,
uint _assignedID
);
event Transferred(
string indexed _transferReason,
address indexed _transferTo,
uint _transferAmount
);
event TransferRejected(
string indexed _transferReason,
address indexed _transferTo,
uint _transferAmount
);
constructor(address[3] memory _addresses, uint _approvalsRequired) {
owners = _addresses;
approvalsRequired = _approvalsRequired;
}
function deposit() external payable {
emit DepositReceived(msg.sender, msg.value);
}
function checkContractBalance() external view returns (uint _balance) {
_balance = address(this).balance;
}
function createTransferRequest(
string memory _transferReason,
address _transferTo,
uint _transferAmount
) public {
TransferRequests storage newTransferRequest = transferRequests[id];
uint _assignedID = id;
id++;
newTransferRequest.transferReason = _transferReason;
newTransferRequest.transferTo = _transferTo;
newTransferRequest.transferAmount = _transferAmount;
emit TransferRequestReceived(
_transferReason,
_transferTo,
_transferAmount,
_assignedID
);
}
function vote(uint _id, bool approve) external {
uint voterIndex;
bool _isInOwnerList;
uint yesVotes;
uint noVotes;
for (uint i = 0; i < 3; i++) {
if (msg.sender == owners[i]) {
_isInOwnerList = true;
voterIndex = i;
}
}
require(_isInOwnerList, "Only owners can perform this action");
require(
keccak256(bytes(transferRequests[_id].transferReason)) !=
keccak256(bytes("")),
"The transfer request was not found for the provided ID"
);
require(
transferRequests[_id].transferRequestClosed != true,
"The transfer request is closed"
);
if (approve) {
transferRequests[_id].vote[voterIndex] = "Y";
} else {
transferRequests[_id].vote[voterIndex] = "N";
}
for (uint i = 0; i < 3; i++) {
if (transferRequests[_id].vote[i] == "Y") {
yesVotes++;
}
if (transferRequests[_id].vote[i] == "N") {
noVotes++;
}
}
if (yesVotes == approvalsRequired) {
transferRequests[_id].transferApproved = true;
transferRequests[_id].transferRequestClosed = true;
(bool success, ) = transferRequests[_id].transferTo.call{
value: transferRequests[_id].transferAmount
}("");
require(success, "Transfer failed");
emit Transferred(
transferRequests[_id].transferReason,
transferRequests[_id].transferTo,
transferRequests[_id].transferAmount
);
} else if ((noVotes == approvalsRequired)) {
transferRequests[_id].transferRejected = true;
transferRequests[_id].transferRequestClosed = true;
emit TransferRejected(
transferRequests[_id].transferReason,
transferRequests[_id].transferTo,
transferRequests[_id].transferAmount
);
}
}
}
Help me to fix the error below!
My code was successfully compiled, but I got an error below during the deployment.
Error: expected array value (argument=null, value="", code=INVALID_ARGUMENT, version=abi/5.5.0)
This is my wallet code. I created a list of request (each request stores the amount, recipient, and list of voters). If the owner signs, the address would be added to the list of voters. The length of list means the number of approvals. Transaction will be made once the approval number is the same as the initial threshold.
pragma solidity 0.7.5;
pragma abicoder v2;
contract Wallet {
// minimum number of approvals to make a transaction
uint threshold;
// return true if the owner address is saved as a key in the dict
mapping(address => bool) isOwnerDict;
struct Request {
address payable recipient;
address[] voters;
uint amount;
}
Request[] requestLog;
modifier onlyOwners() {
require(isOwnerDict[msg.sender] == true);
_;
}
modifier stopSecondVote(uint _id) {
bool isSecondVote = false;
for (uint i=0; i<requestLog[_id].voters.length; i++) {
if (requestLog[_id].voters[i] == msg.sender) {
isSecondVote = true;
}
}
require(isSecondVote = false);
_;
}
// setup the initial values (owners and minimum number of approvals)
constructor(address[] memory _ownerList, uint _threshold) {
threshold = _threshold;
for (uint i=0; i<_ownerList.length; i++) {
isOwnerDict[_ownerList[i]] = true;
}
}
// create a request
function createRequest(address payable _recipient, uint _amount) public onlyOwners {
address[] memory emptyVoterList;
requestLog.push(
Request(_recipient, emptyVoterList, _amount)
);
}
// approve a request and transfer the amount
function approve(uint _id) public onlyOwners stopSecondVote(_id) {
addVoter(_id);
transfer(_id);
}
// add the owner address in the voter list
function addVoter(uint _id) private {
requestLog[_id].voters.push(msg.sender);
}
// transfer the requested asset once
function transfer(uint _id) private {
require(requestLog[_id].voters.length == threshold);
requestLog[_id].recipient.transfer(requestLog[_id].amount);
}
}
where does the error throw. when calling what fuctiom? im going to assume ots from your constrictot. you need to pass in an array of addresses and a threshold value before u deploy it
in remix you will see an option to input params beside the deploy tab. this is where yoy do it. arg1 should be an array of owners and arg2 should be the threshold number
Thanks a lot. My code was working!! Just my deployment approach was wrong lol
pragma solidity 0.8.7;
pragma abicoder v2;
contract Wallet {
mapping(address => bool) isOwner;
mapping(address => uint256) balance;
uint256 approvalsNeeded;
struct Transfer {
uint256 transferID;
address from;
address to;
uint256 transferAmount;
address[] approvalList;
bool approvalStatus;
}
Transfer[] transferList;
event TransferSuccessful(address _from, address _to, uint256 _amount, string _message);
constructor(
address owner1,
address owner2,
address owner3,
uint256 requiredApprovals
) {
isOwner[owner1] = true;
isOwner[owner2] = true;
isOwner[owner3] = true;
approvalsNeeded = requiredApprovals;
}
modifier onlyOwner {
require(isOwner[msg.sender] == true, "Only an Owner can run this function!");
_;
}
function checkOwner(address _addrs) public view returns (bool) {
return isOwner[_addrs];
}
// parameter: amount to deposit
// returns: current total amount after deposit
function deposit() payable public {
balance[msg.sender] += msg.value;
}
// balance can only be viewed by the balance holder
function getBalance() public view returns (uint256) {
return balance[msg.sender];
}
// require: owned amount to be >= amount to transfer
function createTransfer(address _receiver, uint256 _amount) public {
require(balance[msg.sender] >= _amount, "You cannot transfer more than what you have!");
require(msg.sender != _receiver, "You cannot transfer to yourself!");
uint256 id = transferList.length + 1;
address[] memory newArray;
Transfer memory newTransfer = Transfer(id, msg.sender, _receiver, _amount, newArray, false);
transferList.push(newTransfer);
}
function seeTransfer(uint256 _index) public view onlyOwner returns (Transfer memory) {
return transferList[_index];
}
// the actual transfer happens here when the approval == 2
// then: deduct amount from sender, assert getBalance-amount, transfer
// then: remove from transferList, move to transferApproved
function approveTransfer(uint256 _index) public onlyOwner returns (string memory message) {
require(transferList[_index].approvalStatus == false, "Transaction was already successful. No approvals needed.");
uint256 approveLength = transferList[_index].approvalList.length;
bool ownerAlreadyApproved = false;
if (approveLength > 0) {
for (uint i = 0; i < approveLength; i++) {
if (transferList[_index].approvalList[i] == msg.sender) {
ownerAlreadyApproved = true;
break;
}
}
require(ownerAlreadyApproved == false, "You have approved this transaction before, you can only approve once per transaction.");
if (!ownerAlreadyApproved) {
transferList[_index].approvalList.push(msg.sender);
}
if (transferList[_index].approvalList.length == approvalsNeeded) {
address currentSender = transferList[_index].from;
address currentReceiver = transferList[_index].to;
uint256 currentSenderBalance = balance[transferList[_index].from];
uint256 amountToTransfer = transferList[_index].transferAmount;
require(currentSenderBalance >= amountToTransfer, "This sender's balance is insufficient.");
_transfer(currentSender, currentReceiver, amountToTransfer);
transferList[_index].approvalStatus = true;
emit TransferSuccessful(currentSender, currentReceiver, amountToTransfer, "Transfer successful!");
assert(balance[currentSender] == currentSenderBalance - amountToTransfer);
message = "Transaction successful.";
return message;
}
} else {
transferList[_index].approvalList.push(msg.sender);
message = "Your approval recorded.";
return message;
}
}
function _transfer(address _from, address _to, uint256 _amount) private {
balance[_from] -= _amount;
balance[_to] += _amount;
}
}
Whew… tried to write 90% blind before checking… maybe 10% of that was usable. I’ll have to go through this course about 10 more times to really absorb everything, but wanted to at least try before looking it up.
The only meaningful change to the solution that I came up with that is (maybe) functional includes:
- coding the constructor to include owner addresses in the actual code
- omitting the “limit” variable in the constructor
- specifying the “if” statement to >= 2, in place of the “limit” variable
pragma solidity 0.7.5;
pragma abicoder v2;
contract Wallet {
address[] public owners;
uint limit;
struct Transfer{
uint amount;
address payable receiver;
uint approvals;
bool hasBeenSent;
uint id;
}
event TransferRequestCreated(uint _id, uint _amount, address _initiator, address _receiver);
event ApprovalReceived(uint _id, uint _approvals, address _approver);
event TransferApproved(uint _id);
Transfer[] transferRequests;
mapping(address => mapping(uint => bool)) approvals;
modifier onlyOwners(){
bool owner = false;
for(uint i=0; i<owners.length;i++){
if(owners[i] == msg.sender){
owner = true;
}
}
require(owner == true);
_;
}
constructor (){
address owner;
owner = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
owner = 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2;
owner = 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db;
owner = msg.sender;
}
function deposit() public payable {}
function createTransfer(uint _amount, address payable _receiver) public onlyOwners {
emit TransferRequestCreated(transferRequests.length, _amount, msg.sender, _receiver);
transferRequests.push(
Transfer(_amount, _receiver, 0, false, transferRequests.length)
);
}
function approve(uint _id) public onlyOwners {
require(approvals[msg.sender][_id] == false);
require(transferRequests[_id].hasBeenSent == false);
approvals[msg.sender][_id] = true;
transferRequests[_id].approvals++;
emit ApprovalReceived(_id, transferRequests[_id].approvals, msg.sender);
if(transferRequests[_id].approvals >= 2){
transferRequests[_id].hasBeenSent = true;
transferRequests[_id].receiver.transfer(transferRequests[_id].amount);
emit TransferApproved(_id);
}
}
function getTransferRequests() public view returns (Transfer[] memory){
return transferRequests;
}
}
how to fix this error
creation of Wallet errored: Error encoding arguments: Error: types/values length mismatch (count={“types”:2,“values”:5}, value={“types”:[“address[]”,“uint256”],“values”:["[","], [","], [","]",“2”]}, code=INVALID_ARGUMENT, version=abi/5.5.0)
Are you typing in the correct constructor parameters when deploying the contract? It looks to me you are typing in empty arguments when your constructor takes 2 arguments
when trying to deploy i input the 3 addresses in _owners using [""], and i set the _limit to 2 and i get this error everytime…
can u share the code, hwoeevr if i was to guess you need to pass in an array of addresses into the constructor. however from the error it looks lik eyour passing in an array of arrays. so change your format of the arguments u pas into the constructor to be
["addr1", "addr2", "addr3"], 2
and not
[["addr1"], ["addr2"], ["addr3"], "2"]
if this does not work then paste your code and ill know staright away for u what the problem is
hey @pappas_kellyn great for completing the assignment anyway and trying to do it on your own. i would not be discourage at all. this is a difficult and advanced assignment for a beginners course. solidity is very hard at the beginning i found this to be true myself when beginning also.
and yes see this is why u will do well. instead of feeling fed up and just moving on your motivated to go back and really go over things again, this is by far the harder option to convince yourself to do and its much easier to just move onto the course and convince yourself youll learn more there. so this is a brilliant decision.
if you ever have any questions im always on call so dont be shy to ping me in dm or to tag me in a post on the forums
Work in progress so missing alot here. Dont understand whay I get the error on line 56
pragma solidity 0.8.11;
contract Multisig {
struct externalTransfer {
uint amount;
address payable receiver;
uint approvals;
bool hasBeenSent;
uint id;
}
mapping(address => mapping(uint => bool)) approvals;
event depositDone(uint amount, address depositedFrom);
address payable Owner;
address payable CoOwnerA;
address payable CoOwnerB;
externalTransfer[] transferRequests ;
modifier onlyOwners(){
require(msg.sender == Owner || msg.sender == CoOwnerA || msg.sender == CoOwnerB);
_;
}
constructor () {
Owner = payable(msg.sender);
CoOwnerA = payable(0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2);
CoOwnerB = payable(0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db);
uint limit = 2;
}
//Function that recives ETH into wallet
function Deposit () public payable {
emit depositDone(msg.value, msg.sender);
}
//function to transfer ETH out of wallet to external adress
function createExternalTransfer (uint _amount, address payable _receiver) public onlyOwners {
require(address(this).balance >= _amount, "Insufficient funds");
transferRequests.push(externalTransfer(_amount, _receiver, 0, false, transferRequests.length));
}
function Approve (uint _id) public onlyOwners {
}
function getWalletBalance () public view returns (uint) {
return address(this).balance;
}
function getTransferRequests () public view returns (externalTransfer[] memory) {
return transferRequests[];
}
}
MultiOwnable.sol
pragma solidity 0.8.7;
contract MultiOwnable {
//owner set
mapping(address => bool) OwnerList;
//storing for illustration only, else not needed for prod-env
address public contractAddress;
modifier onlyOwner {
require(OwnerList[msg.sender] == true, "Only SC Owner can Call this");
//run the real functions, Practically EVM will replace all of function statement here
_;
}
constructor(address owner1, address owner2, address owner3) {
//at SC creation all owners are set in ownerlist
OwnerList[owner1] = true;
OwnerList[owner2] = true;
OwnerList[owner3] = true;
//storing contract address
contractAddress = address(this);
}
function getWalletBalance() public view returns(uint256){
return contractAddress.balance;
}
}
DestructMulti.sol
pragma solidity 0.8.7;
import "./MultiOwnable.sol";
contract DestructMulti is MultiOwnable {
int8 private contractCount;
constructor(address owner1, address owner2, address owner3) MultiOwnable(owner1,owner2,owner3) {
}
function destructContract() public onlyOwner {
//uint256 _contractBalance = contractAddress.balance;
//commenting below, since selfdestruct implictly does the transfer to owner
//payable(owner).transfer(_contractBalance);
selfdestruct(payable(msg.sender));
}
}
MultiSigWallet.sol
pragma solidity 0.8.7;
pragma abicoder v2;
/*
Here are the requirements of the smart contract wallet you will be building
– Anyone should be able to deposit ether into the smart contract
– The contract creator should be able to input (1): the addresses of the owners and (2): the numbers of approvals required for a transfer, in the constructor. For example, input 3 addresses and set the approval limit to 2.
– Anyone of the owners should be able to create a transfer request. The creator of the transfer request will specify what amount and to what address the transfer will be made.
– Owners should be able to approve transfer requests.
– When a transfer request has the required approvals, the transfer should be sent.
*/
//importing other contract
import "./DestructMulti.sol";
contract MultiSigWallet is DestructMulti {
struct PendingTxn{
address to;
uint256 amount;
uint8 approvalCount;
address[3] approver;
bool isPending;
}
//mapping(address => uint) balances;
//Pending Transactions Array, Waiting for approval
PendingTxn pendingTxnList;
uint8 minApprover;
event balanceUpdated(uint256 amount, uint256 Balance, address indexed from, address indexed to);
modifier sufficientBalance(uint _amount){
require(address(this).balance >= _amount,"SC Balance Not Sufficient");
_;
}
constructor(address owner1, address owner2, address owner3,uint8 _approverCount) DestructMulti(owner1,owner2,owner3){
//check for approverCount boundary conditions
if(_approverCount <=3){
minApprover = _approverCount;
} else if(_approverCount >3)
{
minApprover = 3;
} else {
minApprover = 1;
}
initPendingTransaction();
}
function initPendingTransaction() private {
pendingTxnList.to = address(0x0);
pendingTxnList.amount = 0;
pendingTxnList.approvalCount = 0;
pendingTxnList.isPending = false;
pendingTxnList.approver[0] = address(0x0);
pendingTxnList.approver[1] = address(0x0);
pendingTxnList.approver[2] = address(0x0);
}
// admin function can only be perfromed by owner of SC
//payable allows to receive money from the caller of the function
//the msg.value will get added to balance of the SC
//the internal balance datastructure allows to keep track of who this money is owed to
function deposit() public payable returns(uint256){
//emit log
emit balanceUpdated(msg.value,address(this).balance, msg.sender, address(this));
return address(this).balance;
}
//Create transfer Request to an Address. Registers the transaction details, wait for approval
function transferRequest(address _recipient, uint _amount) public onlyOwner sufficientBalance(_amount) {
//modifier onlyOwner- checks if its one of the owner
//modifier suffieientBalance- check if balance is sufficient
//check if recipient is not SC-Address, unnecessary operation will cost GAS
require(address(this) != _recipient,"Transfer to Self-SC not Allowed");
//check for Zero amount transfer
require(_amount != 0,"Zero Amount Transfer. Ignoring");
pendingTxnList.to = _recipient;
pendingTxnList.amount = _amount;
pendingTxnList.approvalCount = 0;
pendingTxnList.isPending = true;
}
function getPendingTxn() public view onlyOwner returns(PendingTxn memory) {
return pendingTxnList;
}
//approve transfer, if approvalCount>=2, does transfer, reset pendingTransaction
function approveTransfer() public onlyOwner {
//check if there is a pending transfer
require(pendingTxnList.isPending != false,"No Pending Transaction for Approval");
//check, same user shouldnt be approving again
if(msg.sender != pendingTxnList.approver[0] && msg.sender != pendingTxnList.approver[1] && msg.sender != pendingTxnList.approver[2]){
//increment approved count
pendingTxnList.approvalCount++;
if(pendingTxnList.approvalCount++ >= minApprover) {
//confirm transfer
_transfer(pendingTxnList.to,pendingTxnList.amount);
//emit log
emit balanceUpdated(pendingTxnList.amount, address(this).balance, address(this), pendingTxnList.to);
//init pending transaction
initPendingTransaction();
}
else {
pendingTxnList.approver[pendingTxnList.approvalCount- uint8(1)] = msg.sender;
}
}
else {
//check if there is a pending transfer
require(false,"Same User Can't Approve Again");
}
}
//function to cancel pendingTransaction
function cancelPendingTxn() public {
initPendingTransaction();
}
//private function, can be resused by others fn
function _transfer(address _to, uint _amount) private {
//confirm transfer
payable(_to).transfer(_amount);
}
}
I saw the assignment assistance video and one aspect i think i have not covered is multiple pending transaction. May be i will reimplement and share again.
You are trying to return the entire array, so there is no need to specify the index of it.
Your function should work in this way:
function getTransferRequests () public view returns (externalTransfer[] memory) {
return transferRequests;
}
Carlos Z
Ahh, that fixed it right up. Thank you.
Hey guys! Here’s my solution for the Multisig Wallet Smart contract. All feedback is more than welcome Cheers!
pragma solidity ^0.7.5;
pragma abicoder v2;
contract MultisigWallet {
mapping(address => bool) owners;
mapping(address => mapping(uint => bool)) signedTransfers;
uint numberOfApprovals;
event FundsDeposited(address _from, uint _amount);
Transfer[] public submittedTransfers;
struct Transfer {
address to;
uint256 amount;
uint confirmations;
bool processed;
}
modifier onlyOwners {
require(owners[msg.sender], "Not an owner of the contract");
_;
}
constructor(uint _numberOfApprovals, address[] memory _owners) {
for (uint i = 0; i < _owners.length ; i++) {
owners[_owners[i]] = true;
}
numberOfApprovals = _numberOfApprovals;
}
function deposit() public payable {
emit FundsDeposited(msg.sender, msg.value);
}
function getBalance() public view returns(uint256) {
return address(this).balance;
}
function createTransfer(address _to, uint256 _amount) public onlyOwners {
address[] memory _confirmedBy = new address[](3);
submittedTransfers.push( Transfer(_to, _amount, 1, false));
signedTransfers[msg.sender][submittedTransfers.length -1] = true;
}
function signTransfer(uint _idx) public onlyOwners {
Transfer storage submittedTransfer = submittedTransfers[_idx];
require(!signedTransfers[msg.sender][_idx], "This owner already signed the transfer");
submittedTransfer.confirmations += 1;
signedTransfers[msg.sender][_idx] = true;
if(submittedTransfer.confirmations >= numberOfApprovals){
require(submittedTransfer.processed==false, "Unable to process an already processed transfer");
_processTransfer(submittedTransfer);
}
}
function _processTransfer(Transfer storage _transferToProcess) private {
address payable _recipient = payable(_transferToProcess.to);
uint256 _amount = _transferToProcess.amount;
require(_amount <= address(this).balance, "Contract does not have enought funds to process transaction");
_recipient.transfer(_amount);
_transferToProcess.processed = true;
}
}
Work in progress so missing alot here. Dont understand whay I get the error on line 55: Undeclared identifier
pragma solidity 0.8.11;
contract Multisig {
struct externalTransfer {
uint amount;
address payable receiver;
uint approvals;
bool hasBeenSent;
uint id;
}
mapping(address => mapping(uint => bool)) approvals;
event depositDone(uint amount, address depositedFrom);
address payable Owner;
address payable CoOwnerA;
address payable CoOwnerB;
externalTransfer[] transferRequests;
modifier onlyOwners(){
require(msg.sender == Owner || msg.sender == CoOwnerA || msg.sender == CoOwnerB);
_;
}
constructor () {
Owner = payable(msg.sender);
CoOwnerA = payable(0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2);
CoOwnerB = payable(0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db);
uint limit = 2;
}
//Function that recives ETH into wallet
function Deposit () public payable {
emit depositDone(msg.value, msg.sender);
}
//function to transfer ETH out of wallet to external adress
function createExternalTransfer (uint _amount, address payable _receiver) public onlyOwners {
require(address(this).balance >= _amount, "Insufficient funds");
transferRequests.push(externalTransfer (_amount, _receiver, 0, false, transferRequests.length));
}
function Approve (uint _id) public onlyOwners {
require(approvals[msg.sender][_id] == false);
require(transferRequests[_id].hasBeenSent == false);
approvals[msg.sender][_id] = true;
transferRequests[_id].approvals++;
if(transferRequests[_id].approvals >= limit){
transferRequests[_id].hasBeenSent = true;
transferRequests[_id].receiver.transfer(transferRequests[_id].amount);}
}
function getWalletBalance () public view returns (uint) {
return address(this).balance;
}
function getTransferRequests () public view returns (externalTransfer[] memory) {
return transferRequests;
}
}