
We will providing github repository and some cool exercises in the later titorial as we will progress.
Remix is an IDE for the smart contract programming language Solidity and has an integrated debugger and testing environment.
Its an online IDE where we code our smart contracts just like visual code studio. You dont need to download it to work with it just go to http://remix.ethereum.org start playing with it. Here in this lecture we are providing description about the IDE basics as we progress in the tutorial you will become a pro on using remix and coding your smart contracts.
Solidity is a contract-oriented, high-level language for implementing smart contracts. It was influenced by C++, Python and JavaScript and is designed to target the Ethereum Virtual Machine (EVM).
Solidity is statically typed, supports inheritance, libraries and complex user-defined types among other features.
As you will see, it is possible to create contracts for voting, crowdfunding, blind auctions, multi-signature wallets and more.
As we will progress in the tutorial we will master Solidity. In this lecture we will get familiar will solidity basic contract.
Solidity is a statically typed language, which means that the type of each variable (state and local) needs to be specified at compile-time. Solidity provides several elementary types which can be combined to form complex types.
Contract has two kind of variables State Variables and memory variables.
State variables are permanently stored in contract storage. This means they're written to the Ethereum blockchain. Think of them like writing to a DB.
In this lecture we will get familiar with variiable.
We provide github repository in the later tutorials, Till then just play with solidity and created some cool functions!
A function declaration in solidity looks like the following:
function myFuntion(string _name, uint _amount) {
}
Note: It's convention (but not required) to start function parameter variable names with an underscore (_) in order to differentiate them from global variables. We'll use that convention throughout our tutorial.
In Solidity, functions are public by default. This means anyone (or any other contract) can call your contract's function and execute its code.
Obviously this isn't always desireable, and can make your contract vulnerable to attacks. Thus it's good practice to mark your functions as private by default, and then only make public the functions you want to expose to the world.
This means only other functions within our contract will be able to call this function.
we use the keyword private after the function name. And as with function parameters, it's convention to start private function names with an underscore (_).
We will learn about types of function in depth in this tutorial and implement them in our game.
The Ethereum blockchain is made up of accounts, which you can think of like bank accounts. An account has a balance of Ether (the currency used on the Ethereum blockchain), and you can send and receive Ether payments to other accounts, just like your bank account can wire transfer money to other bank accounts.
Each account has an address, which you can think of like a bank account number. It's a unique identifier that points to that account, and it looks like this:
0x3d4079B588630918f8966460CdB0908d71A551a3
We'll get into the depth of addresses in a later lesson, but for now you only need to understand that an address is owned by a specific user (or a smart contract).
So we can use it as a unique ID for ownership of our fighters. When a user creates new fighter by interacting with our app, we'll set ownership of those fighter to the Ethereum address that called the function.
Every contract file in solidity need to start with compiler version:
pragma solidity ^0.4.19;
Pragma solidity is the way to tell the EVM(Ethereum virtual machine) about the version of our file.
To create the smart contract we have to use key word 'contract' followed by its name and close them in braces.
contract FighterAvatar {
}
we will introduce 2 uint (uint256) variable in contract, in Solidity you can use ** as exponential power.
uint fighterSkill = 16;
uint fighterSkillModulus = 10 ** 16;
This fighter skill will belong to our fighter which will be a random string it will make sure it is only 16 character long.
/* our whole source file should look like this after: */
contract FighterAvatar {
uint fighterSkill = 16;
uint fighterSkillModulus = 10 ** 16;
}Solidity provides a way to define new types in the form of structs, which is shown in the following example:
struct Funder {
address addr;
uint amount;
}
Structs allow you to create more complicated data types that have multiple properties.
Let's create our FIghter struct with name and skill properties:
struct Fighter {
string name;
uint skill;
}
When you want a collection of something, you can use an array. There are two types of arrays in Solidity: fixed arrays and dynamic arrays:
// Array with a fixed length of 2 elements: uint[2] fixedArray; // another fixed Array, can contain 5 strings: string[5] stringArray; // a dynamic Array - has no fixed size, can keep growing: uint[] dynamicArray;
To store our Fighters we will create a fighters array:
Fighter[] public fighters;
/* Our overall contract will look like*/
pragma solidity ^0.4.19;
contract FighterAvatar {
uint fighterSkill = 16;
uint fighterSkillModulus = 10 ** 16;
struct Fighter {
string name;
uint skill;
}
Fighter[] public fighters;
}In this lecture we are going to create a function for our fighter and learn about keccak256 cryptographic hash function.
Let's create a private function _createFighter it is going to accept 2 arguments, and dont forget to add underscore for arguments and private functions:
function _createFighter(string _name, uint _skill)
our function should look like:
function _createFighter(string _name, uint _skill) internal {
fighters.push(Fighter(_name, _skill);
}Keccak256:
We want our _generateRandomSkill function to return a (semi) random uint. How can we accomplish this?
Ethereum has the hash function keccak256 built in, which is a version of SHA3. A hash function basically maps an input string into a random 256-bit hexidecimal number. A slight change in the string will cause a large change in the hash.
It's useful for many purposes in Ethereum, but for right now we're just going to use it for pseudo-random number generation.
Example:
//6e91ec6b618bb462a4a6ee5aa2cb0e9cf30f7a052bb467b0ba58b8748c00d2e5
keccak256("aaaab");
//b1f078126895a1424524de5321b339ab00408010b7cf0e6ed451514981e58aa9
keccak256("aaaac");
As you can see, the returned values are totally different despite only a 1 character change in the input.
This function is perfect to generate skill for our fighter and make sure it is only 16 digits:
function _generateRandomSkill(string _name) private view returns(uint) {
uint skill = uint(keccak256(_name));
return skill % fighterSkillModulus ;
}and in the end create a public function which is going to call these functions and crating and storing the fighter list:
function generateRandomFighter(string _name) public {
require(ownerFighterCount[msg.sender] == 0);
uint randomSkill = _generateRandomSkill(_name);
_createFighter(_name, randomSkill);
}/* Our code will look like this */
pragma solidity ^0.4.19;
contract FighterAvatar {
uint fighterSkill = 16;
uint fighterSkillModulus = 10 ** 16;
struct Fighter {
string name;
uint skill;
}
Fighter[] public fighters;
function _createFighter(string _name, uint _skill) internal {
fighters.push(Fighter(_name, _skill)
function _generateRandomSkill(string _name) private view returns(uint) {
uint skill = uint(keccak256(_name));
return skill % fighterSkillModulus ;
}
function generateRandomFighter(string _name) public {
require(ownerFighterCount[msg.sender] == 0);
uint randomSkill = _generateRandomSkill(_name);
_createFighter(_name, randomSkill);
}
}In this lecture we are going to learn about Mappings in Solidity while building our fighter army.
Mappings are another way of storing organized data in Solidity.
Defining a mapping looks like this:
// For a financial app, storing a uint that holds the user's account balance: mapping (address => uint) public balanceAccount;
A mapping is essentially a key-value store for storing and looking up data. In the above example, the key is an address and the value is a uint.
In our game we need two mapping :
first mapping to store which fighter belongs to which address:
mapping (uint => address) public fighterToOwner;
second mapping to store count of how many fighters belongs to a particular address:
mapping (address => uint) ownerFighterCount;
In this lecture we are going to learn about msg Global variable.
These are special variables and functions which always exist in the global namespace and are mainly used to provide information about the blockchain or are general-use utility functions.
msg.data (bytes): complete calldatamsg.gas (uint): remaining gas - deprecated in version 0.4.21 and to be replaced by gasleft()msg.sender (address): sender of the message (current call)msg.sig (bytes4): first four bytes of the calldata (i.e. function identifier)msg.value (uint): number of wei sent with the messagemsg.sender (address): gives us the address of user which is carrying out the transaction or calling the function on the block chain.
so lets map the newly created fighter from our create function to its owner:
you can get the id of the created fighter after pushing the array and sutracting 1 from it:
uint id = fighters.push(Fighter(_name, _skill, 1, uint32(now + cooldDownTimer), 0, 0)) - 1;
map our fighter to address by:
fighterToOwner[id] = msg.sender;
and finally increase the count of owner as by doing '++' which is same as '+ 1'.
ownerFighterCount[msg.sender]++;
so our _createFighter will look like:
function _createFighter(string _name, uint _skill) internal {
uint id = fighters.push(Fighter(_name, _skill) - 1;
fighterToOwner[id] = msg.sender;
ownerFighterCount[msg.sender]++;
}In this lecture we are going to learn about require.
We created fighter by calling generateRandomFighter and entering a name. However, if users could keep calling this function to create unlimited fighter in their army, the game wouldn't be very fun.
Let's make it so each player can only call this function once. That way new players will call it when they first start the game in order to create the initial fighter in their army.
How can we make it so this function can only be called once per player?
For that we use require. require makes it so that the function will throw an error and stop executing if some condition is not true:
function stopFunction(string _name) public returns (string) {
// Compares if _name equals "stop". Throws an error and exits if not true.
// (Side note: Solidity doesn't have native string comparison, so we
// compare their keccak256 hashes to see if the strings are equal)
require(keccak256(_name) == keccak256("stop"));
// If it's true, proceed with the function:
return "done!";
}
If you call this function with stopFunction("stop"), it will return "done!". If you call it with any other input, it will throw an error and not execute.
Thus require is quite useful for verifying certain conditions that must be true before running a function.
So in our case we are going to stop the user from creating multiple fighter if he already have a fighter by checking our count mapping i.e how many fighter he own:
require(ownerFighterCount[msg.sender] == 0);
and our function looks like this:
function generateRandomFighter(string _name) public {
require(ownerFighterCount[msg.sender] == 0);
uint randomSkill = _generateRandomSkill(_name);
_createFighter(_name, randomSkill);
}
/* Our contract looks like */
pragma solidity ^0.4.19;
contract FighterAvatar {
uint fighterSkill = 16;
uint fighterSkillModulus = 10 ** 16;
struct Fighter {
string name;
uint skill;
}
Fighter[] public fighters;
mapping (uint => address) public fighterToOwner;
mapping (address => uint) ownerFighterCount;
function _createFighter(string _name, uint _skill) internal {
uint id = fighters.push(Fighter(_name, _skill) - 1;
fighterToOwner[id] = msg.sender;
ownerFighterCount[msg.sender]++;
}
function _generateRandomSkill(string _name) private view returns(uint) {
uint skill = uint(keccak256(_name));
return skill % fighterSkillModulus ;
}
function generateRandomFighter(string _name) public {
require(ownerFighterCount[msg.sender] == 0);
uint randomSkill = _generateRandomSkill(_name);
_createFighter(_name, randomSkill);
}
}
In this lecture we are going to learn about Inheritance.
Our game code is getting quite long. Rather than making one extremely long contract, sometimes it makes sense to split your code logic across multiple contracts to organize the code.
One feature of Solidity that makes this more manageable is contract inheritance:
contract first {
function catchphrase() public returns (string) {
return "So Wow CryptoDoge";
}
}
contract second is fist {
function anotherCatchphrase() public returns (string) {
return "Such Moon BabyDoge";
}
}
second inherits from first. That means if you compile and deploy second, it will have access to both catchphrase() and anotherCatchphrase() (and any other public functions we may define on first).
This can be used for logical inheritance (such as with a subclass, a Catis an Animal). But it can also be used simply for organizing your code by grouping similar logic together into different contracts.
we use 'import ' statement to import our solidity files.
In Solidity, there are two places you can store variables — in storageand in memory.
Storage refers to variables stored permanently on the blockchain. Memory variables are temporary, and are erased between external function calls to your contract. Think of it like your computer's hard disk vs RAM.
Most of the time you don't need to use these keywords because Solidity handles them by default. State variables (variables declared outside of functions) are by default storage and written permanently to the blockchain, while variables declared inside functions are memory and will disappear when the function call ends.
However, there are times when you do need to use these keywords, namely when dealing with structs and arrays within functions:
contract ChoclateFactory {
struct Choclate {
string name;
string status;
}
Choclate[] Choclates;
function eatChoclate(uint _index) public {
// Choclate myChoclate = Choclates[_index];
// ^ Seems pretty straightforward, but solidity will give you a warning
// telling you that you should explicitly declare `storage` or `memory` here.
// So instead, you should declare with the `storage` keyword, like:
Choclate storage myChoclate = Choclates[_index];
// ...in which case `myChoclate` is a pointer to `Choclates[_index]`
// in storage, and...
myChoclate.status = "Eaten!";
// ...this will permanently change `Choclates[_index]` on the blockchain.
// If you just want a copy, you can use `memory`:
Choclate memory anotherChoclate = Choclates[_index + 1];
// ...in which case `anotherChoclate` will simply be a copy of the
// data in memory, and...
anotherChoclate.status = "Eaten!";
// ...will just modify the temporary variable and have no effect
// on `Choclates[_index + 1]`. But you can do this:
Choclates[_index + 1] = anotherChoclate;
// ...if you want to copy the changes back into blockchain storage.
}
}
Don't worry if you don't fully understand when to use which one yet — throughout this tutorial we'll tell you when to use storage and when to use memory, and the Solidity compiler will also give you warnings to let you know when you should be using one of these keywords.
For now, it's enough to understand that there are cases where you'll need to explicitly declare storage or memory!
So lets create a fightAndInclude function and point to our fighter on our blockchain in it:
function fightAndInclude(uint _fighterId, uint _skill) public {
require(fighterToOwner[_fighterId] == msg.sender);
Fighter storage myFighter = fighters[_fighterId];
}
In this tutorial we are going to fight a new fighter and add it to our army:
firstly we will make sure that the new fighter skill is of 16 digits by using our fighterSkillModulus:
_skill = _skill % fighterSkillModulus;
Next we will take average of our our fighter skilll and new fighter we can use storage fighter variable as it is pointing to original fighter on the blockchain:
uint newSkill = (myFighter.skill + _skill) / 2;
and finally call createFighter with "newName" for now:
_createFighter("newFighter", newSkill);
In this tutorial we are going to learn about Interrfaces:
For our contract to talk to another contract on the blockchain that we don't own, first we need to define an interface.
Let's look at a simple example. Say there was a contract on the blockchain that looked like this:
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];
}
}
This would be a simple contract where anyone could store their lucky number, and it will be associated with their Ethereum address. Then anyone else could look up that person's lucky number using their address.
Now let's say we had an external contract that wanted to read the data in this contract using the getNum function.
First we'd have to define an interface of the LuckyNumber contract:
contract NumberInterface {
function getNum(address _myAddress) public view returns (uint);
}
Notice that this looks like defining a contract, with a few differences. For one, we're only declaring the functions we want to interact with — in this case getNum — and we don't mention any of the other functions or state variables.
Secondly, we're not defining the function bodies. Instead of curly braces ({ and }), we're simply ending the function declaration with a semi-colon (;).
So it kind of looks like a contract skeleton. This is how the compiler knows it's an interface.
By including this interface in our dapp's code our contract knows what the other contract's functions look like, how to call them, and what sort of response to expect.
We'll get into actually calling the other contract's functions in the next lesson, but for now let's declare our interface for the CryptoKitties contract.
Here is the Interface to use:
contract kittyInterface {
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 this lecture we are going to learn how to initialise and interact with the contract. Here is how you initialise the contract from interface:
First get the address where the contract is:
address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
and then pass it into the constructor function which has same name as interface .
kittyInterface myKitty = kittyInterface(ckAddress);
In this lecture we will interact with our interface.
This getKitty function is the above example we've seen that returns multiple values. Let's look at how to handle them:
function multipleReturns() internal returns(uint a, uint b, uint c) {
return (1, 2, 3);
}
function processMultipleReturns() external {
uint a;
uint b;
uint c;
// This is how you do multiple assignment:
(a, b, c) = multipleReturns();
}
// Or if we only cared about one of the values:
function getLastReturnValue() external {
uint c;
// We can just leave the other fields blank:
(,,c) = multipleReturns();
}
so we will get the skill of our fighter from cat genes (as both are uint256) by handling multiple values like this:
function fightWithKitty(uint _fighterId, uint _kittyId) public {
uint kittySkill;
( , , , , , , , , , , kittySkill) = myKitty.getKitty(_kittyId);
fightAndInclude(_fighterId, kittySkill);
}
Up until now, Solidity has looked quite similar to other languages like JavaScript. But there are a number of ways that Ethereum DApps are actually quite different from normal applications.
To start with, after you deploy a contract to Ethereum, it’s immutable, which means that it can never be modified or updated again.
The initial code you deploy to a contract is there to stay, permanently, on the blockchain. This is one reason security is such a huge concern in Solidity. If there's a flaw in your contract code, there's no way for you to patch it later. You would have to tell your users to start using a different smart contract address that has the fix.
But this is also a feature of smart contracts. The code is law. If you read the code of a smart contract and verify it, you can be sure that every time you call a function it's going to do exactly what the code says it will do. No one can later change that function and give you unexpected results.
In above example, we hard-coded the CryptoKitties contract address into our DApp. But what would happen if the CryptoKitties contract had a bug and someone destroyed all the kitties?
It's unlikely, but if this did happen it would render our DApp completely useless — our DApp would point to a hardcoded address that no longer returned any kitties. Our fighters would be unable to feed on kitties, and we'd be unable to modify our contract to fix it.
For this reason, it often makes sense to have functions that will allow you to update key portions of the DApp.
For example, instead of hard coding the CryptoKitties contract address into our DApp, we should probably have a changeKittyContractAddressfunction that lets us change this address in the future in case something happens to the CryptoKitties contract.
/* our contract look like */
pragma solidity ^0.4.19;
import "./FighterAvatar.sol";
contract kittyInterface {
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
);
}
contract FighterWar is FighterAvatar {
// address of cryptokitties
address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
kittyInterface myKitty = kittyInterface(ckAddress);
function fightAndInclude(uint _fighterId, uint _skill) public {
require(fighterToOwner[_fighterId] == msg.sender);
Fighter storage myFighter = fighters[_fighterId];
_skill = _skill % fighterSkillModulus;
uint newSkill = (myFighter.skill + _skill) / 2;
_createFighter("newFighter", newSkill);
}
function fightWithKitty(uint _fighterId, uint _kittyId) public {
uint kittySkill;
( , , , , , , , , , , kittySkill) = myKitty.getKitty(_kittyId);
fightAndInclude(_fighterId, kittySkill);
}
function changeKittyContractAddress(address _address) public {
ckAddress = _address;
}
}Below is the Ownable contract taken from the OpenZeppelin Solidity library. OpenZeppelin is a library of secure and community-vetted smart contracts that you can use in your own DApps. After this lesson, we highly recommend you check out their site to further your learning!
Give the contract below a read-through. You're going to see a few things we haven't learned yet, but don't worry, we'll talk about them afterward.
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
function Ownable() public {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0));
OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
A few new things here we haven't seen before:
function Ownable() is a constructor, which is an optional special function that has the same name as the contract. It will get executed only one time, when the contract is first created.modifier onlyOwner(). Modifiers are kind of half-functions that are used to modify other functions, usually to check some requirements prior to execution. In this case, onlyOwner can be used to limit access so only the owner of the contract can run this function. We'll talk more about function modifiers in the next chapter, and what that weird _; does.indexed keyword: don't worry about this one, we don't need it yet.So the Ownable contract basically does the following:
When a contract is created, its constructor sets the owner to msg.sender (the person who deployed it)
It adds an onlyOwner modifier, which can restrict access to certain functions to only the owner
It allows you to transfer the contract to a new owner
onlyOwner is such a common requirement for contracts that most Solidity DApps start with a copy/paste of this Ownable contract, and then their first contract inherits from it.
Since we want to limit changeKittyContractAddress to onlyOwner, we're going to do the same for our contract.
In this lecture we are going to learn about modifiers and use them in our contract.
A function modifier looks just like a function, but uses the keyword modifier instead of the keyword function. And it can't be called directly like a function can — instead we can attach the modifier's name at the end of a function definition to change that function's behavior.
Let's take a closer look by examining onlyOwner:
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
We would use this modifier as follows:
contract MyContract is Ownable {
event LaughManiacally(string laughter);
// Note the usage of `onlyOwner` below:
function likeABoss() external onlyOwner {
LaughManiacally("Muahahahaha");
}
}
Notice the onlyOwner modifier on the likeABoss function. When you call likeABoss, the code inside onlyOwner executes first. Then when it hits the _; statement in onlyOwner, it goes back and executes the code inside likeABoss.
So while there are other ways you can use modifiers, one of the most common use-cases is to add quick require check before a function executes.
In the case of onlyOwner, adding this modifier to a function makes it so only the owner of the contract (you, if you deployed it) can call that function.
Note: Giving the owner special powers over the contract like this is often necessary, but it could also be used maliciously. For example, the owner could add a backdoor function that would allow him to transfer anyone's fighter to himself!
So it's important to remember that just because a DApp is on Ethereum does not automatically mean it's decentralized — you have to actually read the full source code to make sure it's free of special controls by the owner that you need to potentially worry about. There's a careful balance as a developer between maintaining control over a DApp such that you can fix potential bugs, and building an owner-less platform that your users can trust to secure their data.
so lets add onlyOwner modifier in our cangeKittyContractAddress so no one else can change the call the function except owner:
function changeKittyContractAddress(address _address) public onlyOwner{
ckAddress = _address;
}In Solidity, your users have to pay every time they execute a function on your DApp using a currency called gas. Users buy gas with Ether (the currency on Ethereum), so your users have to spend ETH in order to execute functions on your DApp.
How much gas is required to execute a function depends on how complex that function's logic is. Each individual operation has a gas cost based roughly on how much computing resources will be required to perform that operation (e.g. writing to storage is much more expensive than adding two integers). The total gas cost of your function is the sum of the gas costs of all its individual operations.
Because running functions costs real money for your users, code optimization is much more important in Ethereum than in other programming languages. If your code is sloppy, your users are going to have to pay a premium to execute your functions — and this could add up to millions of dollars in unnecessary fees across thousands of users.
Ethereum is like a big, slow, but extremely secure computer. When you execute a function, every single node on the network needs to run that same function to verify its output — thousands of nodes verifying every function execution is what makes Ethereum decentralized, and its data immutable and censorship-resistant.
The creators of Ethereum wanted to make sure someone couldn't clog up the network with an infinite loop, or hog all the network resources with really intensive computations. So they made it so transactions aren't free, and users have to pay for computation time as well as storage.
In previous lessons, we mentioned that there are other types of uints: uint8, uint16, uint32, etc.
Normally there's no benefit to using these sub-types because Solidity reserves 256 bits of storage regardless of the uint size. For example, using uint8 instead of uint (uint256) won't save you any gas.
But there's an exception to this: inside structs.
If you have multiple uints inside a struct, using a smaller-sized uintwhen possible will allow Solidity to pack these variables together to take up less storage. For example:
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);
For this reason, inside a struct you'll want to use the smallest integer sub-types you can get away with.
You'll also want to cluster identical data types together (i.e. put them next to each other in the struct) so that Solidity can minimize the required storage space. For example, a struct with fields uint c; uint32 a; uint32 b; will cost less gas than a struct with fields uint32 a; uint c; uint32 b; because the uint32 fields are clustered together.
Let's add two more properties to our fighter in the struct and use:
struct Fighter {
string name;
uint skill;
uint32 level;
uint32 readyTime;
}
where level tells us the level of fighter and readyTime let us about wheather the fighter is ready for next fight or not.
In this lecture we will learn about time units in solidity.
The level property is pretty self-explanatory. Later on, when we create a battle system, Fighter who win more battles will level up over time and get access to more abilities.
The readyTime property requires a bit more explanation. The goal is to add a "cooldown period", an amount of time a fighter has to wait after fighting or attacking before it's allowed to fight / attack again. Without this, the fighter could attack and multiply 1,000 times per day, which would make the game way too easy.
In order to keep track of how much time a fighter has to wait until it can attack again, we can use Solidity's time units.
Solidity provides some native units for dealing with time.
The variable now will return the current unix timestamp (the number of seconds that have passed since January 1st 1970). The unix time as I write this is
1524096125
Note: Unix time is traditionally stored in a 32-bit number. This will lead to the "Year 2038" problem, when 32-bit unix timestamps will overflow and break a lot of legacy systems. So if we wanted our DApp to keep running 20 years from now, we could use a 64-bit number instead — but our users would have to spend more gas to use our DApp in the meantime. Design decisions!
Solidity also contains the time units seconds, minutes, hours, days, weeks and years. These will convert to a uint of the number of seconds in that length of time. So 1 minutes is 60, 1 hours is 3600 (60 seconds x 60 minutes), 1 days is 86400 (24 hours x 60 minutes x 60 seconds), etc.
Here's an example of how these time units can be useful:
uint lastUpdated;
// Set `lastUpdated` to `now`
function updateTimestamp() public {
lastUpdated = now;
}
// Will return `true` if 5 minutes have passed since `updateTimestamp` was
// called, `false` if 5 minutes have not passed
function fiveMinutesHavePassed() public view returns (bool) {
return (now >= (lastUpdated + 5 minutes));
}
We can use these time units for our FIghtercooldownTimer feature:
uint cooldDownTimer = 1 days;
Since we added a level and readyTime to our Fighter struct in the previous chapter, we need to update _createFighter() to use the correct number of arguments when we create a new Fighter struct.
Update the fighters.push line of code to add 2 more arguments: 1 (for level), and uint32(now + cooldownTimer) (for readyTime).
Note: Theuint32(...)is necessary becausenowreturns auint256by default. So we need to explicitly convert it to auint32.
now + cooldownTimer will equal the current unix timestamp (in seconds) plus the number of seconds in 1 day — which will equal the unix timestamp 1 day from now. Later we can compare to see if this fighter'sreadyTimer is greater than now to see if enough time has passed to use the fighter.
our _createFighter function after adding the required parameter looks like:
function _createFighter(string _name, uint _skill) internal {
uint id = fighters.push(Fighter(_name, _skill, 1, uint32(now + cooldDownTimer))) - 1;
fighterToOwner[id] = msg.sender;
ownerFighterCount[msg.sender]++;
}In this lecture we are going to learn how to pass an entire struct as argument and implement coolDownTimer functionality to our fighter.
Now that we have a readyTimer property on our Fighter struct, let's jump to FighterWar.sol and implement a cooldDownTimer timer.
We're going to modify our fightAndInclude such that:
Fighting triggers a fighter cooldDownTimerdown, and
Fighter can't fight on kitties until their cooldDownTimer period has passed
This will make it so fighter can't just fight on with kitties and include all day. In the future when we add battle functionality, we'll make it so attacking other fighter also relies on the cooldDownTimer.
First, we're going to define some helper functions that let us set and check a fighter's readyTime.
You can pass a storage pointer to a struct as an argument to a privateor internal function. This is useful, for example, for passing around our Fighter structs between functions.
The syntax looks like this:
function _doStuff(Fighter storage _fighter) internal {
// do stuff with _fighter
}
This way we can pass a reference to our fighter into a function instead of passing in a fighrer ID and looking it up.
Start by defining a _triggerCoolDownFunction function. It will take 1 argument, _fighter, a Fighter storage pointer. The function should be internal.
The function body should set _fighter.readyTime to uint32(now + cooldownTime).
Next, create a function called _isReady. This function will also take a Fighter storage argument named _fighter. It will be an internal view function, and return a bool.
The function body should return (_fighter.readyTime <= now), which will evaluate to either true or false. This function will tell us if enough time has passed since the last time the fighter's fight.
function _triggerCoolDownFunction(Fighter storage myFighter) internal {
myFighter.readyTime = uint32(now + 1 days);
}
function _isReady(Fighter storage myFighter) internal view returns (bool) {
return (myFighter.readyTime < now);
}
/* Our contract looks like */
pragma solidity ^0.4.19;
import "./FighterAvatar.sol";
contract kittyInterface {
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
);
}
contract FighterWar is FighterAvatar {
// address of cryptokitties
address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
kittyInterface myKitty = kittyInterface(ckAddress);
function _triggerCoolDownFunction(Fighter storage myFighter) internal {
myFighter.readyTime = uint32(now + 1 days);
}
function _isReady(Fighter storage myFighter) internal view returns (bool) {
return (myFighter.readyTime < now);
}
function fightAndInclude(uint _fighterId, uint _skill) public {
require(fighterToOwner[_fighterId] == msg.sender);
Fighter storage myFighter = fighters[_fighterId];
_skill = _skill % fighterSkillModulus;
uint newSkill = (myFighter.skill + _skill) / 2;
_createFighter("newFighter", newSkill);
}
function fightWithKitty(uint _fighterId, uint _kittyId) public {
uint kittySkill;
( , , , , , , , , , , kittySkill) = myKitty.getKitty(_kittyId);
fightAndInclude(_fighterId, kittySkill);
}
function changeKittyContractAddress(address _address) public onlyOwner{
ckAddress = _address;
}
}
In this lecture we will learn a bit more about modifiers.
Previously we looked at the simple example of onlyOwner. But function modifiers can also take arguments. For example:
// A mapping to store a user's age:
mapping (uint => uint) public age;
// Modifier that requires this user to be older than a certain age:
modifier olderThan(uint _age, uint _userId) {
require(age[_userId] >= _age);
_;
}
// Must be older than 16 to drive a car (in the US, at least).
// We can call the `olderThan` modifier with arguments like so:
function driveCar(uint _userId) public olderThan(16, _userId) {
// Some function logic
}
You can see here that the olderThan modifier takes arguments just like a function does. And that the driveCar function passes its arguments to the modifier.
Let's try making our own modifier that uses the fighter's levelproperty to restrict access to special abilities.
In FighterHelper, create a modifier called aboveLevel. It will take 2 arguments, _level (a uint) and fighterId (also a uint).
The body should check to make sure fighters[_fighterId].levelis greater than or equal to _level.
Remember to have the last line of the modifier call the rest of the function with _;.
modifier aboveLevel(uint _level, uint fighterId) {
fighters[fighterId].level > _level;
_;
}
In this lecture we will give some special powers to fighter based on their level.
Now let's use our aboveLevel modifier to create some functions.
Our game will have some incentives for people to level up their fighters:
We'll implement these functions below. Here's the example code from the previous lesson for reference:
// A mapping to store a user's age:
mapping (uint => uint) public age;
// Require that this user be older than a certain age:
modifier olderThan(uint _age, uint _userId) {
require (age[_userId] >= _age);
_;
}
// Must be older than 16 to drive a car (in the US, at least)
function driveCar(uint _userId) public olderThan(16, _userId) {
// Some function logic
}
Create a function called changeName. It will take 2 arguments: _fighterId (a uint), and _newName (a string), and make it external. It should have the aboveLevel modifier, and should pass in 2 for the _level parameter. (Don't forget to also pass the _zombieId).
In this function, first we need to verify that msg.sender is equal to fighterToOwner[_fighterId]. Use a require statement.
Then the function should set fighters[_fighterId].name equal to _newName.
Create another function named changeSkill below changeName. Its definition and contents will be almost identical to changeName, except its second argument will be _newSkill (a uint), and it should pass in 20 for the _level parameter on aboveLevel. And of course, it should set the fighter's skill to _newSkill instead of setting the fighte's name.
The functions will look like this:
function changeName(uint _fighterId, string _newName) public aboveLevel(_fighterId, 2) {
fighters[_fighterId].name = _newName;
}
function changeSkill(uint _fighterId, uint _newSKill) public aboveLevel(_fighterId, 20) {
fighters[_fighterId].skill = _newSKill;
}Let's add one more function: our DApp needs a method to view a user's entire fighter army — let's call it getFightersByOwner.
This function will only need to read data from the blockchain, so we can make it a view function. Which brings us to an important topic when talking about gas optimization:
view functions don't cost any gas when they're called externally by a user.
This is because view functions don't actually change anything on the blockchain – they only read the data. So marking a function with viewtells web3.js that it only needs to query your local Ethereum node to run the function, and it doesn't actually have to create a transaction on the blockchain (which would need to be run on every single node, and cost gas).
We'll cover setting up web3.js with your own node later. But for now the big takeaway is that you can optimize your DApp's gas usage for your users by using read-only external view functions wherever possible.
Note: If aviewfunction is called internally from another function in the same contract that is not aviewfunction, it will still cost gas. This is because the other function creates a transaction on Ethereum, and will still need to be verified from every node. Soviewfunctions are only free when they're called externally.
One of the more expensive operations in Solidity is using storage — particularly writes.
This is because every time you write or change a piece of data, it’s written permanently to the blockchain. Forever! Thousands of nodes across the world need to store that data on their hard drives, and this amount of data keeps growing over time as the blockchain grows. So there's a cost to doing that.
In order to keep costs down, you want to avoid writing data to storage except when absolutely necessary. Sometimes this involves seemingly inefficient programming logic — like rebuilding an array in memory every time a function is called instead of simply saving that array in a variable for quick lookups.
In most programming languages, looping over large data sets is expensive. But in Solidity, this is way cheaper than using storage if it's in an external view function, since view functions don't cost your users any gas. (And gas costs your users real money!).
We'll go over for loops in the next chapter, but first, let's go over how to declare arrays in memory.
You can use the memory keyword with arrays to create a new array inside a function without needing to write anything to storage. The array will only exist until the end of the function call, and this is a lot cheaper gas-wise than updating an array in storage — free if it's a viewfunction called externally.
Here's how to declare an array in memory:
function getArray() external pure returns(uint[]) {
// Instantiate a new array in memory with a length of 3
uint[] memory values = new uint[](3);
// Add some values to it
values.push(1);
values.push(2);
values.push(3);
// Return the array
return values;
}
This is a trivial example just to show you the syntax, but in the next chapter we'll look at combining this with for loops for real use-cases.
Note: memory arrays must be created with a length argument (in this example,3). They currently cannot be resized like storage arrays can witharray.push(), although this may be changed in a future version of Solidity.
In our getFightersByOwner function, we want to return a uint[] array with all the fighters a particular user owns.
Declare a uint[] memory variable called result
Set it equal to a new uint array. The length of the array should be however many fighters this _owner owns, which we can look up from our mapping with: ownerFighterCount[_owner].
Declare a uint called counter and set it equal to 0. We'll use this variable to keep track of the index in our result array.
Declare a for loop that starts from uint i = 0 and goes up through i < fighters.length. This will iterate over every fighters in our array.
Inside the for loop, make an if statement that checks if fighterToOwner[i] is equal to _owner. This will compare the two addresses to see if we have a match.
Inside theif statement:
Add the fighters's ID to our result array by setting result[counter] equal to i.
Increment counter by 1 (see the for loop example above).
Our function looks like this:
function getFightersByOwner(address _owner) external view returns(uint[]) {
uint[] memory result = new uint[](ownerFighterCount[_owner]);
uint count = 0;
for (uint i = 0 ; i < fighters.length ; i++) {
if (fighterToOwner[i] == _owner) {
result[count] = i;
count++;
}
}
return result;
}
Up until now, we've covered quite a few function modifiers. It can be difficult to try to remember everything, so let's run through a quick review:
We have visibility modifiers that control when and where the function can be called from: private means it's only callable from other functions inside the contract; internal is like private but can also be called by contracts that inherit from this one; external can only be called outside the contract; and finally public can be called anywhere, both internally and externally.
We also have state modifiers, which tell us how the function interacts with the BlockChain: view tells us that by running the function, no data will be saved/changed. pure tells us that not only does the function not save any data to the blockchain, but it also doesn't read any data from the blockchain. Both of these don't cost any gas to call if they're called externally from outside the contract (but they do cost gas if called internally by another function).
Then we have custom modifiers, which we learned about in Lesson 3: onlyOwner and aboveLevel, for example. For these we can define custom logic to determine how they affect a function.
These modifiers can all be stacked together on a function definition as follows:
function test() external view onlyOwner anotherModifier { /* ... */ }
In this chapter, we're going to introduce one more function modifier: payable.
payable Modifierpayable functions are part of what makes Solidity and Ethereum so cool — they are a special type of function that can receive Ether.
Let that sink in for a minute. When you call an API function on a normal web server, you can't send US dollars along with your function call — nor can you send Bitcoin.
But in Ethereum, because both the money (Ether), the data (transaction payload), and the contract code itself all live on Ethereum, it's possible for you to call a function and pay money to the contract at the same time.
This allows for some really interesting logic, like requiring a certain payment to the contract in order to execute a function.
contract OnlineStore {
function buySomething() external payable {
// Check to make sure 0.001 ether was sent to the function call:
require(msg.value == 0.001 ether);
// If so, some logic to transfer the digital item to the caller of the function:
transferThing(msg.sender);
}
}
Here, msg.value is a way to see how much Ether was sent to the contract, and ether is a built-in unit.
What happens here is that someone would call the function from web3.js (from the DApp's JavaScript front-end) as follows:
// Assuming `OnlineStore` points to your contract on Ethereum:
OnlineStore.buySomething({from: web3.eth.defaultAccount, value: web3.utils.toWei(0.001)})
Notice the value field, where the javascript function call specifies how much ether to send (0.001). If you think of the transaction like an envelope, and the parameters you send to the function call are the contents of the letter you put inside, then adding a value is like putting cash inside the envelope — the letter and the money get delivered together to the recipient.
Note: If a function is not markedpayableand you try to send Ether to it as above, the function will reject your transaction.
Let's create a payable function in our fighter game.
Let's say our game has a feature where users can pay ETH to level up their zombies. The ETH will get stored in the contract, which you own — this a simple example of how you could make money on your games!
Define a uint named levelUpFee, and set it equal to 0.001 ether.
Create a function named levelUp. It will take one parameter, _fighterId, a uint. It should be external and payable.
The function should first require that msg.value is equal to levelUpFee.
It should then increment this fighter's level: fighters[_fighterId].level++.
Overall function looks like:
function levelUp(uint _fighterId) public payable {
require(msg.value == levelUpFee);
fighters[_fighterId].level++;
}After you send Ether to a contract, it gets stored in the contract's Ethereum account, and it will be trapped there — unless you add a function to withdraw the Ether from the contract.
You can write a function to withdraw Ether from the contract as follows:
contract GetPaid is Ownable {
function withdraw() external onlyOwner {
owner.transfer(this.balance);
}
}
Note that we're using owner and onlyOwner from the Ownablecontract, assuming that was imported.
You can transfer Ether to an address using the transfer function, and this.balance will return the total balance stored on the contract. So if 100 users had paid 1 Ether to our contract, this.balance would equal 100 Ether.
You can use transfer to send funds to any Ethereum address. For example, you could have a function that transfers Ether back to the msg.sender if they overpaid for an item:
uint itemFee = 0.001 ether; msg.sender.transfer(msg.value - itemFee);
Or in a contract with a buyer and a seller, you could save the seller's address in storage, then when someone purchases his item, transfer him the fee paid by the buyer: seller.transfer(msg.value).
These are some examples of what makes Ethereum programming really cool — you can have decentralized marketplaces like this that aren't controlled by anyone.
Lets work on our code:
Create a withdraw function in our contract, which should be identical to the GetPaid example above.
The price of Ether has gone up over 10x in the past year. So while 0.001 ether is about $1 at the time of this writing, if it goes up 10x again, 0.001 ETH will be $10 and our game will be a lot more expensive.
So it's a good idea to create a function that allows us as the owner of the contract to set the levelUpFee.
a. Create a function called setLevelUpFee that takes one argument, uint _fee, is external, and uses the modifier onlyOwner.
b. The function should set levelUpFee equal to _fee.
our functions looks like:
function withdraw() external onlyOwner {
owner.transfer(this.balance);
}
function changeLevelUpFee(uint _fee) external onlyOwner {
levelUpFee = _fee;
}
/* Overall contract looks like */
pragma solidity ^0.4.19;
import "./FighterWar.sol";
contract FighterHelper is FighterWar {
uint levelUpFee = 0.01 ether;
modifier aboveLevel(uint _level, uint fighterId) {
fighters[fighterId].level > _level;
_;
}
function changeName(uint _fighterId, string _newName) public aboveLevel(_fighterId, 2) {
fighters[_fighterId].name = _newName;
}
function changeSkill(uint _fighterId, uint _newSKill) public aboveLevel(_fighterId, 20) {
fighters[_fighterId].skill = _newSKill;
}
function getFightersByOwner(address _owner) external view returns(uint[]) {
uint[] memory result = new uint[](ownerFighterCount[_owner]);
uint count = 0;
for (uint i = 0 ; i < fighters.length ; i++) {
if (fighterToOwner[i] == _owner) {
result[count] = i;
count++;
}
}
return result;
}
function levelUp(uint _fighterId) public payable {
require(msg.value == levelUpFee);
fighters[_fighterId].level++;
}
function withdraw() external onlyOwner {
owner.transfer(this.balance);
}
function changeLevelUpFee(uint _fee) external onlyOwner {
levelUpFee = _fee;
}
}In this lecture we are going to give attack functionality to our fighter.
Let's review creating a new contract. Repetition leads to mastery!
If you can't remember the syntax for doing these, check FighterHelper.sol for the syntax — but try to do it without peeking first to test your knowledge.
Declare at the top of the file that we're using Solidity version ^0.4.19.
import from FighterHelper.sol.
Declare a new contract called FighterAttack that inherits from FighterHelper.
All good games require some level of randomness. So how do we generate random numbers in Solidity?
The real answer here is, you can't. Well, at least you can't do it safely. Let's look at why
keccak256The best source of randomness we have in Solidity is the keccak256hash function.
We could do something like the following to generate a random number:
// Generate a random number between 1 and 100: uint randNonce = 0; uint random = uint(keccak256(now, msg.sender, randNonce)) % 100; randNonce++; uint random2 = uint(keccak256(now, msg.sender, randNonce)) % 100;
What this would do is take the timestamp of now, the msg.sender, and an incrementing nonce (a number that is only ever used once, so we don't run the same hash function with the same input parameters twice).
It would then use keccak to convert these inputs to a random hash, convert that hash to a uint, and then use % 100 to take only the last 2 digits, giving us a totally random number between 0 and 99.
Let's implement a random number function we can use to determine the outcome of our attack, even if it isn't totally secure from attack.
Give our contract a uint called randNonce, and set it equal to 0.
Create a function called randMod (random-modulus). It will be an internal function that takes a uint named _modulus, and returns a uint.
The function should first increment randNonce (using the syntax randNonce++).
Finally, it should (in one line of code) calculate the uint typecast of the keccak256 hash of now, msg.sender, and randNonce — and return that value % _modulus. (Whew! That was a mouthful. If you didn't follow that, just take a look at the example above where we generated a random number — the logic is very similar).
In this lecture we will implement the attack functionlity
First create an attack function which will inlclude 2 arguments fighterId and targetId
The first thing our function should do is get a storage pointer to both fighters so we can more easily interact with them:
a. Declare a Fighter storage named myFighter, and set it equal to fighters[_fighterId].
b. Declare a Fighter storage named enemyFighter, and set it equal to fighters[_fighterId].
Create an if statement that checks if rand is less than or equal to attackVictoryProbability.
If this condition is true, our fightes wins! So:
a. Increment myFighter's winCount.
b. Increment myFighter's level. (Level up!!!!!!!)
c. Increment enemyFightes's lossCount.
d. Run the fightAndInclude function. Check FighterWar.sol to see the syntax for calling it.
3.Add an else statement. If our fighters loses:
a. Increment myFighter's lossCount.
b. Increment enemyFighter's winCount.
c. Run the _triggerCoolDownFunction function on myFighter. This way the fighter can only attack once per day. (Remember, _triggerCoolDownFunction is already run inside fightandInclude. So the fighter's cooldown will be triggered whether he wins or loses.)
/* Our overall function looks like */
pragma solidity ^0.4.19;
import "./FighterHelper.sol";
contract FighterAttack is FighterHelper {
uint randNonce;
uint attackingProbability = 70;
function randMod(uint _modulus) internal returns(uint) {
randNonce++;
return uint(keccak256(now, msg.sender, randNonce)) % _modulus;
}
function attack(uint _fighterId, uint _targetId) external {
require(msg.sender == fighterToOwner[_fighterId]);
uint rand;
Fighter storage myFighter = fighters[_fighterId];
Fighter storage enemyFighter = fighters[_targetId];
rand = randMod(100);
if (rand < attackingProbability) {
myFighter.winCount ++;
myFighter.level++;
enemyFighter.lossCount++;
fightAndInclude(_fighterId, enemyFighter.skill);
} else {
myFighter.lossCount++;
enemyFighter.winCount++;
}
_triggerCoolDownFunction(myFighter);
}
}
In this lecture We are wrapping up our contract and testing it on Remix.
In this lecture we are going to learn about angular.
Angular is a platform that makes it easy to build applications with the web. Angular combines declarative templates, dependency injection, end to end tooling, and integrated best practices to solve development challenges. Angular empowers developers to build applications that live on the web, mobile, or the desktop.
First of all you need npm to install Angular of ahead and find it on this link: https://nodejs.org/en/download/
after install nodejs and npm. go to Angular home page from the following url: https://angular.io/guide/quickstart
go to the directory where you want to install install the create the project and install angular-cli at the global level by typing the following command:
npm install -g @angular/cli
Inside the directory create a new angular project by typing:
ng new fighter
after a while when the project setup is complete go inside the directory and run : ng serve
this will start the project and serve it on localhost:4200 port.
In this lecture we are going to l create a new module and page for our fighter.
open the project in your visual code studio.
TIP: you can go inside project folder via cli and type code ./ (make sure you have visual code installed on your system).
create a routing file in your root directory by typing the following command
ng generete module app-routing --flat module=app
This will generate the routing routing module file --flate will generate single file and in the root module. import RouterModule and Routes from our @angular/router:
import { Routes, RouterModule } from '@angular/router';
and configure it our app-routing.module.ts should look like:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { Route } from '@angular/router/src/config';
const routes: Routes = [
{ path: 'fighter', loadChildren: './fighter/fighter.module#FighterModule' }
];
@NgModule({
imports: [
RouterModule.forRoot(routes),
],
exports: [
RouterModule
],
declarations: []
})
export class AppRoutingModule { }
Lets import it in our app-module in the import section
@NgModule({
imports: [
CommonModule,
FighterRoutingModule,
FormsModule
],
providers: [],
declarations: []
})
export class FighterModule { }
Now we generate routing for our application lets generate the new module and component for out fighter:
to generate moule with routing use the following command :
ng g m fighter --routing . This command will create a new module for our fighter along with routing.
g stand for generate and m stand for module
Now create the component inside our module In angular we do component based programing to make application module, use following command to create the component:
ng g c fighter/fighter
g stand for generate and c stand for component.
Now configure our fighter-routing module and create a route fro our component by:
const routes: Routes = [
{ path: '', component: FighterComponent }
];
By now our fighter-routing.module.ts should look like.
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { FighterComponent } from './fighter/fighter.component';
const routes: Routes = [
{ path: '', component: FighterComponent }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class FighterRoutingModule { }
Now go to our app-routing module and create a route to load our fighter module on the url '/fighter'
const routes: Routes = [
{ path: 'fighter', loadChildren: './fighter/fighter.module#FighterModule' }
];
final version of our app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { Route } from '@angular/router/src/config';
const routes: Routes = [
{ path: 'fighter', loadChildren: './fighter/fighter.module#FighterModule' }
];
@NgModule({
imports: [
RouterModule.forRoot(routes),
],
exports: [
RouterModule
],
declarations: []
})
export class AppRoutingModule { }
now one last thing is go to our app.component.html and load the router instead of default code
<router-outlet> </router-outlet>
Now go to our application and ng serve It will load our server at localhost: 4200.
In this lecture we are going to learn about web3js.
Web3 js library is a way to talk to Ethereum Blockchain. To communicate to your Blockchain you need to either use a browser is Blockchain enabled one of the browser is mist provided by Ethereum or you just need to use the chrome extension like Metamask.
Here is the official documentation for web3: https://github.com/ethereum/web3.js/.
To install web3 we are going to use 0.20.6 version of web3. So go ahead in your project directory via console and type:
npm install web3@0.20.6
this will install the web3 js lybrary in your node modules.
After our web3 service in installed lets create a service in our application where we will configure our web3. In Angular application we create service to talk to our data related function. To create the service create a folder ptoviders in the main directory and use the following command in the console
ng g s providers/web3
s stands for service it will create a web3 service in tje providers folder.
after creating the service we need to import the service in the fighter.module.ts so we can use that in the application. To import the service you have to import it in the providers array in the module file
import { Web3Service } from '../providers/web3.service';
@NgModule({
imports: [
CommonModule,
FighterRoutingModule,
FormsModule
],
providers: [Web3Service],
declarations: [FighterComponent]
})
After doing that to initialise the web3 we have to import it in our component as well so go to our fighter.component.ts and initialise it
first import it via:
import { Web3Service } from '../../providers/web3.service';
then create its instance in constructer:
constructor(private web3: Web3Service) { }
now go to our web3.service.ts and import the web3 from the node modules
store the instance of the service by requiring it
const Web3 = require('web3');
now in the constructor function check for the Ethereum blockchain provider we do that by using the code:
if (typeof window.web3 !== 'undefined') {
// Web3 = new Web3(Web3.currentProvider);
this._web3 = new Web3(window.web3.currentProvider);
console.log('metamask is found');
} else {
// set the provider you want from Web3.providers
// web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}
As in our case we are using metamask lets it will get the provider as metamask.
/* Our web3.service.ts should look like */
import { Injectable } from '@angular/core';
const Web3 = require('web3');
declare let window: any;
declare let require: any;
@Injectable()
export class Web3Service {
public _web3: any;
constructor() {
this.initializeWeb3();
}
initializeWeb3() {
if (typeof window.web3 !== 'undefined') {
// Web3 = new Web3(Web3.currentProvider);
this._web3 = new Web3(window.web3.currentProvider);
console.log('metamask is found');
} else {
// set the provider you want from Web3.providers
}
}
now run our application by doing ng serve and check console on localhost:4200
In this tutorial we are going to talk to talk to our blockchain via Angular application.
Go to your remix make sure you have selected Ropsten network on your Metamask and FighterAttack contract to deploy as we have to maintain the hierarchy. When you click on your create button metamask will open to confirm the transaction confirm the transaction.
create a new file inside your providers folder contract.abi.json. Now in your web3.service.ts create 2 variables which will hel[ us to connect to blockchain via metamask.
const contractAbi = require('./contract.abi.json');
const contractAddress = '0xae39355a8c3ab79ecbf378575461ca0f3b7540e1';inside your contractAddress you have to keep your address. which you will get from your remix IDE after deploying your contract.
again from the compile section of your remix IDE you will get the abi definition of your contract copy it and past in your contract.abi.json file.
Create a contract instance like this:
this._contract = this._web3.eth.contract(contractAbi).at(contractAddress);
communication with Blockchain is like communicating with remote response call they are asynchronous i.e we will not immediately get the response get the response we have to wait for a while , So go ahead and create a function getAccounts which will return Promise of accounts and read the comments on the function to understand it better.
// this function returns a promise
public async getAccount(): Promise {
// we will check if we already have the _account.
if (this._account == null) {
// then we will create a new Promise
this._account = await new Promise((resolve, reject) => {
// with the help od web3 we will talk to metamask and get the account.
// this._web3.eth.getAccounts will return the accounts from metamask
// We have to pass a callback function to get the response from the metamask
this._web3.eth.getAccounts((err, accs) => {
if (err == null) {
// console.log('There was an error fetching your accounts.');
return;
}
if (accs.length === 0) {
alert(
'Couldn\'t get any accounts! Make sure your Ethereum client or Metamask is configured correctly.'
);
return;
}
return resolve(accs[0]);
});
}) as string;
this._web3.eth.defaultAccount = this._account;
}
// console.log(Promise.resolve(this._account));
return Promise.resolve(this._account);
}Similary we will create another function to get the owner of the contract
// function will return promise
public async owner(): Promise {
const account = await this.getAccount();
return new Promise((resolve, reject) => {
// we have to pass the callback function to get the response from the blockchain
this._contract.owner.call((err, result) => {
if (err != null) {
return reject(err);
}
return resolve(result);
});
}) as Promise;
}Now go to our component and create a function getAccunt which will call the owner function from the service read the comments for understanding the function and code better.
getAccount() {
// since it is a promise we have to use then()
return this.web3.owner().then(result => {
// use an arrow function to get the result
console.log(result);
this.getFighterById(result[0]);
// and use catch to catch any error
}).catch(err => {
console.log(err);
});
}and finally call this function in the ngOnInit
ngOnInit() {
this.getAccount();
}Check the console after running the application by ng serve at localhost:4200 we should be getting out owner of the contract.
In this lecture we are going to build web interface for our bootstrap application.
Lets include bootstrap 4 in the index file by adding
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet">
and we are going the take the sample theme from our bootstrap example go to following link and copy the album theme:
http://getbootstrap.com/docs/4.0/examples/
go to the album theme and we are going to copy the source code from the theme and past it in our fighter.component.html here is the source code for the theme.
/* html code for fighter.comopnent,html*/
<main role="main">
<section class="jumbotron text-center">
<div class="container">
<h1 class="jumbotron-heading">Album example</h1>
<p class="lead text-muted">Something short and leading about the collection below—its contents, the creator, etc. Make it short and sweet, but not too short so folks don't simply skip over it entirely.</p>
<p>
<a href="#" class="btn btn-primary my-2">Main call to action</a>
<a href="#" class="btn btn-secondary my-2">Secondary action</a>
</p>
</div>
</section>
<div class="album py-5 bg-light">
<div class="container">
<div class="row">
<div class="col-md-4">
<div class="card mb-4 box-shadow">
<img class="card-img-top" data-src="holder.js/100px225?theme=thumb&bg=55595c&fg=eceeef&text=Thumbnail" alt="Thumbnail [100%x225]" style="height: 225px; width: 100%; display: block;" src="data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22348%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20348%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_162de86d323%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A17pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_162de86d323%22%3E%3Crect%20width%3D%22348%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22116.234375%22%20y%3D%22120.3%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E" data-holder-rendered="true">
<div class="card-body">
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-secondary">View</button>
<button type="button" class="btn btn-sm btn-outline-secondary">Edit</button>
</div>
<small class="text-muted">9 mins</small>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card mb-4 box-shadow">
<img class="card-img-top" data-src="holder.js/100px225?theme=thumb&bg=55595c&fg=eceeef&text=Thumbnail" alt="Thumbnail [100%x225]" src="data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22348%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20348%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_162de86d325%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A17pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_162de86d325%22%3E%3Crect%20width%3D%22348%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22116.234375%22%20y%3D%22120.3%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E" data-holder-rendered="true" style="height: 225px; width: 100%; display: block;">
<div class="card-body">
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-secondary">View</button>
<button type="button" class="btn btn-sm btn-outline-secondary">Edit</button>
</div>
<small class="text-muted">9 mins</small>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card mb-4 box-shadow">
<img class="card-img-top" data-src="holder.js/100px225?theme=thumb&bg=55595c&fg=eceeef&text=Thumbnail" alt="Thumbnail [100%x225]" src="data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22348%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20348%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_162de86d327%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A17pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_162de86d327%22%3E%3Crect%20width%3D%22348%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22116.234375%22%20y%3D%22120.3%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E" data-holder-rendered="true" style="height: 225px; width: 100%; display: block;">
<div class="card-body">
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-secondary">View</button>
<button type="button" class="btn btn-sm btn-outline-secondary">Edit</button>
</div>
<small class="text-muted">9 mins</small>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card mb-4 box-shadow">
<img class="card-img-top" data-src="holder.js/100px225?theme=thumb&bg=55595c&fg=eceeef&text=Thumbnail" alt="Thumbnail [100%x225]" src="data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22348%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20348%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_162de86d329%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A17pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_162de86d329%22%3E%3Crect%20width%3D%22348%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22116.234375%22%20y%3D%22120.3%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E" data-holder-rendered="true" style="height: 225px; width: 100%; display: block;">
<div class="card-body">
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-secondary">View</button>
<button type="button" class="btn btn-sm btn-outline-secondary">Edit</button>
</div>
<small class="text-muted">9 mins</small>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card mb-4 box-shadow">
<img class="card-img-top" data-src="holder.js/100px225?theme=thumb&bg=55595c&fg=eceeef&text=Thumbnail" alt="Thumbnail [100%x225]" src="data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22348%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20348%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_162de86d32a%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A17pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_162de86d32a%22%3E%3Crect%20width%3D%22348%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22116.234375%22%20y%3D%22120.3%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E" data-holder-rendered="true" style="height: 225px; width: 100%; display: block;">
<div class="card-body">
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-secondary">View</button>
<button type="button" class="btn btn-sm btn-outline-secondary">Edit</button>
</div>
<small class="text-muted">9 mins</small>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card mb-4 box-shadow">
<img class="card-img-top" data-src="holder.js/100px225?theme=thumb&bg=55595c&fg=eceeef&text=Thumbnail" alt="Thumbnail [100%x225]" src="data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22348%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20348%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_162de86d32c%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A17pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_162de86d32c%22%3E%3Crect%20width%3D%22348%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22116.234375%22%20y%3D%22120.3%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E" data-holder-rendered="true" style="height: 225px; width: 100%; display: block;">
<div class="card-body">
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-secondary">View</button>
<button type="button" class="btn btn-sm btn-outline-secondary">Edit</button>
</div>
<small class="text-muted">9 mins</small>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card mb-4 box-shadow">
<img class="card-img-top" data-src="holder.js/100px225?theme=thumb&bg=55595c&fg=eceeef&text=Thumbnail" alt="Thumbnail [100%x225]" src="data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22348%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20348%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_162de86d32d%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A17pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_162de86d32d%22%3E%3Crect%20width%3D%22348%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22116.234375%22%20y%3D%22120.3%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E" data-holder-rendered="true" style="height: 225px; width: 100%; display: block;">
<div class="card-body">
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-secondary">View</button>
<button type="button" class="btn btn-sm btn-outline-secondary">Edit</button>
</div>
<small class="text-muted">9 mins</small>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card mb-4 box-shadow">
<img class="card-img-top" data-src="holder.js/100px225?theme=thumb&bg=55595c&fg=eceeef&text=Thumbnail" alt="Thumbnail [100%x225]" src="data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22348%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20348%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_162de86d32f%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A17pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_162de86d32f%22%3E%3Crect%20width%3D%22348%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22116.234375%22%20y%3D%22120.3%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E" data-holder-rendered="true" style="height: 225px; width: 100%; display: block;">
<div class="card-body">
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-secondary">View</button>
<button type="button" class="btn btn-sm btn-outline-secondary">Edit</button>
</div>
<small class="text-muted">9 mins</small>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card mb-4 box-shadow">
<img class="card-img-top" data-src="holder.js/100px225?theme=thumb&bg=55595c&fg=eceeef&text=Thumbnail" alt="Thumbnail [100%x225]" src="data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22348%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20348%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_162de86d331%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A17pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_162de86d331%22%3E%3Crect%20width%3D%22348%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22116.234375%22%20y%3D%22120.3%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E" data-holder-rendered="true" style="height: 225px; width: 100%; display: block;">
<div class="card-body">
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-secondary">View</button>
<button type="button" class="btn btn-sm btn-outline-secondary">Edit</button>
</div>
<small class="text-muted">9 mins</small>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
In this lecture we are going to fetch our fighter from the smart contract go ahead and deploy our contract and copy the address and abi definition of the contract and past it in our web3.service.ts.
create a new fighter in the contract from remix IDE.
Now go to our web3.service.ts and create a function called getFighers(). This function will call the getFightersBhyOwner() and get the fighter belong to the owner.
here is the function looks like go through the comment in the code in order to get the better understanding of the code.
// create a function called getFighters it will return a promise
public async getFighters(): Promise {
const account = await this.owner();
//It will return a new Promise
return new Promise((resolve, reject) => {
// from our contract instance call getFighterByOwner from the contract
// pass a callback in the function the contract function
this._contract.getFightersByOwner.call(account, (err, result) => {
if (err != null) {
return reject(err);
}
// we will get the response in BigNumber javascript cant deal with big numbers so lets return a comfortable digit
return resolve(result[0].c);
});
}) as any;
}now in our contract function create a new function called getFighter(), This function this function will call the getFighter() from the web3.service.ts and result the fighters of the owner. our function will look like:
public getFighters() {
// we have to use then since it is a promise
this.web3.getfighters().then(result => {
console.log(result);
}).catch(err => {
console.log(err);
});
}
Now create another function called getFighterById() in our web3.service.ts This function will again return a promise and the function will look like this (go through comment to understand the code betrter).
// this function will return a promise
public async getFighterById(id: number): Promise {
return new Promise((resolve, reject) => {
// call fighters function from the contract and to get the response pass a callback function to it.
this._contract.fighters.call(id, (err, result) => {
if (err != null) {
return reject(err);
}
return resolve(result);
});
}) as any;
}
And in the fighter.component.ts create a function getFighterById and call the function getFighterById() from the web3.service.ts, For better understanding of the code read through the comment section of the code.
// these variables will store the fighter
public name: string;
public skill: string;
public level: string;
public readyTime: string;
public winCount: string;
public lossCount: string;
public getFighterById(id: number) {
// use then as it is promise
this.web3.getFighterById(id).then(result => {
// store the fighter in the respectove variables to show in the ui.
console.log(result);
this.name = result[0].toString();
this.skill = result[1].toString();
this.level = result[2].toString();
this.readyTime = result[3].toString();
this.winCount = result[4].toString();
this.lossCount = result[5].toString();
}).catch(err => {
console.log(err);
});
}and integrate these variable in the html :
<p class="lead text-muted">{{name}}, skill: {{skill}}, level: {{level}}, readyTimer: {{readyTimer}} </p>
<p class="lead text-muted">winCount : {{winCount}}, lossCount: {{lossCount}}</p>
<p>
<a href="#" class="btn btn-primary my-2">Main call to action</a>
<a href="#" class="btn btn-secondary my-2">Secondary action</a>
</p>
now go to our application and run the application by doing ng serve and cjheck the result at localhost:4200
In this lecture we are going to fetch all the fighters.
First go to our remix IDS and in the FighterAvatar,sol create a function in the end, This function will give us the total number of fighters in the contract.
function totalFighters() public view returns(uint) {
return fighters.length;
}
Now as you have changed the contract you have to deploy the contract again go to remix deploy the contract on the ropsten network make sure you have FighterAttack.sol selected to maintain the hierarchy of the smart contracts. Get the deployed contract address and the abi definition of the contract. and configute in the web3.service.ts as we have done previously.
Create a new fighter in the new contract.
Now change the account in you metamask and create another fighter from another account.
Now create a new function in the contract totalFighters() it should look like this (read the code comment to understand the code)
// function will return a promise
public async totalFighters(): Promise {
// pass a callback function
return new Promise((resolve, reject) => {
// from the contract instance call totalFighters function
this._contract.totalFighters.call((err, result) => {
if (err != null) {
return reject(err);
}
return resolve(result.toString());
});
}) as any;
}As we have created totalFIghter in the fighter.component.ts create a function getTotalFigher() which looks like:
// function to get total fighter on the blockchain
public getTotalFighter() {
// use then as it is a promise
this.web3.totalFighters().then(result => {
console.log(result);
}).catch(err => {
console.log(err);
});
}
and finally to check call this function inside our ngOnInt()
ngOnInit() {
this.getAccount();
this.getFighters();
this.getTotalFighter();
}
Create another function in the service with the name getFighterById() this function will give the fighter detail based on specific if
public async getFighterById(id: number): Promise {
return new Promise((resolve, reject) => {
this._contract.fighters(id, (err, result) => {
if (err != null) {
return reject(err);
}
return resolve(result);
});
}) as any;
}
Now to store all the fighters create an array on our components like
public uiFighterArray: any[] = [];
create a function in the component called getAllFighters() this function will loop on the getTotalFighters() to get the list of the fighters and the function looks like.
public getAllFighters(id) {
this.web3.getFighterById(id).then(result => {
this.allFighters.push(result);
const fighter: { name: string, id: string } = { name: '', id: '' };
fighter.name = result[0];
fighter.id = id;
this.uiFighterArray.push(fighter);
}).catch(err => {
console.log(err);
});
}
Lets call this function in our getTotalFIghter() the code looks looks like:
public getTotalFighter() {
this.web3.totalFighter().then(result => {
// console.log(result);
for (var i = 0; i < parseInt(result, 10); i++) {
this.getAllFighters(i);
}
console.log(this.uiFighterArray);
}).catch(err => {
console.log(err);
});
}
Now run our application by ng serve and check the response in the console.
In this lecture we are going to integrate our fighter which we fetched from the Blockchain.
Lets's create a new array where we will stored our new modified fighters in the componenet which we are going to show.
public uiFighterArray: any[] = [];
lets fill this array with the modified fighters which we want to display, Inside our get allFIghter() defined the basic structure of the fighter which we want to loop and display.
// to loop we have to define a basic structure for the uiFighterArray
const fighter: { name: string, id: string } = { name: '', id: '' };
// fighters array 0th element is name from the blockchain
fighter.name = result[0];
// we are keeping the id same as the id in the function
fighter.id = id;
// And push every new edited fighter in the uiFighterArray
this.uiFighterArray.push(fighter);
our getAllFIghters(id) function should look like:
public getAllFighters(id) {
this.web3.getFighterById(id).then(result => {
this.allFighters.push(result);
const fighter: { name: string, id: string } = { name: '', id: '' };
fighter.name = result[0];
fighter.id = id;
this.uiFighterArray.push(fighter);
}).catch(err => {
console.log(err);
});
}
Now since we have all our fighters to loop we are going to integrate all of them in the html. To loop all the fighters in the html we have to use *ngFor property from angular which will repeat the array our html will look like:
<div class="col-md-4" *ngFor="let fighter of uiFighterArray">
</div>
Inside this div we can access to all our fighters and access them via {{fighter.name}} and fighter.id.
In this lecture we are going to create attack functionality to our fighter When it attacks the fighter it will start the battle by calling the attack function from the smart contract.
let's create a function in our component called attack() which is and add it in our html button as well
<button type="button" class="btn btn-sm btn-outline-primary" (click)="attack(fighter.id)">Attack</button>
public attack(enemyId) {
}
Now create a function in our web3.service.ts where we are going to call our attack() function from our smart contract.
This will look like:
(go through the comments in the code)
// This is an async function so we have to create the Promise for it
public async attack(myId, enemyId): Promise {
const account = await this.getAccount();
return new Promise((resolve, reject) => {
// This function is a transactional function we need an account from which it is been carrying out
this._contract.attack(myId, enemyId, { from: account[0] },
// pass a callback function to get the value from the blockchain
(err, result) => {
if (err != null) {
return reject(err);
}
// return the promise
return resolve(result);
}
);
}) as any;
}
This is an transactionnal function we are not just reading the data from the blockchain we are writing the data into it.
now come back to our component and code the attack() function.
public attack(enemyId) {
const myId = 0;
console.log(this.myFighterId);
// since we need 2 ids in our attack function, 1st who is attacking and 2nd who getting attacked
// so 1st is of our owner
this.web3.attack(0, enemyId).then(result => {
console.log(result);
}).catch(err => {
console.log(err);
});
}
Go to our application by running ng serve and test out the functionality and If any confusion go to our github page and check out the code.
In this lecture we are going to make the fighter id's dynamic.
As you saw in our last tutorial we are taking the hardcoded id for our fighter lets make it dynamic.
lets define a variable:
public myFighterId: number;
and lets get this id from our getFighters() function:
public getFighters() {
this.web3.getFighters().then(result => {
// console.log(result[0]['c'][0]);
// here we are getting the id of my Fighter
this.myFighterId = result[0]['c'][0];
this.getFighterById(result[0]['c']);
}).catch(err => {
});
}And we have just implemented the dynamic id for our fighter.
In this lecture we are going to learn about Payable function implementation.
As in our Smart contract we have a levelUp function were we can just pay our ether to increase the level of our fighter., Let's configure that function.
We will start by configuring the html , hook it up with a levelU() function.
<button (click)="levelUp()" class="btn btn-primary mb-2">Level UP</button>
Let's create a levelUp function in our web3.service.ts.
This will be a transactional function as it is going to be change the variables on blockchain also we have to send the ether with this function
we can send ether's with a function call in the msg object
{ from: account[0], value: this._web3.toWei(0.01, 'ether') },
value: this._web3.toWei(0.01, 'ether' ) will make sure that the ether we are sending is going that too in wei (smallest denomination of ether). So our function will look like
// async function so we have to use the promise
public async levelUp(id: number): Promise {
const account = await this.getAccount();
return new Promise((resolve, reject) => {
// transactional function so we have to send account information
// also its a payable function so send the ether value in value key
this._contract.levelUp(id, { from: account[0], value: this._web3.toWei(0.01, 'ether') },
((err, result) => {
if (err != null) {
return reject(err);
}
return resolve(result);
}),
);
}) as any;
}
Now as we have integrated the function in our web3.service.ts let create and call this function in our component.
public levelUp() {
// send the id of our main fighter and use then as it is promise
this.web3.levelUp(this.myFighterId).then(result => {
console.log(result);
}).catch(err => {
console.log(err);
});
}
now let's go to our application using ng serve and check the levelUP functionality in the via Level Up button.
In this lecture we are going to integrate changeName and changeSkilll functionality to our fighter based on its level.
For changing the name we need inputs we are going to use horizontal forms for that purposes. So go to our fighter.component.html
and implement the horizontal form it should look like this:
<div>
<form class="form-inline form-style">
<div class="form-group mx-sm-3 mb-2">
<label for="changeName" class="sr-only">change Name</label>
<input type="text" class="form-control" [(ngModel)]="changedName" name="changedName" id="changeName" placeholder="Change Name">
</div>
<button class="btn btn-success mb-2" (click)="changeName(changedName)">Confirm</button>
<div class="form-group mx-sm-3 mb-2">
<label for="changeSkill" class="sr-only">change Name</label>
<input type="text" class="form-control" id="changeSkill" placeholder="Change Skill">
</div>
<button class="btn btn-success mb-2">Confirm</button>
<div class="form-group mx-sm-3 mb-2">
<label for="changeSkill" class="sr-only">change Name</label>
</div>
<button (click)="levelUp()" class="btn btn-primary mb-2">Level UP</button>
</form>
</div>
We have created the form with the input added the [(ngModel)] with it to access that property in the component. again to added the functions changeName() and changeSkill().
Lets give the form styling as well in our fighter.component.css
.form-style{
width: 90%;
margin-left: 10%;
} In the next lecture lets create this functions in our fighter.component.ts and integrate them.
In this lecture we are going to integrate our changename() function in our fighter.component.ts file.
start by creating two functions on our web3.service.ts changeName() and changeSkill().
the changeName() function in our service looks like this:
public async changeName(id: number, newName: string): Promise {
const account = await this.getAccount();
return new Promise((resolve, reject) => {
// since this is a transactional function we have to tell blockchain where it is coming from
// account is the first account from the account list that's why account[0]
// calling the changeName function on the contract
this._contract.changeName(id, newName, { from: account[0] },
(err, result) => {
if (err != null) {
return reject(err);
}
return resolve(result);
},
);
}) as any;
}Since we are going to change the data on our Blockchain this function is going to be a transactional function.
Now lets create the changeName() function in our component which will be:
public changeName(newName: string) {
// this is a promise so we have to use then()
this.web3.changeName(this.myFighterId, newName).then(result => {
console.log(result);
}).catch(err => {
console.log(err);
});
}
and dont forget to import the FormsModule in our module for angular to make the form work so in our module :
import { FormsModule } from '@angular/forms';
@NgModule({
imports: [
CommonModule,
FighterRoutingModule,
FormsModule
],
providers: [Web3Service],
declarations: [FighterComponent]
})
export class FighterModule { }
Now lets run our application by ng serve and change the name and test it out.
In this lecture we are going to integrate some images to our application.
to add images go back to src folder and create a images folder inside the asserts and download some gif, or you can look then up in our github directory also.
In this case we are adding the hardcoded id so in our html for our main fighter add it like
<img class="card-img-top" data-src="/assets/images/{{myFighterId}}.gif" alt="Thumbnail [100%x225]" style="height: 225px; width: 30%; display: block; margin-left:35%"
src="/assets/images/{{myFighterId}}.gif" data-holder-rendered="true">
and for all the list of the fighter use the ngFor loo[p variable:
<img class="card-img-top" data-src="holder.js/100px225?theme=thumb&bg=55595c&fg=eceeef&text=Thumbnail" alt="Thumbnail [100%x225]" style="height: 225px; width: 100%; display: block;" src="/assets/images/{{fighter.id}}.gif" data-holder-rendered="true">
In this lecture we are going to integrate the create fighter functionality:
First we will add a functionality if our account font have any fighter it will show a input where we will keep our create fighter functionality.
So go to our fighter.component.html and our html will look like:
<div *ngIf="myFighterId == undefined">
<form class="form-inline form-style">
<div class="form-group mx-sm-3 mb-2">
<label for="changeName" class="sr-only">change Name</label>
<input type="text" class="form-control" [(ngModel)]="createFighterName" name="createFighterName" id="changeName" placeholder="Change Name">
</div>
<button class="btn btn-success mb-2" (click)="createFighter(createFighterName)">Confirm</button>
</form>
</div>
*ngIf="myFighterId == undefined" will make sure that it will only visible when our account or user dont have any fighter.
Like we have done previously lets create a function in our web3.service.ts createFighter() which will look like (go through comment !!):
// async function will return a promise
public async createFighter(name: string): Promise {
// transactional function so it will need an address in the msg object
const account = await this.getAccount();
return new Promise((resolve, reject) => {
// calling generateRandomFighter from smart contract from the blockchain
this._contract.generateRandomFighter(name, { from: account[0] },
// pass a call back to get the transactionId from the blockchain
(err, result) => {
if (err != null) {
return reject(err);
}
return resolve(result);
}
);
}) as any;
}
Now create another createFunction in our fighter.component.ts to call function from service.
public createFighter(name: string) {
console.log(name);
// as it is promise we have to use then()
this.web3.createFighter(name).then(result => {
console.log(result);
}).catch(err => {
console.log(err);
});
}
Now lets check this functionality by doing ng serve and testing the createFighter() function.
You can look at the complete source code from our github repository.
Welcome to the Blockchain Ninja complete Blockchain development course. I'm going to teach you, step-by-step, how to build a Blockchain application from scratch. You don't need any prior knowledge of Blockchain.
My name is Dheeraj Pal and Blockchain developer and founder at an IoT and Blockchain company. We have developed some of the very complex dual Blockchain integrated applications (dapps). I have extensive knowledge of the complete Blockchain Dapps. And that's what I'm going to teach you !
At the end of this course you will be able to create your own Blockchain applications (dapps) and add a massive value of Blockchain skill on your resume.. It's entirely up to you, your commitment, your determination.
The course is designed with the total beginner in mind, but if you're struggling I will help you along the way. I'll be taking you step-by-step, showing you exactly what I have done to create some of the great dapp applications.
.
This course will bring you an interactive tutorial where we are going to code an end to end Ethereum game with solidity, Angular 5 and Web3.
After taking the tutorial you will learn :
1) How to code Blockchain Dapps.
2) How to build real time Blockchain applications.
3) How to apply decentralise Blockchain concepts in real time applications.
4) How to Deploy and interact with Blockchain applications.