BD Flashcards
The difference between –save and –save-dev is
–save gets added to the dependencies and –save-dev gets added to devDependencies which is libraries you only need for your development environment
The Zsh equivalent of .bash_profile is
.zshenv
note: Zsh does not source .bash_profile, .bashrc
The first line in a solidity contract is
pragma solidity >=0.7.0 <0.9.0;
Contract names are written in
contract CapitalCase
To write a getter function in solidity, type
function get() public view returns(string) {
return var_name;
}
or
string public varName;
Note: Setters are not generated, only getters for public/external top level variables
Variable names are written in
camelCase
The 4 contract memory locations are
storage, memory, stack, calldata
storage: stored in the blockchain
memory: stored in memory and disappears after function runs, can be passed to other functions
stack: a variable that only exists inside a function and ceases to exist after function runs. A plain variable declaration in a function is automatically stored in stack.
calldata: Must be used for arguments to functions that are public or external. Like, literally data from a call.
To create an array, type
type[10]
arrays in solidity must all have the same type
To create a variable for an ethereum address type
address varName = “string”
Top level variables that have no memory location tag are automatically saved to
storage
To write a setter function in solidity, type
function set(string _paramName) external {
varName = _paramName;
}
The 4 function visibility keywords are
private: Only the smart contract can call the function (use underscore on func name as convention)
internal: Only the smart contract can call the function or the smart contract that inherited this function
external: Can only be called from outside the smart contract
public: Can be called from inside and outside the contract
The 4 function visibility keywords are
private, internal, external, public
private: Only the smart contract can call the function (use underscore on func name as convention)
internal: Only the smart contract can call the function or the smart contract inherited this function
external: Can only be called from outside the smart contract
public: Can be called from inside and outside the contract
Variables with no visibility keyword default to
private
Remix will not let me deploy without starting with
// SPDX-License-Identifier: GPL-3.0
People can only run your smart contract’s functions on ether scan if you
upload your source code
An interface is
A group of function signatures (everything before the curly braces) that match ones present in a different contract. You define them in order to be able to call that remote contract’s functions in your own contract.
Each interface only connects to one contract.
interface MyInterface {
function remoteFunc() external view returns (uint);
}
Then to call the remote function in my contract, type
MyInterface(remoteContractAddress).remoteFunc();
Note: For some reason, interface functions must be external even though the original function might be public and it still works.
I do not need to copy the virtual/override from the signature.
msg.sender is
the address of the person or contract calling your function. The msg variable is global.
msg.value is
the amount of gwei being sent to your contract from the person calling your function.
The difference between tx.origin and msg.sender is
tx.origin is the original person to initiate a transaction and msg.sender can be an intermediate contract triggered by the original person.
To throw an error if a value is negative, type
require(false, “Error message”)
To send ether to an address, type
(bool success, ) = address(“address”).call{value: 1 ether}(“”);
require(success);
For a smart contract to receive ether, it must have at least one function with
a payable tag
To make your contract able to receive ether, add the function
receive() external payable { … }
Or make any function payable, but that might require the sender to call it in a more sophisticated way (like in a UI or using an interface), rather than simple send from any wallet.
note: Contracts can only have one receive function
The fallback() function is called when someone
calls a contract with non empty calldata but no other function matches
To make an array of payable addresses, type
address payable[] public arrayVar
To set the owner of a contract to the person that deploys it, type
address owner;
address param;
constructor(string passedInParam) {
// initialize the owner on deployment
owner = msg.sender;
param = passedInParam;
}
Note: Useful for when you need one person to be able to run important functions.
You need to initialize the empty variables above the constructor before it updates them.
To cast a msg.sender address into a payable address, type
payable(msg.sender)
To add to an array at the end, type
arrayName.push(itemName)
To get the balance of your own smart contract, type
address(this).balance
To remove all the elements of an array, type
delete arrayName
If variables are declared outside a function, changing them inside a function will still
update the storage on the blockchain
The protocol id for NFTs is
ERC-721
To inherit the functions and variables from a parent contract, type
contract ChildContract is ParentContract {
}
To pass an argument to the constructor function of a parent contract, type
constructor() public ParentContractName(“param_1”) { … }
To inherit multiple contracts, type
contract ChildContract is ParentContract1, ParentContract2 { … }
If multiple parents have the same function, the last in the list overrides the previous. The convention is to order from most base to most derived.
To inherit multiple contracts, type
contract ChildContract is ParentContract1, ParentContract2 { … }
If multiple parents have the same function, the last in the list overrides the previous
The difference between a contract that is basic vs derived
derived inherits from more contracts
To create an event, type
event EventName(type varName, type varName)
Note: using “indexed” makes that field filterable, but its expensive.
to emit an event, type
emit EventName(varName,varName)
To allow external consumers to filter out events based on the value on one of the fields, type
event EventName(type indexed varName, type varName)
Only 3 fields can have indexed.
Events cannot be read by
smart contracts. They are read by listeners using web3/ethers.
Use the storage memory location if you need
the variable change to persist after the function ends/create a transaction. It writes to the blockchain.
To set unique permissions on a function use
require() at the beginning of it so the function errors if the expression is false
A good way to model people into a variable is
struct Person {
uint height;
bool alive;
address friend;
}
mapping(address => Person) public people;
To create a modifier that only allows the owner to run a function, type
modifier onlyOwner() {
require(owner == msg.sender, “Ownable: caller is not the owner”);
_;
}
import “@openzeppelin/contracts/access/Ownable.sol”;
contract MyContract is Ownable {
function myFunc() public onlyOwner {}
}
To map an NFT to an owner, type
mapping(uint256 => address) private _owners;
Dom did it like this:
// Mapping from owner to list of owned token IDs
mapping(address => mapping(uint256 => uint256)) private _ownedTokens;
// Mapping from token ID to index of the owner tokens list mapping(uint256 => uint256) private _ownedTokensIndex;
You cannot know what NFTs an address holds because
there is no index to search. You would either have to use a function in the smart contract that maps an owner address to an NFT id, or set up a listener for Transfer events that constantly updates your database.
To verify a user has the key to a wallet, you essentially need to
create a random nonce, have them sign it with metamask.
Cryptokitties has the correct workflow.
Metamask automatically injects
an object called ethereum that you can call functions on to do stuff
To see a user’s wallet addresses using metamask, type
ethereum.request({ method: ‘eth_requestAccounts’ }).then(data => console.log(data))
To sign a message using metamask, type
Note: In order use this, you must first use ‘eth_requestAccounts’
var from = “0xf7c3abd77184cdbc9417890820b3ffcfe4bcd333”
var msg = “Hi there” // Prob need to cast to hex
var params = [msg, from]
var method = ‘personal_sign’
ethereum.request({method, params}).then(data => console.log(data))
or
ethereum.request({method, params, from}).then(data => console.log(data))
After I sign into cryptokitties, they send my wallet address and my encrypted message to their server and their server sends me back another token. If I can successfully extract their real wallet address from the signed message just using the initial message and the token, then it means they successfully signed it with their secret.
For some reason crypto kitties sends back a token and saves it under a tokens cookie after my wallet address.
“To summarize this block, what it does is, given our msg (containing the nonce) and our signature, the ecrecover function outputs the public address used to sign the msg. If it matches our publicAddress from the request body, then the user who made the request successfully proved their ownership of publicAddress. We consider them authenticated.”
This explains the backend decently.
https://www.toptal.com/ethereum/one-click-login-flows-a-metamask-tutorial#lets-build-it-together
This post shows the steps necessary to get public address from signed message
https://ethereum.stackexchange.com/questions/12571/getting-an-address-from-ethereumjs-utils-ecrecover
ethereumJSUtils works in node
https://github.com/ethereumjs/ethereumjs-util
Closest solution
https://ethereum.stackexchange.com/questions/35486/how-to-verify-metamask-account-holder-is-the-real-owner-of-the-address?noredirect=1&lq=1
https://ethereum.stackexchange.com/questions/35486/how-to-verify-metamask-account-holder-is-the-real-owner-of-the-address
https://ethereum.stackexchange.com/questions/54715/metamask-verification-on-a-server-with-web3-personal-sign?noredirect=1&lq=1
https://github.com/danfinlay/js-eth-personal-sign-examples/blob/master/index.js
How synthetic loot works is basically
it uses your wallet address as a seed to deterministically choose all the items. If you make a ton of wallets, then it will make a ton of loots.
Adding the –global tag to npm install makes
makes the package available in any shell and not just inside the current project
npm only manages
server side packages. Not front end. There are other front end package managers. It’s called NODE package manager.
To signal the intent that a function can be overridden by a contract that inherits it
add the modifier “virtual”
function myFunc() virtual public returns(uint) {…}
To run the scripts with async functions in hardhat reliably, people usually
use an async function and then put await before all async calls
To control a contract using ethers, type
Ethers always expects you to give it and returns BigNumbers instead of regular js numbers. eg ethers.BigNumber.from(5)
To pass values to a contract constructor deployed by ethers
add as arguments to the deploy function.
Ethers.js returns the transaction data instead of the contract function return value, if the function requires creating a transaction. This means if I want to get the return, I’ll need to set it to a function and then create a getter function.
To deploy a contract
const contractTemplate = await ethers.getContractFactory(“Token”)
const myDeployedContract = await contractTemplate.deploy()
await myDeployedContract.deployed()
then you can use
myDeployedContract.address
await myDeployedContract.contractFunction()
Note: Contracts must have an owner. Ethers automatically uses the first signer from const [owner] = await ethers.getSigners() as the owner. It is best to call that ourselves as well so we can have access to it.
To call a contract function from a different address than owner, type
const [owner, addr1, addr2] = await ethers.getSigners()
await myDeployedContract.connect(addr1).contractFunction()
To explicitly choose the address of the signer, type
accounts = await window.ethereum.request({ method: ‘eth_requestAccounts’ });
provider = new ethers.providers.Web3Provider(window.ethereum);
signer = provider.getSigner(accounts[0]);
To create a signer, type
new ethers.Wallet(private_key, optional_provider)
To call a payable function, you need to add as a third argument
overrides = {
// To convert Ether to Wei:
value: ethers.utils.parseEther(“1.0”) // ether in this case MUST be a string
// Or you can use Wei directly if you have that:
// value: someBigNumber
// value: 1234 // Note that using JavaScript numbers requires they are less than Number.MAX_SAFE_INTEGER
// value: “1234567890”
// value: “0x1234”
// Or, promises are also supported:
// value: provider.getBalance(addr)
};
await MintingPress.mintToken(1, overrides)
To return a public variables in ethers, just call the exact variable name as a function
To create an event listener in ethers, type
filter = {
address: dwede, // Omit to listen to all addresses
topics: [
utils.id(“Transfer(address,address,uint256)”),
]
}
provider.on(filter, (data) => { … })
in hardhat, ethers is added to the global scope in the scripts folder, so you dont need to instantiate it, it instantiates based on your default config or the flags you add when running a script.
In ethers a provider can x while a signer can y
providers can read a smart contract
signers (have access to private key) so can create transactions aka write to blockchain
To use ethers to interact with a contract on a testnet or mainnet, you need to put the script inside scripts folder and run hh run scripts/script.js –network network-name and it already has your ethers object set up.
It seems that in hardhard, getting a instantiating a signer uses the api in the config for the wallet, but getting a provider you need pass in your api info during instantiation.
You can guess which javascript functions will take a long time by
asking if this action is probably going to take a long time to complete
You must use “await” if the function you are calling
returns a promise
To add OpenZeppelin to hardhat, type
npm install –save-dev @openzeppelin/contracts
An abstract class is
a base class that you can inherit into a child class but contains functions that are declared but have no body that you will be forced to override them in order to instantiate the child class.
To override a virtual function, type
function myFunc() public override(ContractName) {}
A modifier is
A function that you add after your visibility keyword that will run before your function.
A modifier can access the parameters passed into the main function by using parentheses.
myModifier(paramName)
You can use multiple modifiers and they will run in sequence.
To create a modifier type
modifier myModifer() {
// do something
_;
}
The underscore in modifiers represents calls the main function or the next modifier in the chain of there is one.
https://youtu.be/RobaQulUzsY
The basic signature of a function is
function functionNams(type paramName, type paramName) visibility modifier modifier returns(type) {}
To return multiple values from one function, type
function multiFunc() visibility modifier returns(type, type) {
return(valueOne, valueTwo);
}
And to assign each returned value, type
variableOne, variableTwo := multiFunc()
For struct, array and mapping types, it is mandatory to
Explicit data location for all variables of struct, array or mapping types is now mandatory. This is also applied to function parameters and return variables.
write the explicit data location keyword including for function params and return variables.
uint[] storage myArray;
function myFunc(uint[] memory myArray) visibility modifier returns(uint[] memory) {}