Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save skplunkerin/1a11df1ba2edc5d03e1cecb67d3d6d98 to your computer and use it in GitHub Desktop.
Save skplunkerin/1a11df1ba2edc5d03e1cecb67d3d6d98 to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.8.7+commit.e28d00a7.js&optimize=false&runs=200&gist=
pragma solidity 0.8.7;
abstract contract abstractHelloWorld {
function GetValue() virtual public view returns (uint);
function SetValue(uint _value) virtual public;
function AddNumber(uint _value) virtual public returns (uint) {
return 10;
}
}
interface pureAbstractHelloWorld {
function GetValue() external view returns (uint);
function SetValue(uint _value) external;
}
contract HelloWorld is abstractHelloWorld{
uint private simpleInteger;
function GetValue() public override view returns (uint) {
return simpleInteger;
}
function SetValue(uint _value) public override {
simpleInteger = _value;
}
function AddNumber(uint _value) public override returns (uint ){
return simpleInteger = _value;
}
}
pragma solidity 0.8.7;
import "./owner.sol";
contract MultiSigWallet is Owner {
//// TYPE DECLARATIONS ////
struct Signature {
User Owner;
bool Approved;
}
struct Request {
uint256 ID;
User Owner;
uint256 Amount;
address payable ToAddress;
string Status;
Signature[] Signatures;
}
struct RequestsLookup {
uint256[] Pending;
uint256[] Completed;
// TODO: can add more logic to account for Transfer Requests that get
// denied. (chris)
// uint256[] Denied;
}
//// STATE VARIABLES ////
uint256 internal RequiredApprovals;
Request[] internal TransferRequests;
RequestsLookup internal RequestLog;
string _pendingStatus = "pending";
string _completedStatus = "completed";
// constructor sets the owner address in OwnerList.
constructor(address[] memory _owners, uint8 _approvalCount) {
RequiredApprovals = _approvalCount;
// Loop through all addresses to add to OwnerList:
for (uint256 i = 0; i < _owners.length; i++) {
OwnerList[_owners[i]] = User(_owners[i], true);
}
}
//// EVENTS ////
event requestCreated(
uint256 _requestID,
uint256 _amount,
User indexed _owner,
address indexed _to
);
event requestApproved(uint256 _requestID, address indexed _signedBy);
event requestCompleted(uint256 _requestID);
//// MODIFIERS ////
// notSelfSigning requires that msg.sender isn't the Request.Owner.
modifier notSelfSigning(uint256 _requestID) {
require(
msg.sender != TransferRequests[_requestID].Owner.Address,
"Cannot self-sign Transfer Requests"
);
_;
}
//// PUBLIC FUNCTIONS ////
// CreateRequest will push a new Request to the TransferRequests array if
// msg.sender is an Owner, and if _amount is greater than 0 and less than or
// equal to the contracts balance.
function CreateRequest(uint256 _amount, address payable _toAddress) public isOwner {
require(_amount > 0, "Amount must be greater than 0");
require(
address(this).balance >= _amount,
"Amount must be <= contract balance"
);
uint256 requestID = TransferRequests.length;
Signature[] memory signatures;
TransferRequests.push(
Request(
requestID,
_getOwner(),
_amount,
_toAddress,
_pendingStatus,
signatures
)
);
RequestLog.Pending.push(requestID);
emit requestCreated(requestID, _amount, _getOwner(), _toAddress);
}
// PendingRequests returns all un-completed Transfer Requests if msg.sender
// is an Owner.
function PendingRequests() public view isOwner returns (Request[] memory) {
Request[] memory requests;
// Loop through the Pending request ID's and return each Transfer
// Request for the ID's found:
for (uint256 i = 0; i < RequestLog.Pending.length; i++) {
uint256 requestID = RequestLog.Pending[i];
Request memory r = TransferRequests[requestID];
// NOTE: ".push()" cannot be used due for memory arrays:
// - Member "push" is not available in struct Request memory[]
// memory outside of storage.
// docs info: https://docs.soliditylang.org/en/v0.8.10/types.html#allocating-memory-arrays
// BUT chaning requests to use storage also doesn't work:
// - This variable is of storage pointer type and can be accessed
// without prior assignment, which would lead to undefined behaviour.
//
// This is a workaround, I'm not sure if there's a better approach:
// requests.push(r);
requests[i] = r;
}
return requests;
}
// CompletedRequests returns all completed Transfer Requests, this is
// available to Owners and non-Owners.
function CompletedRequests() public view returns (Request[] memory) {
Request[] memory requests;
// Loop through the Completed request ID's and return each Transfer
// Request for the ID's found:
for (uint256 i = 0; i < RequestLog.Completed.length; i++) {
uint256 requestID = RequestLog.Completed[i];
Request memory r = TransferRequests[requestID];
// NOTE: ".push()" cannot be used, see comment in PendingRequests().
requests[i] = r;
}
return requests;
}
// ApproveRequest adds an Approved signature to a Request if isOwner and not
// self-signing the Request; Then if all required approvals are met, the
// Request is moved from Pending to Completed status and the funds are
// transferred.
function ApproveRequest(uint256 _requestID)
public
payable
isOwner
notSelfSigning(_requestID)
{
Request storage request = TransferRequests[_requestID];
require(request.Amount != 0, "Transfer Request not found");
require(request.Amount > address(this).balance, "Insufficient funds");
request.Signatures.push(Signature(_getOwner(), true));
emit requestApproved(_requestID, _getOwner().Address);
// if we have enough signatures, mark the request as complete and
// transfer the funds:
if (request.Signatures.length >= RequiredApprovals) {
_pendingToCompleted(request);
request.ToAddress.transfer(request.Amount);
emit requestCompleted(_requestID);
}
}
//// PRIVATE FUNCTIONS ////
// _getOwner returns msg.sender as a User{} struct type if the msg.sender is
// an Owner.
function _getOwner() private view isOwner returns (User memory) {
return User(msg.sender, true);
}
// _pendingToCompleted moves the _request.ID out of RequestLog.Pending to
// RequestLog.Completed and marks _request.Status as completed.
function _pendingToCompleted(Request storage _request) internal isOwner {
uint256 pendingLength = RequestLog.Pending.length;
uint256 completedLength = RequestLog.Completed.length;
// string memory previousStatus = _request.Status;
uint256[] memory newPending;
// Move requestID out of Pending to Completed TransferRequests:
uint256 j = 0;
for (uint256 i = 0; i < pendingLength; i++) {
if (RequestLog.Pending[i] != _request.ID) {
newPending[j] = RequestLog.Pending[i];
j++;
}
}
RequestLog.Pending = newPending;
RequestLog.Completed.push(_request.ID);
_request.Status = _completedStatus;
assert(RequestLog.Pending.length == pendingLength - 1);
assert(RequestLog.Completed.length == completedLength + 1);
// to compare strings, convert them to bytes, generate a Hashes, and
// compare the hashes:
// https://docs.soliditylang.org/en/v0.8.10/types.html#bytes-and-string-as-arrays
// assert(
// keccak256(bytes(_request.Status)) !=
// keccak256(bytes(previousStatus))
// );
}
}
pragma solidity 0.8.7;
contract Owner {
//// TYPE DECLARATIONS ////
// inspired by:
// https://ethereum.stackexchange.com/a/12539/86218
// https://stackoverflow.com/a/49637782/1180523
struct User {
address Address;
bool Exists;
}
//// STATE VARIABLES ////
mapping(address => User) internal OwnerList;
//// EVENTS ////
//// MODIFIERS ////
// isOwner requires that msg.sender is in the OwnerList.
modifier isOwner() {
require(
!OwnerList[msg.sender].Exists,
"You must be an owner to do this"
);
_;
}
//// PUBLIC FUNCTIONS ////
//// PRIVATE FUNCTIONS ////
}

Project Requirements

– Anyone should be able to deposit ether into the smart contract

– The contract creator should be able to input the following (in the constructor) :

  1. the addresses of the owners
  2. the number of approvals required for a transfer

For example, input 3 addresses and set the approval limit to 2.

– Any one of the owners should be able to create a transfer request.

  • The creator of the transfer request will specify:
    1. what amount
    2. what address to transfer amount to

– Owners should be able to approve transfer requests.

– When a transfer request has the required approvals, the transfer should be sent.

MultiSig Wallet Plan:

  • Object types (break out to own contracts?):

    • MultiSig (project)
      • objects needed:
        • address[3] Owners;
        • struct Approval { address Owner bool Approved }
        • struct TransferRequest { uint Amount address payable ToAddress []Approval Approvals bool Completed }
      • modifiers needed:
        • isOwner
      • functions needed:
        • Deposit() payable (anyone)
        • CreateTransferRequest(amount, toAddress) isOwner
        • ApprovedTransferRequests
        • DeniedTransferRequests
        • TransferRequests isOwner
        • ApproveTransferRequest isOwner
    • Owner (child)
    • Transfer Request (child)
      • Create TR
        • the amount
        • the wallet to transfer to
      • Return TR's
      • Approve TR's
        • when the last needed approval is made, the transfer will occur.
  • Functions/Actions:

    • Constructor
      • input 3 addresses and approval limit on contract creation
    • Deposit money
      • any one can deposit (not just owners)
    • Transfer Requests
      • only owners can create, view, and approve TR's
pragma solidity 0.8.7;
import "./owner.sol";
contract TransferRequest is Owner {
//// TYPE DECLARATIONS ////
struct Signature {
User Owner;
bool Approved;
}
struct Request {
uint256 ID;
User Owner;
uint256 Amount;
address payable ToAddress;
string Status;
Signature[] Signatures;
}
struct RequestsLookup {
uint256[] Pending;
uint256[] Completed;
// TODO: can add more logic to account for Transfer Requests that get
// denied. (chris)
// uint256[] Denied;
}
//// STATE VARIABLES ////
uint256 internal RequiredApprovals;
Request[] internal TransferRequests;
RequestsLookup internal RequestLog;
string _pendingStatus = "pending";
string _completedStatus = "completed";
//// EVENTS ////
//// MODIFIERS ////
// notSelfSigning requires that msg.sender isn't the Request.Owner.
modifier notSelfSigning(uint256 _requestID) {
require(
msg.sender != TransferRequests[_requestID].Owner.Address,
"Cannot self-sign Transfer Requests"
);
_;
}
//// PUBLIC FUNCTIONS ////
//// PRIVATE FUNCTIONS ////
}
pragma solidity 0.8.7;
contract MemoryAndStorage {
struct User{
uint id;
uint balance;
}
// state variable - using storage
mapping(uint => User) Users;
// PUBLIC
// addUser creates a new user with the passed in _id and _balance,
// adding them to the users state variable.
function addUser(uint _id, uint _balance) public {
// only add if the user doesn't exist
require(!_userExists(_id), "user already exists, please use updateBalance()");
Users[_id] = User(_id, _balance);
_assertUserBalance(_id, _balance);
}
// updateBalance will modify a users balance if the passed in user
// _id is found.
function updateBalance(uint _id, uint _balance) public {
_requireUserExists(_id);
Users[_id].balance = _balance;
_assertUserBalance(_id, _balance);
}
// getBalance returns the users balance if the passed in user _id
// is found.
function getBalance(uint _id) view public returns (uint) {
_requireUserExists(_id);
return Users[_id].balance;
}
// PRIVATE
// _userExists verifies if the passed in user _id is found or not.
function _userExists(uint _id) private view returns(bool) {
User memory u = Users[_id];
if (u.id == 0) {
return false;
}
return true;
}
// _assertUserBalance verifies the user.balance matches the passed
// in _balance.
function _assertUserBalance(uint _id, uint _balance) private view {
assert(_userExists(_id));
assert(Users[_id].balance == _balance);
}
// _requireUserExists requires that the passed in user _id is found
// in users.
function _requireUserExists(uint _id) private view {
require(_userExists(_id), "User doesn't exist, please create the user first.");
}
}
pragma solidity 0.8.7;
// EXTERNAL CONTRACTS
// https://academy.moralis.io/lessons/external-contracts-2
contract Government {
//// STATE ////
struct Transaction {
address from;
address to;
uint amount;
uint txId;
}
Transaction[] TransactionLog;
uint[] DepositLog;
//// EVENTS ////
event txAdded(uint _txID, address indexed _from, address indexed _to, uint indexed _amount);
//// PUBLIC ////
function AddTransaction(address _from, address _to, uint _amount) external {
uint txID = TransactionLog.length;
TransactionLog.push(Transaction(_from, _to, _amount, txID));
emit txAdded(txID, _from, _to, _amount);
}
function SendValueExample() external payable {
DepositLog.push(msg.value);
}
function getBalance() internal view returns(uint){
return address(this).balance;
}
function GetTransaction(uint _txID) public view returns(Transaction memory){
return TransactionLog[_txID];
}
//// PRIVATE ////
}
pragma solidity 0.8.7;
contract Ownable {
// address owner;
// NOTE: adding public will automatically create a getter
// function for the state varaible.
address public owner;
// modifiers used to restrict access to functions
modifier onlyOwner {
require(msg.sender == owner, "owner address missing");
_;
}
// don't need to pass the address in, msg.sender works here
// constructor(address _owner){
constructor(){
owner = msg.sender;
}
}
pragma solidity 0.8.7;
// The Basics:
contract HelloWorld {
// called state variables:
string message;
// CONSTRUCTORS
// https://academy.moralis.io/lessons/constructors-2
// runs only 1x during compile.
constructor(string memory _msg){
message = _msg;
}
// VIEW vs PURE
// https://academy.moralis.io/lessons/view-pure
// view function, reads data outside of func.
function HelloExternalScope() public view returns(string memory) {
return message;
}
// pure function, does NOT read/write data outside of func.
function HelloLocalScope() public pure returns(string memory) {
// called local variables:
string memory localMessage = "Hello Function";
// NOTE ABOUT STRINGS:
// + and . don't work to concat strings, nothing
// is built-in to solidity for string concats.
// return localMessage + message;
// return localMessage . message;
return localMessage;
}
// CONTROL FLOW (if statements)
// https://academy.moralis.io/lessons/control-flow
// NOTE: parameters naming scheme is prepended with "_" to mark is as an "input variable",
// but you can name the variable anything you want
function HelloIfStatement(int _number) public pure returns(string memory) {
// NOTE: these variables are ALWAYS available in every function:
// msg.sender // this address executing the code
// msg.value // the callers amount of ETH (or currency)
// standard if statement
if (_number <= 10) {
return "good number";
} else {
return "bad number";
}
}
// LOOPS
// https://academy.moralis.io/lessons/loops
// NOTE: returns(int) doesn't need memory *unlike returns(string memory)
// While loops:
function HelloWhile(int _number) public pure returns(int) {
int i = 0;
while(i < 10){
// NOTE: no way to console.log();
_number++;
i++;
}
return _number;
}
// For loops:
function HelloFor(int _number) public pure returns(int) {
for(int i = 0; i < 10; i++){
// NOTE: no way to console.log();
_number++;
}
return _number;
}
// GETTERS & SETTERS
// https://academy.moralis.io/lessons/setter-functions
int num;
// Get
function GetNum() public view returns(int){
return num;
}
// Set
function SetNum(int _n) public returns(int){
num = _n;
return num;
}
// ARRAYS
// https://academy.moralis.io/lessons/arrays
int[] numbersDynamic; // dynamic length array
int[3] numbersStatic; // static length array of 3
// int[3] numbersStaticVals = [1,2,3]; // static length with init vals
function AppendDynamicNumbers(int _n) public returns(int[] memory){
// only dynamic arrays have the ".push()" method.
numbersDynamic.push(_n);
return numbersDynamic;
}
function GetDynamicNumbersID(uint _id) public view returns(int){
// NOTE: if _id is passed that doesn't exist, an error is triggered:
// call to HelloWorld.GetDynamicNumbersID errored: VM error: revert.
// revert
// The transaction has been reverted to the initial state.
// Note: The called function should be payable if you send value and the value you send should be less than your current balance.
// Debug the transaction to get more information
return numbersDynamic[_id];
}
// NOTE: _id needs to be uint type, since no position is negative.
function AppendStaticNumbers(uint _id, int _n) public returns(int[3] memory){
// static length arrays don't have ".push()",
// you need to specify the position:
numbersStatic[_id] = _n;
return numbersStatic;
}
// STRUCTS
// https://academy.moralis.io/lessons/structs-3
struct Person{
uint age;
string name;
}
Person[] people;
function AddPerson(uint _age, string memory _name) public {
Person memory p = Person(_age, _name);
people.push(p);
// This also works
// people.push(Person(_age, _name));
}
// NOTE: Filip says you can't return "Person", instead you should do:
// function getPerson(uint _index) public view returns (uint, string memory){...}
// but returning Person seems to work?
function GetPerson(uint _index) public view returns (Person memory){
return people[_index];
}
// BUT, if we do return multiple values like recommended by Filip, this is how:
function GetPersonValues(uint _index) public view returns (uint, string memory){
Person memory personToReturn = people[_index];
return (personToReturn.age, personToReturn.name);
}
}
////// INTERMEDIATE //////
// commented out with all comments, see re-created Bank contract below
// that's broken out using inheritance
// contract Bank {
// // MAPPINGS
// // https://academy.moralis.io/lessons/mappings-introduction
// // https://academy.moralis.io/lessons/mappings-2
// mapping(address => uint) balance;
// // see below for re-declaration of Deposit() being reserved for
// // the owner address.
// // function Deposit(uint _amount) public returns(uint){
// // balance[msg.sender] += _amount;
// // return balance[msg.sender];
// // }
// function GetBalance() public view returns(uint){
// return balance[msg.sender];
// }
// // IMPLEMENTING VISIBILITY
// // https://academy.moralis.io/lessons/introduction-to-visibility-2
// // https://academy.moralis.io/lessons/implementing-visibility
// // AND ERROR HANDLING
// // https://academy.moralis.io/lessons/require-theory
// // https://academy.moralis.io/lessons/assert-invariants-theory
// // https://academy.moralis.io/lessons/require
// // https://academy.moralis.io/lessons/assert
// // MODIFIERS
// // https://academy.moralis.io/lessons/modifiers-2
// address owner;
// // don't need to pass the address in, msg.sender works here
// // constructor(address _owner){
// constructor(){
// owner = msg.sender;
// }
// // modifiers used to restrict access to functions
// modifier onlyOwner {
// require(msg.sender == owner, "owner address missing");
// _; // means to: run the function
// // but what actually happens is, the function logic calling this
// // modifer get's placed right here.
// }
// modifier costs(uint _price){
// require(msg.value >= _price);
// _;
// }
// // used like, where value needs to be a static value:
// // function doSomething() public costs(100) {...}
// // Deposit get's redefined again below to be payable:
// // function Deposit(uint _amount) public onlyOwner returns(uint){
// // balance[msg.sender] += _amount;
// // // emit event (see EVENTS below):
// // emit balanceAdded(_amount, msg.sender);
// // emit indexedBalanceAdded(_amount, msg.sender);
// // return balance[msg.sender];
// // }
// function Transfer(address _recipient, uint _amount) public {
// // check if sender has balance first
// require(balance[msg.sender] >= _amount, "insufficient balance");
// require(msg.sender != _recipient, "must send to a separate address"); // should never allow this, doesn't make sense and might cause glitches/bugs
// uint previousBalance = balance[msg.sender];
// // break out re-usable code into private functions
// // balance[msg.sender] -= _amount;
// // balance[_recipient] += _amount;
// _transfer(msg.sender, _recipient, _amount + 10);
// // NOTE:
// // assert(false);
// // will consume all remaining gas in the function call
// assert(balance[msg.sender] == previousBalance - _amount); // unsure how to set custom err: "new balance doesn't match expected value"
// // event logs & further checks
// // TODO
// }
// // NOTE: private functions naming scheme is also using "_" prepended
// // to the function name, but you can name it anything.
// function _transfer(address _from, address _to, uint _amount) private {
// balance[_from] -= _amount;
// balance[_to] += _amount;
// }
// // DATA LOCATION
// // https://academy.moralis.io/lessons/data-location-2
// // 3 types:
// // - storage: permanent data storage (state variables defined outside of functions)
// // - memory: temporary data storage (functions parameters and local variables)
// // - calldata: similar to memory, but READ-ONLY (used for cheaper gas as it's more optimized, it's like a constant )
// // EVENTS
// // https://academy.moralis.io/lessons/events-3
// // Normal event:
// event deposited(uint _amount, address _toAddress);
// // Indexed (searchable in nodes) event:
// // NOTE: limited to 3 indexed parameters per-event.
// event indexedDeposited(uint _amount, address indexed _toAddress);
// // this is how it's triggered in a function (see Deposit()):
// // emit balanceAdded(amount, msg.sender);
// // PAYABLE FUNCTIONS
// // https://academy.moralis.io/lessons/payable-functions-3
// // This deposit's from the Address calling Deposit()
// // to the Smart Contracts address.
// // function Deposit() public payable returns(uint){
// function Deposit() public payable{
// // NOTE: not required, this is only for us to internally keep track.
// // The ETH used happens automatically.
// balance[msg.sender] += msg.value;
// // emit event (see EVENTS below):
// emit deposited(msg.value, msg.sender);
// // emit indexedDeposited(msg.value, msg.sender);
// // return balance[msg.sender];
// }
// event withdrawn(uint _amount, address _toAddress);
// function Withdraw(uint _amount) public returns(uint){
// require(balance[msg.sender] >= _amount, "not enough funds");
// // msg.sender.transfer() doesn't work on normal address types
// // need to make msg.sender to address payable type:
// address payable sender = payable(msg.sender);
// // NOTE: for security purposes, ALWAYS modify contract state before
// // transfering any funds.
// // see this forum comment: https://studygroup.moralis.io/t/transfer-assignment/27368/24
// uint oldBalance = balance[msg.sender];
// balance[msg.sender] -= _amount;
// // Transfer to the sender:
// // NOTE: if transfer fails, it will refend just like with require()
// sender.transfer(_amount);
// assert(balance[msg.sender] == oldBalance - _amount);
// emit withdrawn(_amount, sender);
// return balance[msg.sender];
// }
// }
// INHERITANCE
// https://academy.moralis.io/lessons/inheritance-reading-assignment-2
// https://academy.moralis.io/lessons/inheritance-2
import "./helloworld-inheritance-Ownable.sol";
// EXTERNAL CONTRACTS
// https://academy.moralis.io/lessons/external-contracts-2
interface GovernmentInterface {
function AddTransaction(address _from, address _to, uint _amount) external;
function SendValueExample() external payable;
// NOTE: can only have externally declared functions in an interface.
// function GetTransaction(uint _txID) public view returns(Transaction memory);
}
// NOTE: lots of comments removed, see original Bank contract with course
// notes/comments/links above.
// Bank is now the child contract to Ownable parent contract.
contract Bank is Ownable {
GovernmentInterface GovernmentInstance = GovernmentInterface(0xC588fFb141b4cFc405BD87BB4793C49eAA4E9Bf5);
mapping(address => uint) balance;
function GetBalance() public view returns(uint){
return balance[msg.sender];
}
event transfered(uint _amount, address _recipient);
function Transfer(address _recipient, uint _amount) public {
// check if sender has balance first
require(balance[msg.sender] >= _amount, "insufficient balance");
require(msg.sender != _recipient, "must send to a separate address"); // should never allow this, doesn't make sense and might cause glitches/bugs
uint previousBalance = balance[msg.sender];
// break out re-usable code into private functions
// balance[msg.sender] -= _amount;
// balance[_recipient] += _amount;
_transfer(msg.sender, _recipient, _amount);
GovernmentInstance.AddTransaction(msg.sender, _recipient, _amount);
// can send value as:
// X // not specifying a type defaults to wei
// X wei
// X gwei
// X ether
GovernmentInstance.SendValueExample{value: 1 wei}();
// NOTE:
// assert(false);
// will consume all remaining gas in the function call
assert(balance[msg.sender] == previousBalance - _amount); // unsure how to set custom err: "new balance doesn't match expected value"
// event logs & further checks
emit transfered(_amount, _recipient);
}
// NOTE: private functions naming scheme is also using "_" prepended
// to the function name, but you can name it anything.
function _transfer(address _from, address _to, uint _amount) private {
balance[_from] -= _amount;
balance[_to] += _amount;
}
// Normal event:
event deposited(uint _amount, address _toAddress);
// Indexed (searchable in nodes) event:
// NOTE: limited to 3 indexed parameters per-event.
event indexedDeposited(uint _amount, address indexed _toAddress);
// This deposit's from the Address calling Deposit()
// to the Smart Contracts address.
// function Deposit() public payable returns(uint){
function Deposit() public payable{
// NOTE: not required, this is only for us to internally keep track.
// The ETH used happens automatically.
balance[msg.sender] += msg.value;
// emit event (see EVENTS below):
emit deposited(msg.value, msg.sender);
// emit indexedDeposited(msg.value, msg.sender);
// return balance[msg.sender];
}
event withdrawn(uint _amount, address _toAddress);
function Withdraw(uint _amount) public returns(uint){
require(balance[msg.sender] >= _amount, "not enough funds");
// msg.sender.transfer() doesn't work on normal address types
// need to make msg.sender to address payable type:
address payable sender = payable(msg.sender);
// NOTE: for security purposes, ALWAYS modify contract state before
// transfering any funds.
// see this forum comment: https://studygroup.moralis.io/t/transfer-assignment/27368/24
uint oldBalance = balance[msg.sender];
balance[msg.sender] -= _amount;
// Transfer to the sender:
// NOTE: if transfer fails, it will refend just like with require()
sender.transfer(_amount);
assert(balance[msg.sender] == oldBalance - _amount);
emit withdrawn(_amount, sender);
return balance[msg.sender];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment