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;
}
Transfer[] transferRequests;
mapping(address => mapping(uint => bool)) approvals;
// mapping[address][transferID] => true/false
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[] memory _owners, uint _limit) {
owners = _owners;
limit = _limit;
}
function deposite() public payable returns (uint) {
}
function createTransfer(uint _amount, address payable _receiver) public onlyOwners {
transferRequests.push(
Transfer(_amount, _receiver, 0, false, transferRequests.length)
);
}
function approval(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 getTransferRequests() public view returns (Transfer[] memory) {
return transferRequests;
}
}
Hey @Denzel, hope you are ok.
I have made a few adjustments in your contracts to help you with that request.
contract validOwner
will take care of the users, check the comments on that one.
contract Ownable
will have the methods for the contract owner.
pragma solidity 0.7.5;
// SPDX-License-Identifier: UNLICENSED
contract Ownable {
address private _owner;
// always set functions name, different than contract name
modifier onlyOwner() {
require(msg.sender == _owner, "not contract owner");
_;
}
// get the Owner of contract
function getOwner() public view returns(address){
return _owner;
}
}
contract validOwner {
// address => valid owner
// to control who can vote
mapping(address => bool) private _owners;
// always set functions name, different than contract name
// to Add an owner into mapping
function addValidOwner(address _newValidOwner) internal{
_owners[_newValidOwner] = true;
}
modifier isvalidOwner() {
require(_owners[msg.sender], "not validOwner");
_;
}
}
contract multisig is Ownable, validOwner{
struct user{
uint id;
uint balance;
}
struct transaction {
bool sent;
uint amount;
uint approvals;
uint id;
}
mapping(address => uint) balance;
mapping(address => mapping(uint16 => bool)) approvals;
event depositDone(uint amount, address from);
event withdrawDone(uint amount, address to);
event transferDone(uint amount, address to, address from);
/*
* replaced by getOwner()
function MultiSigWallet() public{
_owner = msg.sender;
}
*/
function addOwner(address owner) onlyOwner public {
addValidOwner(owner);
}
function collectApprovals(uint _approvals) isvalidOwner public {
}
function deposit() public payable returns (uint){
balance[msg.sender] += msg.value;
emit depositDone(msg.value, msg.sender);
return balance[msg.sender];
}
function withdraw(uint amount) isvalidOwner public {
require(address(this).balance >= amount);
msg.sender.transfer(amount);
emit withdrawDone(amount, msg.sender);
}
function transfer(address to, uint amount) isvalidOwner public {
require(address(this).balance >= amount);
msg.sender.transfer(amount);
emit transferDone(amount, to, msg.sender);
}
}
Any question you got, let us know
Carlos Z
Itâs been 11 days since I posted my code but got no response. It would be very helpful for me if somebody could review my code and give me feedback on where should I improve.
Also, which course would be better to take next after completing this?
Hey @pok, sorry for the delay, i have tested your contract and its working as expected, one recommendation is to use a revert message on your require
statements, after creating a transaction request, approve it with one account, i try to approve it again with the same account, but this one was reverted (problem is that I assume is one of the modifiers got triggered, but without any message, we cant be sure which one).
Overall you have made a great work!
Carlos Z
made some changes to my code but now there is no option to compile, not sure whats happening, tried with other contracts as well and still. It says âCurrently you have no contract instances to interact withâ.
Multisig.sol:
pragma solidity 0.7.5;
import "./Ownable.sol";
import "./validOwner.sol";
contract multisig is validOwner, Ownable{
address private _owner;
struct user{
uint id;
uint balance;
}
struct transaction {
bool sent;
uint amount;
uint approvals;
uint id;
address receiver;
}
mapping(address => uint8) private _owners;
mapping(address => uint) balance;
mapping(address => mapping(uint16 => bool)) approvals;
event depositDone(uint amount, address from);
event withdrawDone(uint amount, address to);
event reqCreated(uint id, uint amount, address to, address from);
event transferDone(uint amount, address to, address from);
constructor(address[] memory _owners, uint _limit) {
owners = _owners;
limit = _limit;
}
Transfer[] TransactionReqs;
function MultiSigWallet() public{
_owner = msg.sender;
}
function addOwner(address owner) isOwner public {
_owners[owner] = 1;
}
function deposit() public payable returns (uint){
balance[msg.sender] += msg.value;
emit depositDone(msg.value, msg.sender);
return balance[msg.sender];
}
function withdraw(uint amount) validOwner public {
require(address(this).balance >= amount);
msg.sender.transfer(amount);
emit withdrawDone(amount, msg.sender);
}
function reqTransfer(uint approvals, address _receiver, uint amount) public validOwner {
emit reqCreated(TransactionReqs.length, _amount, _receiver, msg.sender);
transferRequests.push(
Transfer(_amount, _receiver, 0, false, TransactionReqs.length)
);
}
function transfer(uint transactionId) validOwner public {
require (approval = true);
require(address(this).balance >= amount);
msg.sender.transfer(amount);
emit transferDone(amount, to, msg.sender);
}
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 >= limit){
transferRequests[_id].hasBeenSent = true;
transferRequests[_id].receiver.transfer(transferRequests[_id].amount);
emit TransferApproved(_id);
}
}
}
Ownable.sol:
import "./validOwner.sol";
contract isOwner is validOwner{
address private _owner;
modifier isOwner() {
require(msg.sender == _owner);
_;
}
}
validOwner.sol:
contract validOwner{
address private _owner;
mapping(address => uint8) private _owners;
modifier validOwner() {
require(msg.sender == _owner || _owners[msg.sender] == 1);
_;
}
}
Please share an screenshot about the error so i can understand better what is happening.
Carlos Z
Here is my attempt at the multisig wallet, seems to work but im sure it can be improved
pragma solidity 0.7.5;
pragma abicoder v2;
contract Wallet {
address payable [] public owners;
uint approvalsNeeded;
constructor (address payable _owner2, address payable _owner3, uint _approvalsNeeded) {
owners.push(msg.sender);
owners.push(_owner2);
owners.push(_owner3);
approvalsNeeded = _approvalsNeeded;
}
modifier onlyOwner {
require (msg.sender == owners[0] || msg.sender == owners[1] || msg.sender == owners[2], "You are not an owner");
_;
}
event depositRecieved (address indexed _from, uint _amount);
event transferRequested (address indexed _requestedBy, uint _amount, address indexed _recipient, bool executed);
event transferApproved (address indexed _approvedBy, uint _id);
event transferExecuted (address indexed _finalApprover, uint _id);
struct Transfer {
uint transferID;
address payable to;
uint amount;
uint approvals;
bool executed;
}
Transfer [] transferRequests;
mapping(address => mapping(uint => bool)) approvals;
function deposit () public payable {
emit depositRecieved(msg.sender, msg.value);
}
function requestTransfer (address payable _to, uint _amount) public onlyOwner {
transferRequests.push(Transfer(transferRequests.length, _to, _amount, 1, false));
emit transferRequested(msg.sender, _amount, _to, false);
}
function approveTransfer (uint _id) public onlyOwner {
require (approvals[msg.sender][_id] == false, "You have already approved this transaction");
require (transferRequests[_id].executed == false, "This transfer has alreadu been executed");
approvals[msg.sender][_id] = true;
transferRequests[_id].approvals++;
emit transferApproved(msg.sender, _id);
if (transferRequests[_id].approvals >= approvalsNeeded) {
require (transferRequests[_id].approvals >= approvalsNeeded, "Not enough approvals");
transferRequests[_id].executed == true;
transferRequests[_id].to.transfer(transferRequests[_id].amount);
emit transferExecuted(msg.sender, _id);
}
}
function getTransferRequests () public view onlyOwner returns (Transfer[] memory) {
return transferRequests;
}
function getBalance () public view onlyOwner returns (uint) {
return (address(this).balance);
}
}
sometimes it can just be a glitch with remix i had this happen once before . try closing the browser or switching to another file that does compile and move code there. make sure your code is savd also.
I have refrained from watching the last two videos without giving this a solid try on my own but I seem to still be having some issues recording votes and triggering the transaction when the number of required votes is hit. I believe my issues are caused somewhere in the double mapping. The majority of the code works as intended I just need to get this voting system sorted out. Since I have hit a bit of a wall I have decided to post what I have here before moving onto the last videos, then come back and post an update once my code is working properly.
For my project I have decided to go with an inheritable structure where the restricted permissions for owners are all set in the parent contract and all of the logic that runs the multisig wallet in the child contract.
The portions I specifically have questions about are the double mapping, approveTransaction function, and the getPendingTransactions function. For the first two parts, I believe I made a mistake with the double mapping somewhere and it is not saving the incremented total_approval_votes variable to the transaction object. This prevents the approveTransaction function from ever sending the transaction. Lastly, for the getPendingTransactions function, I was wondering if there was a better way of displaying the variables stored within the transaction objects because just calling the entire array of transactions like this;
function getPendingTransactions() public view onlyOwners returns(Transaction[] memory) { return(transactionRequests); }
produces a list of values for the variables without any clear labels or indication as to which value is for what variable. To help with my troubleshooting I added the functions checkApprovalsById and getTransactionIdList to help me tell which variables had what values but the functions are redundant and I would like to replace them with a single function that produces all variables stored in the object with labels for each.
Any advice would be greatly appreciated!
pragma solidity 0.7.5;
contract Permissions {
//Variable to hold contract creator
address private creator;
//Constructor to set contract creator
constructor(){
creator = msg.sender;
}
//mapping to remember what addresses actually have owner permissions
mapping(address => bool) private owners;
//variable to set how many approval votes are needed for transactions to be approved
uint votesNeeded = 2;
//Modifier set creator permissions
modifier onlyCreator{
require(msg.sender == creator, "Not contract creator, permission denied");
_;
}
//Modifier set creator & owner permissions
modifier onlyOwners{
require(msg.sender == creator || owners[msg.sender] == true, "Not authorized owner, permission denied");
_;
}
//Events for adding or removing owners
event ownerAdded (address NewOwner);
event ownerRemoved (address RemovedOwner);
//Function that allows the contract creator to assign owner status to other addresses
function addOwner(address _owner) public onlyCreator {
owners[_owner] = true;
emit ownerAdded(_owner);
}
//Function that allows the contract creator to revoke owner status from other addresses
function removeOwner(address _owner) public onlyCreator {
owners[_owner] = false;
emit ownerRemoved(_owner);
}
}
pragma solidity 0.7.5;
import "./Permissions.sol";
//allows us to return a struct from functions
pragma abicoder v2;
contract Muilt_Sig_Wallet is Permissions{
//A variable that will increment and be assigned to transactions as they are submitted
uint private TransactionID;
//Struct to hold transaction information, total yes votes, and who has voted for each transaction
struct Transaction {
address submitted_by;
address payable recipient;
uint amount;
uint total_approval_votes;
bool sent;
}
//Empty array to hold transfers that have been proposed by owners
Transaction[] private transactionRequests;
//Mapping that allows us to call transactions by their ID
mapping (uint => Transaction) private transactions;
//Empty array to hold transfer IDs for each transfers
uint[] private transactionIdList;
//Double mapping for owner transaction approvals
mapping(address => mapping(uint => bool)) hasVoted;
//Balance mapping
mapping(address => uint) balance;
//Events to signify a deposit, new transaction request, new vote being cast by owner, and transaction execution
event newDeposit (address from, uint Amount);
event newTransactionRequest (uint TransactionId, address SubmittedBy, address Recipient, uint Amount);
event newTransactionApproval (address VotingOwner, uint TransactionId, uint CurrentYesVotes);
event transactionExecuted (uint TransactionId, address Sender, address Recipient, uint Amount);
//Deposit function
function deposit() public payable returns(uint) {
balance[msg.sender] += msg.value;
emit newDeposit(msg.sender, msg.value);
return(balance[msg.sender]);
}
//Individual amount deposited
function getYourContribution() public view returns(uint) {
return(balance[msg.sender]);
}
//Total balance held by smart contract
function getTotalBalance() public view returns(uint) {
return(address(this).balance);
}
//Function for owners to look at array of all pending transaction objects and all of their variables at once
function getPendingTransactions() public view onlyOwners returns(Transaction[] memory) {
return(transactionRequests);
}
//For owners to quickly look at array of only pending transaction ID numbers
function getTransactionIdList() public view onlyOwners returns(uint[] memory) {
return(transactionIdList);
}
//For owners to quickly look at total_approval_votes only
function checkApprovalsById(uint _transactionId) public view onlyOwners returns(uint) {
Transaction storage transaction = transactions[_transactionId];
return(transaction.total_approval_votes);
}
//Function that only allows owners to propose transactions
function proposeTransaction(address payable _recipient, uint _amount) public onlyOwners{
//Checking required funds, checking owner is not recipent, & incrementing transaction ID
require((address(this).balance) >= _amount);
require (msg.sender != _recipient, "Cannot send funds to Owners.");
uint _transactionID = TransactionID++;
//Building transaction
Transaction memory _transaction;
_transaction.total_approval_votes = 0;
_transaction.submitted_by = msg.sender;
_transaction.recipient = _recipient;
_transaction.amount = _amount;
_transaction.sent = false;
//Saving to memory and emitting event
transactions[_transactionID] = _transaction;
transactionIdList.push(_transactionID);
transactionRequests.push(_transaction);
emit newTransactionRequest(_transactionID, msg.sender, _recipient, _amount);
}
//Allows owners to give approval for transactions by ID number
function approveTransaction(uint _transactionId) public onlyOwners {
//Calls transaction object by it's ID number
Transaction storage transaction = transactions[_transactionId];
//Requiring owner has not already voted for this transaction and it has not yet been sent
require(hasVoted[msg.sender][_transactionId] = false);
require(transaction.sent = false);
//Updating total_approval_votes and hasVoted mapping
hasVoted[msg.sender][_transactionId] = true;
transaction.total_approval_votes++;
emit newTransactionApproval (msg.sender, _transactionId, transaction.total_approval_votes);
//If new vote makes total_approval_votes >= votesNeeded and the contract balance is still >= amount then the transaction is executed
if (transaction.total_approval_votes >= votesNeeded){
require(address(this).balance >= transaction.amount);
transaction.sent = true;
transaction.recipient.transfer(transaction.amount);
emit transactionExecuted (_transactionId, transaction.submitted_by, transaction.recipient, transaction.amount);
}
}
}
Hey @Dustin_Gearhart, hope you are well.
I think the problem with your approveTransaction
function is on your conditions:
//Requiring owner has not already voted for this transaction and it has not yet been sent
require(hasVoted[msg.sender][_transactionId] = false);
require(transaction.sent = false);
You are not comparing (==), instead, you are assigning the value.
Solidity functions are not intended to be coded on the way you ask, you mean to do something like "label:" + mapping[key]
.
Carlos Z
Ah! Thank you so much. My code is working much better now. After watching the last video, some troubleshooting, and a few minor improvements I now have a properly functioning project. I plan to make more improvements and post to my Github but for now here is my final submission.
pragma solidity 0.7.5;
contract Permissions {
//Variable to hold contract creator
address private creator;
//Constructor to set contract creator and give them owner permissions
constructor(){
creator = msg.sender;
owners[creator] = true;
}
//mapping to remember what addresses actually have owner permissions
mapping(address => bool) private owners;
//variable to set how many approval votes are needed for transactions to be approved
uint votesNeeded = 2;
//Modifier set creator permissions
modifier onlyCreator{
require(msg.sender == creator, "Not contract creator, permission denied");
_;
}
//Modifier set creator & owner permissions
modifier onlyOwners{
require(msg.sender == creator || owners[msg.sender] == true, "Not authorized owner, permission denied");
_;
}
//Events for adding or removing owners
event ownerAdded (address NewOwner);
event ownerRemoved (address RemovedOwner);
//Function that allows the contract creator to assign owner status to other addresses and throws if owner has already been added
function addOwner(address _owner) public onlyCreator {
require(owners[_owner] == false, "Owner already added");
owners[_owner] = true;
emit ownerAdded(_owner);
}
//Function that allows the contract creator to revoke owner status from other addresses
function removeOwner(address _owner) public onlyCreator {
owners[_owner] = false;
emit ownerRemoved(_owner);
}
}
pragma solidity 0.7.5;
import "./Permissions.sol";
//allows us to return a struct from functions
pragma abicoder v2;
contract Muilt_Sig_Wallet is Permissions{
//A variable that will increment and be assigned to transactions as they are submitted
uint private TransactionID;
//Struct to hold transaction information, total yes votes, and who has voted for each transaction
struct Transaction {
address submitted_by;
address payable recipient;
uint amount;
uint total_approval_votes;
bool sent;
}
//Empty array to hold transfers that have been proposed by owners
Transaction[] private transactionRequests;
//Mapping that allows us to call transactions by their ID
mapping (uint => Transaction) private transactions;
//Empty array to hold transfer IDs for each transfers
uint[] private transactionIdList;
//Double mapping for owner transaction approvals
mapping(address => mapping(uint => bool)) hasVoted;
//Balance mapping
mapping(address => uint) balance;
//Events to signify a deposit, new transaction request, new vote being cast by owner, and transaction execution
event newDeposit (address from, uint Amount);
event newTransactionRequest (uint TransactionId, address SubmittedBy, address Recipient, uint Amount);
event newTransactionApproval (address VotingOwner, uint TransactionId, uint CurrentYesVotes);
event transactionExecuted (uint TransactionId, address Sender, address Recipient, uint Amount);
//Deposit function
function deposit() public payable returns(uint) {
balance[msg.sender] += msg.value;
emit newDeposit(msg.sender, msg.value);
return(balance[msg.sender]);
}
//Individual amount deposited
function getYourContribution() public view returns(uint) {
return(balance[msg.sender]);
}
//Total balance held by smart contract
function getTotalBalance() public view returns(uint) {
return(address(this).balance);
}
//Function for owners to look at array of all pending transaction objects and all of their variables at once
function getTransactions() public view onlyOwners returns(Transaction[] memory) {
return(transactionRequests);
}
//For owners to quickly look at array of only pending transaction ID numbers
function getTxIdList() public view onlyOwners returns(uint[] memory) {
return(transactionIdList);
}
//Function that only allows owners to propose transactions
function proposeTransaction(address payable _recipient, uint _amount) public onlyOwners{
//Checking required funds, checking owner is not recipent, & incrementing transaction ID
require((address(this).balance) >= _amount, "Insufficient funds");
require (msg.sender != _recipient, "Cannot send funds to Owners");
uint _transactionID = TransactionID++;
//Building transaction
Transaction memory _transaction;
_transaction.total_approval_votes = 0;
_transaction.submitted_by = msg.sender;
_transaction.recipient = _recipient;
_transaction.amount = _amount;
_transaction.sent = false;
//Saving to memory and emitting event
transactions[_transactionID] = _transaction;
transactionIdList.push(_transactionID);
transactionRequests.push(_transaction);
emit newTransactionRequest(_transactionID, msg.sender, _recipient, _amount);
}
//Allows owners to give approval for transactions by ID number
function approveTransaction(uint _transactionId) public onlyOwners {
//Requiring owner has not already voted for this transaction and it has not yet been sent
require(hasVoted[msg.sender][_transactionId] == false, "Vote has already been cast");
require(transactionRequests[_transactionId].sent == false, "Transaction has already been sent");
//Updating total_approval_votes and hasVoted mapping
hasVoted[msg.sender][_transactionId] = true;
transactionRequests[_transactionId].total_approval_votes++;
emit newTransactionApproval (msg.sender, _transactionId, transactionRequests[_transactionId].total_approval_votes);
//If new vote makes total_approval_votes >= votesNeeded and the contract balance is still >= amount then the transaction is executed
if (transactionRequests[_transactionId].total_approval_votes >= votesNeeded){
require(address(this).balance >= transactionRequests[_transactionId].amount, "Insufficient funds");
transactionRequests[_transactionId].sent = true;
transactionRequests[_transactionId].recipient.transfer(transactionRequests[_transactionId].amount);
emit transactionExecuted (_transactionId, transactionRequests[_transactionId].submitted_by, transactionRequests[_transactionId].recipient, transactionRequests[_transactionId].amount);
}
}
}
didnt seem to work
A lot of optimizing I could do but I was more interested in making the code work.
My solution below:
pragma solidity 0.7.5;
contract Wallet {
event deposited(uint amount, address depositedTo);
address public owner;
uint public approveLimit;
address callers;
struct NeedTransfer {
uint id;
address from;
address to;
uint amount;
uint approved;
}
NeedTransfer[] public pendingTransfer;
constructor () {
owner = msg.sender;
}
modifier onlyOwner {
require(msg.sender == owner);
_;
}
mapping (address => uint) balance;
address[] public users;
modifier onlyBy {
address newUser;
for(uint i = 0; i < users.length - 1; i++){
if(msg.sender == users[i]) {
newUser = users[i];
}
}
require(msg.sender == newUser);
_;
}
function deposit() public payable returns (uint) {
balance[msg.sender] += msg.value;
emit deposited(msg.value, msg.sender);
return balance[msg.sender];
}
function addUser( address _walletOwner1) public onlyOwner {
users.push(_walletOwner1);
}
function addApprovalNum (uint _approvedLimit) public onlyOwner {
approveLimit = _approvedLimit;
}
function requestTransfer (uint _id, address _from, address _to, uint _approved, uint _amount) public onlyBy {
pendingTransfer.push(NeedTransfer(_id, _from, _to, _approved, _amount));
}
function approveRequest(uint _index, bool _approve) public onlyBy {
if (_approve) {
pendingTransfer[_index].approved +=1;
}
else{
pendingTransfer[_index].approved +=0;
}
}
function transfer(uint _index) public onlyBy {
require(balance[msg.sender] >= pendingTransfer[_index].amount, "Balance not Sufficient");
require(msg.sender != pendingTransfer[_index].to, "Can't transfer money to yourself");
require(approveLimit <= pendingTransfer[_index].approved, "you have not pass the approved limit");
_transfer(pendingTransfer[_index].from,pendingTransfer[_index].to , pendingTransfer[_index].amount);
}
function _transfer(address from, address to, uint amount) private {
balance[from] -= amount;
balance[to] += amount;
}
}
Added a little functionality, such as all owners being able to approve swapping an owner out for a new one (so three are always maintained) and for a wallet to check its owner status. Also added a time_call function to mark when an owner was changed, though appreciate this is block time rather than the moment of final approval.
Edit: Also on reading around a little everyone seemed to suggest using a mapping as much more efficient than using a list to store the owners, so have done that here as well.
I hardcoded the original owners and limit variables in the constructor rather than sending them with the deploying transaction - is there a reason not to do this?
Thereâs a fair bit more to do but itâs taken me a while to complete this, partly because I just couldnât figure out how to deposit Ether into the contract to be able to test it out (lol!).
contract MultiSig {
uint limit;
uint owner_vote;
// Mapping of owners
mapping(address => bool) owners;
struct Transfer {
address payable to;
uint amount;
uint approvals;
bool hasBeenSent;
uint id;
}
struct NewOwner {
address old;
address newOwner;
uint approvals;
bool complete;
uint id;
}
modifier onlyOwners{
require(owners[msg.sender] == true, "You cannot access this function unless you are a designated owner");
_;
}
constructor() {
owners[msg.sender] = true;
owners[0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2] = true;
owners[0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c] = true;
limit = 2;
owner_vote = 3;
}
Transfer[] transferRequests;
NewOwner[] ownerChanges;
// Double mapping - dictionary of dictionaries - for approvals
mapping(address => mapping(uint => bool)) approvals;
mapping(address => mapping(uint => bool)) ownerApprovals;
// e.g.
// approvals[msg.sender][0] = true;
function ownerStatus () public view returns (bool){
return owners[msg.sender];
}
function changeOwner(address _old, address _new) public onlyOwners {
ownerChanges.push(NewOwner(_old, _new, 0, false, ownerChanges.length));
}
event ownerChanged(address _old, address _new, uint256 _time);
function Time_call() public view returns (uint256){
return block.timestamp;
}
function approveOwnerChange(uint _id) public onlyOwners {
require(ownerApprovals[msg.sender][_id] == false); // owner must not have approved before
require(ownerChanges[_id].complete == false); // change must not have already been made
ownerChanges[_id].approvals ++;
ownerApprovals[msg.sender][_id] = true;
if(ownerChanges[_id].approvals >= owner_vote){
owners[ownerChanges[_id].old] = false;
owners[ownerChanges[_id].newOwner] = true;
uint256 time = Time_call();
emit ownerChanged(ownerChanges[_id].old,ownerChanges[_id].newOwner, time);
}
}
function createTransaction (uint _amount, address payable _to) public onlyOwners{
require(_amount <= address(this).balance,"Insufficient funds"); // Send amount less than or equal to contract balance
transferRequests.push(Transfer(_to, _amount, 0, false, transferRequests.length));
}
event transactionSent(uint _amount, address indexed _from, address indexed _to);
event transactionCreated(uint _id, uint _amount, address indexed _by, address indexed _to);
function getBalance() public view returns (uint256) {
return address(this).balance;
}
function deposit() payable public {}
function approveTransaction (uint _id) public onlyOwners {
require(approvals[msg.sender][_id] == false,"You have already approved this transaction"); // owner must not have approved before
require(transferRequests[_id].hasBeenSent == false,"The transaction has been sent already"); // transaction must not have been sent
transferRequests[_id].approvals ++;
approvals[msg.sender][_id] = true;
if(transferRequests[_id].approvals >= 2){
transferRequests[_id].to.transfer(transferRequests[_id].amount); // send the transfer
transferRequests[_id].hasBeenSent = true;
emit transactionSent(transferRequests[_id].amount,address(this),transferRequests[_id].to);
}
}
function getTransferRequests() public view returns (Transfer[] memory){ //use abicoder to return a struct
return transferRequests;
}
}
I seem to be having an issue with compiling the contract on line 49. Here is the issue: âInsurancePolicy.sol:49:61: TypeError: Invalid type for argument in function call. Invalid implicit conversion from int_const 0 to bool requested. transferRequests.push(Transfer(_amount, _recipient, 0, false, transferRequests.length)); ^â
I have done some research but I would like to ask the forum as well. I got off to a solid enough start but I did decide to move on and watch the other videos. There is a lot more functionality I could do with this contract as well but I am trying to just get to where I can compile it at this point. Any help is appreciated!
pragma solidity 0.7.5;
pragma abicoder v2;
contract InsurancePolicy {
address[] public policyOwners;
uint limit;
struct Transfer{
uint amount;
address payable recipient;
bool hasBeenSent;
uint id;
uint approvals;
}
event TransferRequestCreated(uint id, uint amount, address sender, address recipient);
event ApprovalRecieved(uint id, uint approvals, address approver);
event TransferApproved(uint id);
Transfer[] public transferRequests;
mapping(address => mapping(uint => bool)) approvals;
modifier onlyOwners(){
bool owner = false;
for(uint i=0; i<policyOwners.length; i++){
if(policyOwners[i] == msg.sender){
owner = true;
}
}
require(owner == true);
_;
}
constructor(address[] memory _owners, uint _limit){
policyOwners = _owners;
limit = _limit;
}
function deposit() public payable{
}
function getBalance() public view returns (uint){
return address(this).balance;
}
function createTransfer(uint _amount, address payable _recipient) public onlyOwners {
emit TransferRequestCreated(transferRequests.length, _amount, msg.sender, _recipient);
transferRequests.push(Transfer(_amount, _recipient, 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 ApprovalRecieved(id, transferRequests[id].approvals, msg.sender);
if(transferRequests[id].approvals >= limit){
transferRequests[id].hasBeenSent == true;
transferRequests[id].recipient.transfer(transferRequests[id].amount);
emit TransferApproved(id);
}
}
function getTransferRequests() public view returns (Transfer[] memory) {
return transferRequests;
}
}
Okay after a couple weeks of banging my head against the wall trying to figure out how this works I think I got it.
//admin contract
//SPDX-License-Identifier: UNLICENSED
pragma solidity >= 0.8.4;
contract metaAdmin {
//upon deployment tells contract that msg.sender is owner;
constructor(){
owner = msg.sender;
keyholder[msg.sender]= true;
}
//tells contract that [ owner ] is an address variable.
address owner;
//modifier req owner to execute function.
modifier reqOwner{
require(msg.sender == owner);
_; //run the function.
}
//keyholders
//----------------------------------------------------
//mapping keyholder returns true/false
mapping(address => bool) keyholder;
//modifier to require keyholder to execute function
modifier reqKeyholder{
require(keyholder[msg.sender]);
_; //run the function.
}
//event to log keyholders
event eventSetKeyholder(address);
//make target address keyholder mapping return true, emit to event log.
function setKeyholder(address target) public reqOwner returns(bool Success) {
require(!keyholder[target]== true, "target is already a keyholder");
keyholder[target]=true;
emit eventSetKeyholder(target);
Success = true;
}
// permissioning adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/pull/746/files#diff-8d5f210f8ce2e6f8d7f62f655e6f72ec
//keyholder approval threshold
//-----------------------------------------------------------------------------------
uint defApprovalThreshold = 2;
function setDefApprovalThreshold(uint threshold) public reqOwner returns(bool Success, uint){
defApprovalThreshold = threshold;
return(Success = true, defApprovalThreshold);
}
}
//proposal contract.
// SPDX-License-Identifier: UNLICENSED
pragma solidity >= 0.8.4;
import "./metaAdmin.sol";
pragma experimental ABIEncoderV2;
contract metaProposalProcess is metaAdmin {
struct transaction{
uint txID;
uint amount;
address from;
address to;
uint reqApprovals;
uint approvals;
}
uint defTxId = 0;
transaction[] transactionQue;
//mapping for keyholders
mapping(address => mapping(uint => bool)) keyholderApproval;
function printTransactionQue() public view returns(uint length){
return (transactionQue.length);
}
function printTransaction(uint _txId) public view returns(uint txID, uint amount, address from, address to, uint reqApprovals, uint approvals){
return (transactionQue[_txId].txID, transactionQue[_txId].amount, transactionQue[_txId].from, transactionQue[_txId].to, transactionQue[_txId].reqApprovals, transactionQue[_txId].approvals);
}
}
//multisig wallet contract
//SPDX-License-Identifier: UNLICENSED
pragma solidity >= 0.8.4;
import "./metaAdmin.sol";
import "./metaProposalProcess.sol";
contract metaMultiSigWalletProject is metaProposalProcess{
event eventMSWithdrawl(uint amount, address from, address to);
//allows addresses to keep track of their contributions;
mapping(address => uint) ethDeposited;
//get contribtion of target address;
function getContributionTotal(address target) public view returns(uint){
return ethDeposited[target];
}
//get contract Balance
function getContractBalance() public view returns(uint){
return ethDeposited[address(this)];
}
//deposit eth into contract;
function deposit() public payable returns(bool Success){
ethDeposited[msg.sender] += msg.value;
ethDeposited[address(this)] += msg.value;
Success = true;
}
//--------------------------------
function propTransfer(uint amount, address _to, uint reqApprovals) public reqKeyholder returns(bool Success){
require(ethDeposited[address(this)] >= amount, "Insufficient Balance");
require(reqApprovals >= defApprovalThreshold, "approval threshold too low");
transactionQue.push(transaction(transactionQue.length, amount, address(this), _to, reqApprovals, 1) );
keyholderApproval[msg.sender][transactionQue.length] = true;
return(Success=true);
}
//vote yes on txID, if threshold reached complete transfer function.
function voteYesTransfer(uint _txId) public reqKeyholder returns(bool vSuccess){
require(!keyholderApproval[msg.sender][_txId] == true, "you have already voted yes on this transfer");
keyholderApproval[msg.sender][_txId] = true;
transactionQue[_txId].approvals += 1;
if(transactionQue[_txId].approvals >= transactionQue[_txId].reqApprovals){
require(ethDeposited[address(this)] >= transactionQue[_txId].amount);
ethDeposited[address(this)] -= transactionQue[_txId].amount;
(bool Success, ) = payable(transactionQue[_txId].to).call{value: transactionQue[_txId].amount} ('');
emit eventMSWithdrawl(transactionQue[_txId].amount, transactionQue[_txId].from, transactionQue[_txId].to);
delete transactionQue[_txId];
require(Success, "transfer failed.");
}
return(vSuccess == true);
}
// relevant info for array push/pop https://stackoverflow.com/questions/49051856/is-there-a-pop-functionality-for-solidity-arrays
}
I didnât realize there was a template so I got to work right from scratch so the formatting isnât as clean as it could have been like shown in the final video. Feedback and insight appreciated.
Thanks all,
Ben
Hey @GahuckImGoofy, hope you are well.
You only have a typo error in the order of the arguments that will be inputed into the transferRequests
array, since its based on the Struct Transfer
, the values of each must be inputed in the same order.
Carlos Z
Hey @ImmMeta, hope you are ok.
I have tested your contract, amazing approach to be honest, is quite a nice optimization in the multisig contract.
I like what you did with setKeyholder
instead of having a fixed array at the initialization of the contract, I added 2 addresses more (since metaAdmin
constructor already set the deployer has keyholder).
-
getContributionTotal
andgetContractBalance
to follow how the funds were distributed. -
deposit
15 eth with 1/3 keyholders, 5 eth with 2/3. - used 2 times
propTransfer
to create 2 proposals.From
1/3 keyholderto
3/3 keyholder,amount
of 2eth on both. -
voteYesTransfer
onproposeId=1
(2nd one created) with 2/3 keyholders, twice, 2nd went reverted by"you have already voted yes on this transfer"
, as expected
Funds were received properly on the 3/3 keyholder (as expected).
A suggestion i could made: after receiving the funds, i try printTransaction
with proposeId=1
and all values were reset to default (all zero), allthough is a nice solution, maybe adding a alreadySent
boolean o the struct of the transaction, might be a nice approach in case you want to know in details past transactions that was vote/sent properly, that way i could check the details of the proposeId=1
but also know that has been sent
Overall amazing work man! keep it like that!
Carlos Z
Thank you very much Carlos!