|
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]; |
|
} |
|
} |