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;
}
}
If you specify “uint limit” as variable outside constructor and set its value to 2 in the constructor, it will fix this error. Just like you specified the addresses of owner and CoOwner as payable addresses outside the constructor
Image:
https://prnt.sc/Z0BfNJowPj9L
Thank you for the quick answer. Was just banging my head against the wall. I wish does error messages was a bit more specific. Now that I`m sp new to this its tricky to understand them
Not gonna lie. The approval part of my code I did not have a chance to do alone. Pure copy.
I struggle to understand the rules when we put in this " . " . What is the rule about what is on the left side, and what is on the right side. When you write say ; “whatever[something].” When you put in the “dot” in after the array, what is it you are actually doing?
Keep in consideration, what is transferRequests
, by definition in your contract, is an array of structs values.
struct externalTransfer {
uint amount;
address payable receiver;
uint approvals;
bool hasBeenSent;
uint id;
}
externalTransfer[] transferRequests;
So in order to access the values of the struct of each element in the array, you use the dot like any other struct, just that you need to specify, which is the position of the element that you want to access transferRequests[_id]
.
That way, if you want to know an specific value of the 1st element on the array (the receiver address in this example), you can just call transferRequests[0].receiver
and it will return the address.
While .transfer(transferRequests[_id].amount)
is an internal method of solidity to send funds to a payable
address. Check Here https://docs.soliditylang.org/en/latest/security-considerations.html?highlight=transfer#sending-and-receiving-ether
Carlos Z
I understand that the “dot” is a break, the code is read left to right. So in id[0]. look for reciver . then transfer, look in id[0] for the amount to transfer?
Ok. This is how I did it. Used some help in the end.
Seems to be working. I realize I have not made the constructer very different since it allready have the addresses in there, but was the way I got my “only owner” to be the way it was. I like that more the a “for” loop.
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;
uint limit = 2;
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);
}
//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;
}
}