Categories
Crypto

TRON: ZombieSurvival

Now that we have successfully developed the popular Rock-Paper-Scissors game for the TRON blockchain, let’s take on a new challenge and create an action-packed game – Zombie Survival.

ZombieSurvival is a smart contract game built on the TRON blockchain using the Solidity programming language. The game simulates a zombie apocalypse where each player is a survivor, and they can join the game, shoot zombies, acquire score, and acquire items and weapons to improve their chances of survival. Each player has an owner, health, ammo, and score. The contract has several functions that allow players to join the game, shoot zombies, acquire items, upgrade weapons, complete quests and more.

When a player joins the game, they are added to the mapping “players” as a struct Player with a default health, ammo and score. The player can then use the “shoot” function to try and hit the zombies, this function will check if the player’s health and ammo are greater than 0 and it will use the function “checkHit” to determine if the player hit the zombies or not. If the player hit the zombies, the player’s health will decrease by 10, if the player’s health reaches 0, the player’s stats will be reset.

The contract also has functions to acquire items, upgrade weapons, complete quests and trade with other players. The player can use the “acquireItem” function to purchase items using their score. The player can use the “upgradeWeapon” function to upgrade their weapon using their score. The player can use the “completeQuest” function to complete quests and earn rewards. The player can use the “trade” function to trade with other players using items.

The contract also emits events when a player shoots, when a player’s health changes and when a player’s score changes.

pragma solidity ^0.4.24;

contract ZombieSurvival {
    //Struct to hold the information of a player
    struct Player {
        address owner;
        uint health;
        uint ammo;
        uint score;
        mapping(string => uint) inventory;
        mapping(string => uint) upgrades;
    }

    //Mapping to hold all the players
    mapping(address => Player) public players;

    //Event to be emitted when player shoots a zombies
    event Shoot(address shooter);
    //Event to be emitted when player's health changes
    event HealthChanged(address player, uint newHealth);
    //Event to be emitted when player's score changes
    event ScoreChanged(address player, uint newScore);

    //Function to join the game
    function joinGame() public {
        require(players[msg.sender].owner == address(0));
        players[msg.sender].owner = msg.sender;
        players[msg.sender].health = 100;
        players[msg.sender].ammo = 20;
        players[msg.sender].score = 0;
    }

    //Function to shoot a zombies
    function shoot() public {
        require(players[msg.sender].health > 0);
        require(players[msg.sender].ammo > 0);
        //decrement ammo
        players[msg.sender].ammo--;
        //increment score
        players[msg.sender].score++;
        //check if the player hit a zombies
        if (checkHit()) {
            //decrement health
            players[msg.sender].health -= 10;
            emit HealthChanged(msg.sender, players[msg.sender].health);
            checkDeath();
        }
        emit ScoreChanged(msg.sender, players[msg.sender].score);
        emit Shoot(msg.sender);
    }

    //Function to acquire an item
    function acquireItem(string _itemName) public {
        require(players[msg.sender].score >= getItemCost(_itemName));
        require(players[msg.sender].inventory[_itemName] == 0);
        players[msg.sender].score -= getItemCost(_itemName);
        players[msg.sender].inventory[_itemName]++;
    }
    //Function to upgrade a weapon
    function upgradeWeapon(string _weaponName) public {
        require(players[msg.sender].score >= getWeaponUpgradeCost(_weaponName));
        uint currentLevel = getWeaponLevel(msg.sender, _weaponName);
        require(currentLevel != 0);
        require(currentLevel < maxWeaponLevel);
        players[msg.sender].score -= getWeaponUpgradeCost(_weaponName);
        players[msg.sender].upgrades[_weaponName]++;
    }
    //Function to complete a quest
    function completeQuest(string _questName) public {
        require(players[msg.sender].health > 0);
        require(players[msg.sender].ammo > 0);
        //check if the player completed the quest
        if (checkQuestCompletion(_questName)) {
            //increment score
            players[msg.sender].score += getQuestReward(_questName);
            //increment health
            players[msg.sender].health += getQuestHealthReward(_questName);
            //increment ammo
            players[msg.sender].ammo += getQuestAmmoReward(_questName);
        }
    }
    //Function to buy TRX from the shop
    function buyTRX(uint _amount) public {
        require(msg.sender.transfer(_amount));
    }
    //Function to sell TRX to the shop
    function sellTRX(uint _amount) public payable {
        require(msg.value == _amount);
    }
    //Function to check if the player completed a quest
    function checkQuestCompletion(string _questName) private view returns (bool) {
        uint random = uint(keccak256(abi.encodePacked(block.timestamp, block.difficulty))) % 2;
        if (random == 1) return true;
        else return false;
    }
    //Function to get the cost of an item
    function getItemCost(string _itemName) public view returns (uint) {
        if (_itemName == "medkit") return 1000;
        if (_itemName == "ammo_box") return 5000;
        if (_itemName == "first_aid_kit") return 10000;
    }
    //Function to get the cost of upgrading a weapon
    function getWeaponUpgradeCost(string _weaponName) public view returns (uint) {
        if (_weaponName == "pistol") return 500;
        if (_weaponName == "shotgun") return 2500;
        if (_weaponName == "rifle") return 5000;
    }
    //Function to get the reward of a quest
    function getQuestReward(string _questName) public view returns (uint) {
        if (_questName == "survival") return 100;
        if (_questName == "zombie_hunt") return 250;
        if (_questName == "rescue_mission") return 500;
    }
    //Function to get the health reward of a quest
    function getQuestHealthReward(string _questName) public view returns (uint) {
        if (_questName == "survival") return 10;
        if (_questName == "zombie_hunt") return 25;
        if (_questName == "rescue_mission") return 50;
    }
    //Function to get the ammo reward of a quest
    function getQuestAmmoReward(string _questName) public view returns (uint) {
        if (_questName == "survival") return 5;
        if (_questName == "zombie_hunt") return 10;
        if (_questName == "rescue_mission") return 20;
    }
    //Function to get the current level of a weapon
    function getWeaponLevel(address _owner, string _weaponName) public view returns (uint) {
        return players[_owner].upgrades[_weaponName];
    }
    //Function to login and get a daily reward
    function login() public {
        require(players[msg.sender].health > 0);
        require(players[msg.sender].ammo > 0);
        //increment score
        players[msg.sender].score += 10;
        //increment health
        players[msg.sender].health += 5;
        //increment ammo
        players[msg.sender].ammo += 2;
    }
    //Function to save the game progress
    function saveProgress() public {
        //Save the player's data to storage or IPFS
    }
    //Function to load the game progress
    function loadProgress() public view returns (bool) {
        //Load the player's data from storage or IPFS
        //if the data is successfully loaded return true, else return false
    }
    //Function to trade with another player
    function trade(address _otherPlayer, string _itemName, uint _amount) public {
        require(players[msg.sender].inventory[_itemName] >= _amount);
        require(players[_otherPlayer].inventory[_itemName] >= _amount);
        players[msg.sender].inventory[_itemName] -= _amount;
        players[_otherPlayer].inventory[_itemName] += _amount;
    }
}

Full source code is here.

Categories
Crypto

TRON: Kingdoms

Let’s build a game using TRON, and what better game to create than rock-paper-scissors? We’ve already covered TRON, so now it’s time to put that knowledge into practice.

pragma solidity ^0.4.24;

contract Kingdoms {
    // Struct to hold the information of a Kingdom
    struct Kingdom {
        address owner;
        uint army;
        uint gold;
        uint resources;
        mapping(string => uint) structures;
    }

    // Mapping to hold all the Kingdoms
    mapping(address => Kingdom) public kingdoms;

    // Event to be emitted on attack
    event Attack(address attacker, address defender, bool success);
    //Event to be emitted on structure building
    event Build(address owner, string structureName);

    // Function to create a new Kingdom
    function createKingdom(uint initialArmy, uint initialGold, uint initialResources) public {
        require(kingdoms[msg.sender].owner == address(0));
        kingdoms[msg.sender].owner = msg.sender;
        kingdoms[msg.sender].army = initialArmy;
        kingdoms[msg.sender].gold = initialGold;
        kingdoms[msg.sender].resources = initialResources;
    }

    // Function to attack another Kingdom
    function attack(address _defender) public {
        require(kingdoms[msg.sender].army > 0);
        require(kingdoms[_defender].owner != address(0));
        //calculate the success of the attack
        bool success = calculateAttack(kingdoms[msg.sender].army, kingdoms[_defender].army);
        if (success) {
            kingdoms[_defender].army = kingdoms[_defender].army / 2;
            kingdoms[msg.sender].gold += kingdoms[_defender].gold;
            kingdoms[msg.sender].resources += kingdoms[_defender].resources;
        } else {
            kingdoms[msg.sender].army = kingdoms[msg.sender].army / 2;
        }
        emit Attack(msg.sender, _defender, success);
    }
    // Function to build structures in a Kingdom
    function buildStructure(string _structureName) public {
        require(kingdoms[msg.sender].gold >= getStructureCost(_structureName));
        require(getStructureLevel(msg.sender, _structureName) == 0);
        kingdoms[msg.sender].gold -= getStructureCost(_structureName);
        kingdoms[msg.sender].structures[_structureName] = 1;
        emit Build(msg.sender, _structureName);
    }

    // Function to upgrade a structure in a Kingdom
    function upgradeStructure(string _structureName) public {
        require(kingdoms[msg.sender].gold >= getStructureUpgradeCost(_structureName));
        uint currentLevel = getStructureLevel(msg.sender, _structureName);
        require(currentLevel != 0);
        require(currentLevel < maxStructureLevel);
        kingdoms[msg.sender].gold -= getStructureUpgradeCost(_structureName);
        kingdoms[msg.sender].structures[_structureName]++;
    }

    // Function to get the current level of a structure in a Kingdom
    function getStructureLevel(address _owner, string _structureName) public view returns (uint) {
        return kingdoms[_owner].structures[_structureName];
    }

    // Function to get the cost of building a structure
    function getStructureCost(string _structureName) public view returns (uint) {
        if (_structureName == "wall") return 1000;
        if (_structureName == "tower") return 5000;
        if (_structureName == "barracks") return 10000;
    }

    // Function to get the cost of upgrading a structure
    function getStructureUpgradeCost(string _structureName) public view returns (uint) {
        if (_structureName == "wall") return 500;
        if (_structureName == "tower") return 2500;
        if (_structureName == "barracks") return 5000;
    }

    // Function to calculate the success of the attack
    function calculateAttack(uint attackerArmy, uint defenderArmy) private view returns (bool) {
        uint random = uint(keccak256(abi.encodePacked(block.timestamp, block.difficulty))) % 2;
        if (random == 1 && attackerArmy > defenderArmy) return true;
        else return false;
    }
}

Full source code can be found here.

The smart contract allows players to create new kingdoms, attack other players, and acquire resources and gold and build structures.

The new feature allows players to build and upgrade structures in their kingdom. Structures can be walls, towers, or barracks. The function buildStructure allows a player to build a new structure, it checks if the player has enough gold and if the structure doesn’t exist in the player’s kingdom. When a player builds a new structure, the gold is subtracted from the player’s kingdom and the structure is added to the kingdom with level 1.

The function upgradeStructure allows players to upgrade an existing structure, it checks if the player has enough gold, if the structure exist and if the structure is not at the max level. Upgrading a structure will increase the level of the structure and the gold will be subtracted from the player’s kingdom.

The function getStructureLevel allows players to check the current level of a structure in their kingdom. The function getStructureCost and getStructureUpgradeCost allows players to check the cost of building or upgrading a structure.

The smart contract also includes events that are emitted when a player builds or upgrades a structure, the event includes the player’s address and the name of the structure.

Categories
Crypto

EOS: rock-paper-scissors

Let’s build a game using EOS, and what better game to create than rock-paper-scissors? We’ve already covered EOS in a previous example, so now it’s time to put that knowledge into practice.

// rock-paper-scissors game in EOS

#include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>

using namespace eosio;

class rps : public contract {
 public:
	using contract::contract;

	[[eosio::action]]
	void play(name player1, name player2, std::string choice1, std::string choice2) {
		require_auth(player1);
		require_auth(player2);

		eosio_assert(choice1 == "rock" || choice1 == "paper" || choice1 == "scissors", "Invalid choice1");
		eosio_assert(choice2 == "rock" || choice2 == "paper" || choice2 == "scissors", "Invalid choice2");

		game_index games(_self, _self.value);
		games.emplace(player1, [&](auto& g) {
			g.key = games.available_primary_key();
			g.player1 = player1;
			g.player2 = player2;
			g.choice1 = choice1;
			g.choice2 = choice2;
		});
	}

	[[eosio::action]]
	void reveal(uint64_t key) {
		game_index games(_self, _self.value);
		auto itr = games.find(key);
		eosio_assert(itr != games.end(), "Game not found");

		name p1 = itr->player1;
		name p2 = itr->player2;
		require_auth(p1);
		require_auth(p2);

		endgame(itr);
	}

 private:
	struct [[eosio::table]] game {
		uint64_t key;
		name player1;
		name player2;
		std::string choice1;
		std::string choice2;

		uint64_t primary_key() const { return key; }
	};

	typedef eosio::multi_index<"games"_n, game> game_index;

	void endgame(game_index::const_iterator itr) {
		name winner;
		std::string choice1 = itr->choice1;
		std::string choice2 = itr->choice2;
		if (choice1 == choice2) {
			winner = "tie"_n;
		} else if (choice1 == "rock" && choice2 == "scissors" || choice1 == "paper" && choice2 == "rock" || choice1 == "scissors" && choice2 == "paper") {
			winner = itr->player1;
		} else {
			winner = itr->player2;
		}

		print("Winner: ", winner);
	}
};

EOSIO_DISPATCH(rps, (play)(reveal))

Full source code is here.

The contract allows players to play the game by specifying their choices of rock, paper, or scissors. The game is saved in a multi-index table, and the players can reveal the result by providing the game’s key. The contract includes two actions: play and reveal.

The play action is used to save the game with player1, player2, choice1, choice2.

The reveal action is used to reveal the game result, it requires the key of the game and the auth of player1 and player2.

The game struct is used to save the game details, it includes the primary key, player1, player2, choice1, and choice2.

The endgame function is used to reveal the game result and it takes a const reference to the game struct.

Categories
Crypto

EOS with examples

EOS is a blockchain platform that was created to support the development and execution of decentralized applications (dApps). It uses a Delegated Proof of Stake (DPoS) consensus algorithm, which allows for high scalability and fast transaction processing.

EOS has its own native cryptocurrency called EOS tokens, which are used to pay for resources such as network bandwidth and storage on the EOS blockchain. The EOS platform also includes a web assembly (WASM) virtual machine, which allows for the execution of smart contracts written in multiple programming languages.

One of the key features of EOS is its ability to handle a high number of transactions per second (TPS). While most blockchain platforms are limited to a few TPS, EOS is designed to handle millions of TPS. This is achieved through a combination of a highly efficient consensus algorithm and a unique system of resource allocation.

Another key feature of EOS is its governance structure. The EOS community elects 21 block producers (BPs) through a voting process, who are responsible for maintaining the network and producing new blocks. These BPs are incentivized to act in the best interest of the community, as they are rewarded with EOS tokens for their contributions to the network.

Smart Contracts

EOS smart contracts are written in web assembly (WASM) and can be written in multiple programming languages such as C++, Rust and others.

EOS smart contracts are similar to those on other blockchain platforms, such as Ethereum, in that they allow for the execution of complex logic and the creation of decentralized applications (dApps). However, EOS smart contracts have some unique features that set them apart from other platforms.

One of the key features of EOS smart contracts is their ability to use a “parallel processing” model. This means that multiple smart contracts can be executed simultaneously, which increases the overall speed and efficiency of the network.

Another key feature of EOS smart contracts is the ability to use a “multi-threading” model. This means that a single smart contract can be executed by multiple threads, which increases the overall performance and scalability of the network.

EOS smart contracts also have the ability to access and use the resources of the EOS blockchain, such as network bandwidth and storage. This is achieved through a unique system of resource allocation, where the developer must hold a certain amount of EOS tokens to access and use these resources.

There are also other features such as token creation, peer-to-peer transactions, and more…

Example: token.cpp

#include <eosio/eosio.hpp>

using namespace eosio;

CONTRACT token
: public contract {
public:
using contract::contract;

ACTION create(name issuer, asset maximum_supply);

ACTION issue(name to, asset quantity, string memo);

ACTION transfer(name from, name to, asset quantity, string memo);

private:
TABLE account{
	asset    balance;
	uint64_t primary_key()const { return balance.symbol.code().raw(); }
};

TABLE currency_stats{
	asset          supply;
	asset          max_supply;
	name           issuer;
	uint64_t primary_key()const { return supply.symbol.code().raw(); }
};

typedef eosio::multi_index<"accounts"_n, account> accounts;
typedef eosio::multi_index<"stat"_n, currency_stats> stats;

void sub_balance(name owner, asset value);

void add_balance(name owner, asset value, name ram_payer);

};

ACTION token::create(name issuer, asset maximum_supply)
{
	require_auth(issuer);
	auto sym = maximum_supply.symbol;
	eosio::check(sym.is_valid(), "invalid symbol name");
	eosio::check(maximum_supply.is_valid(), "invalid supply");
	eosio::check(maximum_supply.amount > 0, "max-supply must be positive");

	stats statstable(get_self(), sym.code().raw());
	auto existing = statstable.find(sym.code().raw());
	eosio::check(existing == statstable.end(), "token with symbol already exists");

	statstable.emplace(issuer, [&](auto& s)
	{
		s.supply = asset{ 0, sym };
		s.max_supply = maximum_supply;
		s.issuer = issuer;
	});
}

ACTION token::issue(name to, asset quantity, string memo)
{
	auto sym = quantity.symbol;
	eosio::check(sym.is_valid(), "invalid symbol name");
	eosio::check(memo.size() <= 256, "memo has more than 256 bytes");

	stats statstable(get_self(), sym.code().raw());
	auto existing = statstable.find(sym.code().raw());
	eosio::check(existing != statstable.end(), "token with symbol does not exist, create token before issue");
	const auto& st = *existing;

	require_auth(st.issuer);
	eosio::check(quantity.is_valid(), "invalid quantity");
	eosio::check(quantity.amount > 0, "must issue positive quantity");

	eosio::check(quantity.symbol == st.supply.symbol, "symbol precision mismatch");
	eosio::check(quantity.amount <= st.max_supply.amount - st.supply.amount, "quantity exceeds available supply");
	statstable.modify(st, same_payer, [&](auto& s)
	{
		s.supply += quantity;
	});

	add_balance(to, quantity, st.issuer);
}

ACTION token::transfer(name from, name to, asset quantity, string memo)
{
	eosio::check(from != to, "cannot transfer to self");
	require_auth(from);
	eosio::check(is_account(to), "to account does not exist");
	auto sym = quantity.symbol.code();
	stats statstable(get_self(), sym.raw());
	const auto& st = statstable.get(sym.raw());
	require_recipient(from);
	require_recipient(to);

	eosio::check(quantity.is_valid(), "invalid quantity");
	eosio::check(quantity.amount > 0, "must transfer positive quantity");
	eosio::check(quantity.symbol == st.supply.symbol, "symbol precision mismatch");
	eosio::check(memo.size() <= 256, "memo has more than 256 bytes");

	sub_balance(from, quantity);
	add_balance(to, quantity, from);
}

void token::sub_balance(name owner, asset value)
{
	accounts from_acnts(get_self(), owner.value);

	const auto& from = from_acnts.get(value.symbol.code().raw(), "no balance object found");
	eosio::check(from.balance.amount >= value.amount, "overdrawn balance");

	from_acnts.modify(from, owner, [&](auto& a)
	{
		a.balance -= value;
	});
}

void token::add_balance(name owner, asset value, name ram_payer)
{
	accounts to_acnts(get_self(), owner.value);
	auto to = to_acnts.find(value.symbol.code().raw());
	if (to == to_acnts.end())
	{
		to_acnts.emplace(ram_payer, [&](auto& a)
		{
			a.balance = value;
		});
	}
	else
	{
		to_acnts.modify(to, same_payer, [&](auto& a)
		{
			a.balance += value;
		});
	}
}

This contract is a template for creating, issuing, and transferring a custom ERC-20-like token on the EOS blockchain.

It inherits from the contract class provided by the EOSIO library, which allows it to interact with the EOS blockchain.

The contract defines three actions:

  • create: allows the issuer to create a new token with a specified maximum supply.
  • issue: allows the issuer to issue a certain amount of the token to a specified recipient.
  • transfer: allows a user to transfer a certain amount of the token to another user.

It also defines two tables:

  • account: stores the balance of each user for a specific token.
  • currency_stats: stores the overall supply, maximum supply, and issuer of the token.

And helper functions:

  • sub_balance: subtracts a certain amount of the token from a user’s balance.
  • add_balance: adds a certain amount of the token to a user’s balance.

Overall, this contract provides the basic functionality for creating and managing a custom token on the EOS blockchain, and can be adapted to the specific requirements of a given application.

Full source code here https://github.com/michaldrozd/eos-smart-contracts/blob/main/token_contract.cpp

Categories
Crypto

Cardano with examples

Cardano is a blockchain platform that uses a proof-of-stake consensus algorithm called Ouroboros. It is built using the Haskell programming language and is designed to be a more secure, flexible, and sustainable platform for the development and execution of smart contracts and decentralized applications (dapps).

One key feature of Cardano is its multi-layer architecture, which separates the settlement layer (where transactions are recorded) from the computation layer (where smart contracts are executed). This allows for greater flexibility and scalability, as changes to the computation layer can be made without affecting the settlement layer.

Another important aspect of Cardano is its use of formal verification, a method of mathematically proving the correctness of smart contract code. This helps to ensure the security and reliability of dapps built on the platform.

One example of a dapp built on Cardano is Plutus, a smart contract programming language that is specifically designed for the platform. Plutus allows for the creation of highly secure and reliable smart contracts, and also enables developers to write code that is more easily verifiable and less prone to errors.

Example: auction contract

Here is a practical smart contract in Plutus:

module Main where

import Language.PlutusTx
import Language.PlutusTx.Prelude
import Language.PlutusTx.Builtins

data Auction = Auction {
    itemName :: String,
    itemDescription :: String,
    startPrice :: Integer,
    highestBid :: Integer,
    highestBidder :: Address
}

placeBid : Auction -> Integer -> (Boolean, Auction)
placeBid auction bid =
    if bid > highestBid auction then
        (True, auction { highestBid = bid, highestBidder = txSender })
    else
        (False, auction)

endAuction : Auction -> (Boolean, String)
endAuction auction =
    if txSender == highestBidder auction then
        (True, "Auction sold to " ++ show (highestBidder auction) ++ " for " ++ show (highestBid auction))
    else
        (False, "Auction not ended")

main : () -> ()
main _ =
    let
        auction = Auction {
            itemName = "My Item",
            itemDescription = "This is a very nice item",
            startPrice = 100,
            highestBid = 0,
            highestBidder = Address "0x0"
        }
    in
    trace auction
    trace $ placeBid auction 150

This smart contract example is an auction contract, it defines a custom data type called “Auction” which has five fields, itemName, itemDescription, startPrice, highestBid, and highestBidder. It also defines two functions to interact with the auction, placeBid, endAuction.

The placeBid function allows a user to place a bid on an auction. It checks that the bid is greater than the current highest bid and if so, it updates the highest bid and highest bidder. It returns a tuple of Boolean and the updated auction. The boolean value is true if the bid is successfully placed, otherwise it is false.

The endAuction function allows the highest bidder to end the auction and it checks that the sender of the transaction is the highest bidder. If so, it returns a tuple of Boolean and a message indicating that the auction is sold to the highest bidder for the highest bid amount. Otherwise, it returns a tuple of Boolean and a message indicating that the auction is not ended.

The main function is the entry point of the contract, it creates a new auction, traces it, and traces the result of the placeBid function.

The source code is here https://github.com/michaldrozd/cardano-smart-contracts/blob/main/AuctionContract.plutus

Example: crowdfunding

Another example of a practical smart contract that implements a simple crowdfunding campaign written in Plutus for Cardano:

module Main where

import Language.PlutusTx
import Language.PlutusTx.Prelude
import Language.PlutusTx.Builtins

data Campaign = Campaign {
    campaignName :: String,
    campaignDescription :: String,
    targetFunding :: Integer,
    currentFunding :: Integer,
    deadline :: Integer
}

contribute : Campaign -> Integer -> (Boolean, Campaign)
contribute campaign amount =
    if (txTimestamp <= deadline campaign) && (currentFunding campaign + amount <= targetFunding campaign) then
        (True, campaign { currentFunding = currentFunding campaign + amount })
    else
        (False, campaign)

refund : Campaign -> Address -> (Boolean, Integer)
refund campaign contributor =
    if (txSender == contributor) && (currentFunding campaign < targetFunding campaign) then
        (True, currentFunding campaign)
    else
        (False, 0)

main : () -> ()
main _ =
    let
        campaign = Campaign {
            campaignName = "My Campaign",
            campaignDescription = "This is a very nice campaign",
            targetFunding = 1000,
            currentFunding = 0,
            deadline = 10000000000
        }
    in
    trace campaign
    trace $ contribute campaign 500

This smart contract defines a custom data type called “Campaign” which has five fields, campaignName, campaignDescription, targetFunding, currentFunding, and deadline. It also defines two functions to interact with the campaign, contribute, refund.

The contribute function allows a user to contribute to a campaign. It checks that the transaction timestamp is less than or equal to the campaign deadline and that the total funding after the contribution is less than or equal to the target funding. If the checks pass, it updates the current funding and returns a tuple of Boolean and the updated campaign. The boolean value is true if the contribution is successfully placed, otherwise it is false.

The refund function allows a contributor to get a refund if the campaign is not fully funded. It checks that the sender of the transaction is the contributor and that the current funding is less than the target funding. If the checks pass, it returns a tuple of Boolean and the current funding. Otherwise, it returns a tuple of Boolean and 0.

The main function is the entry point of the contract, it creates a new campaign, traces it, and traces the result of the contribute function.

The source code is here https://github.com/michaldrozd/cardano-smart-contracts/blob/main/CrowdfundingCampaign.plutus

Example: Escrow

module Main where

import Language.PlutusTx
import Language.PlutusTx.Prelude
import Language.PlutusTx.Builtins

data Escrow = Escrow {
    buyer :: Address,
    seller :: Address,
    arbitrator :: Address,
    item :: String,
    amount :: Integer,
    status :: String
}

create : Address -> Address -> Address -> String -> Integer -> Escrow
create buyer seller arbitrator item amount =
    Escrow {
        buyer = buyer,
        seller = seller,
        arbitrator = arbitrator,
        item = item,
        amount = amount,
        status = "created"
    }

release : Escrow -> (Boolean, Escrow)
release escrow =
    if (txSender == buyer escrow) && (status escrow == "created") then
        (True, escrow { status = "released" })
    else
        (False, escrow)

refund : Escrow -> (Boolean, Escrow)
refund escrow =
    if (txSender == seller escrow) && (status escrow == "created") then
        (True, escrow { status = "refunded" })
    else
        (False, escrow)

arbitrate : Escrow -> (Boolean, Escrow)
arbitrate escrow =
    if (txSender == arbitrator escrow) && (status escrow == "created") then
        (True, escrow { status = "arbitrated" })
    else
        (False, escrow)

main : () -> ()
main _ =
    let
        escrow = create (Address "0x1") (Address "0x2") (Address "0x3") "My Item" 500
    in
    trace escrow
    trace $ release escrow

This smart contract example is an escrow service, it defines a custom data type called “Escrow” which has six fields, buyer, seller, arbitrator, item, amount and status. It also defines three functions to interact with the escrow create, release, refund and arbitrate.

The create function allows a user to create a new escrow transaction, it takes the buyer, seller and arbitrator addresses, an item name and the amount of the transaction, it creates an escrow object with the given data.

The release function allows the buyer to release the funds to the seller after they have received the item. It checks that the sender of the transaction is the buyer and that the status of the escrow is “created”, if so it updates the status of the escrow to “released” and returns a tuple of Boolean and the updated escrow. The boolean value is true if the release is successful, otherwise it is false.

The refund function allows the seller to refund the funds to the buyer if they have not received the item. It checks that the sender of the transaction is the seller and that the status of the escrow is “created”, if so it updates the status of the escrow to “refunded” and returns a tuple of Boolean and the updated escrow. The boolean value is true if the refund is successful, otherwise it is false.

The arbitrate function allows the arbitrator to resolve the escrow if there is a dispute between the buyer and the seller. It checks that the sender of the transaction is the arbitrator and that the status of the escrow is “created”, if so it updates the status of the escrow to “arbitrated” and returns a tuple of Boolean and the updated escrow. The boolean value is true if the arbitration is successful, otherwise it is false.

The main function is the entry point of the contract, it creates a new escrow, traces it, and traces the result of the release function.

Here is full source code: https://github.com/michaldrozd/cardano-smart-contracts/blob/main/EscrowService.plutus

Other real-world examples

Another example of a dapp built on cardano is Atala PRISM, which is a decentralized identity solution that allows individuals and organizations to have full control over their own digital identities.

A practical use case for Cardano is in the field of voting systems. A company called Agora is building a decentralized voting platform on Cardano that uses blockchain technology to ensure the security and transparency of the voting process. The platform allows for remote voting and eliminates the need for paper ballots, making the voting process more efficient and accessible.

Another example is in the field of charity and donation, a company called Giveth is building a platform that allows individuals to donate to charity organizations in a transparent and efficient way, by using smart contract to make sure the donation reach its intended target.

In summary, Cardano is a blockchain platform that uses a proof-of-stake consensus algorithm, is built using the Haskell programming language and designed to be more secure, flexible, and sustainable platform for the development and execution of smart contracts and decentralized applications (dapps). Its multi-layer architecture separates the settlement layer from the computation layer and uses formal verification as a method of mathematically proving the correctness of smart contract code.