Skip to content

Instantly share code, notes, and snippets.

@tomstorms
Last active January 14, 2022 10:58
Show Gist options
  • Select an option

  • Save tomstorms/5d1d41207914c0161594df1572557ced to your computer and use it in GitHub Desktop.

Select an option

Save tomstorms/5d1d41207914c0161594df1572557ced to your computer and use it in GitHub Desktop.
#-----------------------------------------------------
# To learn, see: https://cryptozombies.io
#-----------------------------------------------------
# Terminology
wei = the smallest sub-unit of Ether — there are 10^18 wei in one ether
"Proof of Work" = first to solve a computationally-intensive mathematical problem
ERC20 token = used in exchanges as a cryptocurrency
ERC721 token = crypto-collectible; unique
#-----------------------------------------------------
# Types
uint dnaDigits = 16;
uint levelUpFee = 0.001 ether; // 0.001 = the value; ether = the unit
struct Zombie {
string name;
uint dna;
}
Example on saving gas:
- uint will always assign 256; not efficient
struct NormalStruct {
uint a;
uint b;
uint c;
}
struct MiniMe {
uint32 a;
uint32 b;
uint c;
}
// `mini` will cost less gas than `normal` because of struct packing
NormalStruct normal = NormalStruct(10, 20, 30);
MiniMe mini = MiniMe(10, 20, 30);
#-----------------------------------------------------
# Mapping
// public
mapping (uint => address) public zombieToOwner;
zombieToOwner[id] = msg.sender;
mapping (address => uint) ownerZombieCount;
ownerZombieCount[msg.sender]++;
#-----------------------------------------------------
# Events
event NewZombie(uint zombieId, string name, uint dna);
emit NewZombie(id, _name, _dna);
#-----------------------------------------------------
# Functions
// private doesn't return
function _createZombie(string memory _name, uint _dna) private {
// private returns
function _generateRandomDna(string memory _str) private view returns (uint) {
// public
function createRandomZombie(string memory _name) public {
// public returns
function whatIsMyNumber() public view returns (uint) {
// payable function
function buySomething() external payable {
function modifiers:
private = callable from other functions inside the contract
internal = callable from other functions inheriting the contract
external = called outside the contract; cant be called inside contract
public = called anywhere, internal + external
view = no data will be saved/changed in the function
pure = does not read or write data from the blockchain
both dont cost gas if called externally; will cost if called internally
payable = receive Ether. the contract will hold the ether value until withdrawn
#-----------------------------------------------------
# Modifier
modifier olderThan(uint _age, uint _userId) {
require (age[_userId] >= _age);
_;
}
#-----------------------------------------------------
# Security
- examine all your public and external functions
- overflows and underflows
#-----------------------------------------------------
# Interface with other contracts
// Example External contract:
contract LuckyNumber {
mapping(address => uint) numbers;
function setNum(uint _num) public {
numbers[msg.sender] = _num;
}
function getNum(address _myAddress) public view returns (uint) {
return numbers[_myAddress];
}
}
// Our interface to the example external contract (you can copy and paste the function declaration from the external contract);
contract NumberInterface {
function getNum(address _myAddress) public view returns (uint);
}
// Example to connect the interface with the contract address
contract MyContract {
address NumberInterfaceAddress = 0xab38...
// ^ The address of the FavoriteNumber contract on Ethereum
NumberInterface numberContract = NumberInterface(NumberInterfaceAddress);
// Now `numberContract` is pointing to the other contract
function someFunction() public {
// Now we can call `getNum` from that contract:
uint num = numberContract.getNum(msg.sender);
// ...and do something with `num` here
}
}
dealing with return types:
// example of function
function getKitty(uint256 _id) external view returns (
bool isGestating,
bool isReady,
uint256 cooldownIndex,
uint256 nextActionAt,
uint256 siringWithId,
uint256 birthTime,
uint256 matronId,
uint256 sireId,
uint256 generation,
uint256 genes
);
// in a function you can return and assign data like this:
function feedOnKitty(uint _zombieId, uint _kittyId) public {
uint kittyDna;
(,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
// kittyDna = you can now use this as a variable in this function
feedAndMultiply(_zombieId, kittyDna);
}
#-----------------------------------------------------
# for loop
uint counter = 0;
for (uint i = 1; i <= 10; i++) {
// If `i` is even...
if (i % 2 == 0) {
// Add it to our array
evens[counter] = i;
// Increment counter to the next empty index in `evens`:
counter++;
}
}
#-----------------------------------------------------
# Global Variables/Functions
msg.sender = the address of the person who called the function
msg.value = value of ether sent to the contract
ether = built-in unit
require() = throw error and stop if true
; = required on every line
storage = permanently store in blockchain (state variables)
memory = temporarily store in blockchain
now = current unix timestamp
seconds, minutes, hours, days, weeks. years = time variables
calldata = is somehow similar to memory, but it's only available to external functions
view = only read data from the blockchain. dont cost gas. no need to create a transaction to read data
overflow = number resets past it's maximum
underflow = subtracting and going into negative
contract = keyword for building a function
library = keyword for allowing us to use the using keyword and extract methods from a library
indexed = use data for filtering
use // for single line comments
use /* */ for multi line comments
natspec format:
@title and @author are straightforward
@notice explains to a user what the contract / function does
@dev is for explaining extra details to developers.
@param and @return are for describing what each parameter and return value of a function are for
#-----------------------------------------------------
# Inheritance
contract ZombieFactory {}
contract ZombieFeeding is ZombieFactory {}
// multiple
contract SatoshiNakamoto is NickSzabo, HalFinney {}
Use different files:
import "./someothercontract.sol";
#-----------------------------------------------------
# OpenZeppelin
OpenZeppelin's Ownable contract
import "./ownable.sol";
contract ZombieFactory is Ownable {
// then you can use the onlyOwner modifier
function setKittyContractAddress(address _address) external onlyOwner {
owner() and onlyOwner from the Ownable contract
function withdraw() external onlyOwner {
address payable _owner = address(uint160(owner()));
_owner.transfer(address(this).balance);
}
SafeMath library = prevents overflow/underflows
import "./safemath.sol";
contract ZombieFactory is Ownable {
using SafeMath for uint256;
using SafeMath32 for uint32;
using SafeMath16 for uint16;
#-----------------------------------------------------
# keccak256
Random number generation = this can be hackable! use an oracle
keccak256 hash function
random number between 1 and 100;
uint random = uint(keccak256(abi.encodePacked(now, msg.sender, randNonce))) % 100;
#-----------------------------------------------------
# FRONTEND
Web3.js:
- cryptoZombies = new web3js.eth.Contract(cryptoZombiesABI, cryptoZombiesAddress);
- call is used for view and pure functions
- return cryptoZombies.methods.zombieToOwner(id).call()
- send function changes data in your smart contract; will cost gas
subscribe to events:
cryptoZombies.events.NewZombie().on("data", function(event) {
let zombie = event.returnValues;
// We can access this event's 3 return values on the `event.returnValues` object:
console.log("A new zombie was born!", zombie.zombieId, zombie.name, zombie.dna);
}).on("error", console.error);
can filter data in the events wherever the indexed keyword is used:
cryptoZombies.events.Transfer({ filter: { _to: userAccount } }).on("data", function(event) {
let data = event.returnValues;
// The current user just received a zombie!
// Do something here to update the UI to show it
}).on("error", console.error);
#-----------------------------------------------------
# Metamask
var accountInterval = setInterval(function() {
// Check if account has changed
if (web3.eth.accounts[0] !== userAccount) {
userAccount = web3.eth.accounts[0];
// Call a function to update the UI with the new account
getZombiesByOwner(userAccount)
.then(displayZombies);
}
}, 100);
#-----------------------------------------------------
# Truffle Tests
- Setup and initialise class for tests
let [alice, bob] = accounts;
beforeEach(async () => {
contractInstance = await CryptoZombies.new();
});
- To group tests, Truffle provides a function called context:
xcontext("with the single-step transfer scenario", async () => {
it("should transfer a zombie", async () => {
})
})
- combine with Chai:
var expect = require('chai').expect;
let lessonTitle = "Testing Smart Contracts with Truffle";
expect(lessonTitle).to.be.a("string");
#-----------------------------------------------------
# Truffle Config (truffle-config.js)
- Make sure your Solidity version matches your contracts. You can override Truffle config:
{
...
compilers: {
solc: {
version: "0.8.0"
#-----------------------------------------------------
# Truffle Commands
- truffle init = starts a new project
- truffle develop = starts the cli development environment
Once the cli is running you can use the following commands:
- migrate --reset = deploy contracts and ignore cache
- migrate --reset --network kovan = deploy contracts to kovan network. make sure the settings are right in truffle-config.js
- deploy --compile-none --network bsctestnet = will display to the network
- test = executes any tests up
#-----------------------------------------------------
# Import Truffle web3 accounts into Metamask
- truffle develop - will provide you the following:
...
Mnemonic: Strange Purple Adamant Crayons Entice Fun Eloquent Missiles
^ this of course is random, but you need to copy this phrase
- Go to your metamask
- Logout of any existing accounts
- Then on the home of the metamask click "Import using account seed phase"
- Paste the mnemonic phrase from truffle
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment