首页 > 其他分享 >认识智能合约&线上 IDE实现Solidity 合约

认识智能合约&线上 IDE实现Solidity 合约

时间:2024-01-20 22:59:00浏览次数:24  
标签:宠物 dna name AnimalId Solidity uint IDE 合约 函数

实验五:认识智能合约&线上 IDE 实现 Solidity 合约

实验概述

本实验参考自以太坊中的以太猫游戏和 Loom Network 团队的智能合约教学案例,进行 Solidity 智能合约入门与 remix 在线 IDE 使用练习,通过构建一个“宠物游戏”来学习智能合约的编写,在实验中穿插 Solidity 基础知识。

实验5-1:Solidity 基础

电子宠物很多人都知道,乃至养过,其中以拓麻歌子最为著名。本实验则以电子宠物为背景,来用智能合约的方式创造一个新的“电子宠物世界”。
实验 5-1 目的创造一个“宠物孵化池”, 通过孵化池,就能够从中诞生一个全新的宠物。那么这个孵化池需要满足这么几个功能:
• 数据库中能够记录所有的宠物信息
• 应该在孵化池中存在这样一个接口,让我们能够从中孵化宠物
• 每个宠物都应该是独一无二的
宠物 DNA
参考以太猫的 DNA,宠物的独立标识则为他的 DNA。本实验定义的 DNA 很简单,由一个 16 位的整数组成:
8356281049284737
事实上,每一只电子宠物也是由这样一串数字构成,他的各位则表示了不同的属性。比如前 2 位表示物种,紧接着的 2 位表示有没有翅膀,等等。
实验内容

  1. 清空原有.sol 文件,新建文件 AnimalIncubators.sol
  2. 为了建立宠物部队,先建立一个基础合约 AnimalIncubators,并指定 Solidity 编译器版本。
  3. 因为我们的宠物 DNA 由十六位数字组成,所以首先,需要在合约中定义 dnaDigits 为 uint 数据类型, 并赋值 16,用来表示 dna 的位数。
  4. 为了保证我们的宠物的 DNA 只含有 16 个字符,我们需要一个 uint 变量等于 10^16。便于后续取模。
    – 建立一个 dnaLength 变量, 令其等于 10 的 dnaDigits 次方。
  5. 创建宠物结构体
    – 建立一个名为 Animal 的结构体。在其中有两个属性: name (类型为
    string), 和 dna (类型为 uint)。
  6. 准备工作完成后,首先需要将宠物们保存在合约中,并且让其它合约也能够看到这些宠物们,因此需要一个公共数组。
    – 创建一个 Animal 的结构体数组,用 public 修饰,命名为 animals.
  7. 定义一个事件 NewAnimal。 它有 3 个参数: AnimalId (uint), name (string),和 dna (uint)。
  8. 定义一个 孵化宠物函数,其功能为:孵化一个新宠物并添加入 animals 数组中
    • 建立一个私有函数 _createAnimal。 它有两个参数: _name (类型为 string), 和 _dna (类型为 uint)。
    • 在函数体里新创建一个 Animal, 然后把它加入 animals 数组中。 很显然新创建的宠物属性就来自于函数的入参,同时将 animals 的索引值记录下来为 animalId。
    • 在函数结束触发事件 NewAnimal。
  9. 定义 DNA 生成函数:能够根据字符串随机生成一个 DNA。
    • 创建一个函数 _generateRandomDna,将属性标记为 private。它只接收一个输入变量 _str (类型 string), 返回一个 uint 类型的数值。此函数只读取我们合约中的一些变量,所以可以标记 view 属性。
    • 使用以太坊内部的哈希函数 keccak256,根据输入参数_str 来生成一个十六进制数,类型转换为 uint 后,返回该值的后 dnaLength 位。
    Ethereum 内部有一个散列函数 keccak256,它用了 SHA3 版本。一个散列函数基本上就是把一个字符串转换为一个 256 位的 16 进制数字。字符串的一个微小变化会引起散列数据极大变化。 这在 Ethereum 中有很多应用,但是现在我们只是用它造一个伪随机数。
    使用样例:keccak256("abcdefg")
  10. 定义 第一个公共函数来把上面定义的若干部件组合起来,使得实现这样的一个功能:该函数能够接收宠物的名字,然后用这个名字来生成宠物的 DNA
    – 创建一个 public 函数,命名为 createRandomAnimal. 它将接收一个变量 _name (数据类型是 string)。
    – 首先调用 _generateRandomDna 函数,传入 _name 参数来生成一个DNA,取名为 randDna。
    – 调用 _createAnimal 函数,将这个新生成的宠物记录下来, 传入参数: _name 和 randDna。
    需实现效果
    在 JavaScript VM 环境下,部署 AnimalIncubators 合约。创建三个分别叫Drogon、Rheagal、Viserion 的宠物,展示其 DNA。
实验流程

创造一个“宠物孵化池”, 通过孵化池,就能够从中诞生一个全新的宠物。

在 JavaScript VM 环境下,部署 AnimalIncubators 合约。创建三个分别叫 Drogon、Rheagal、Viserion 的宠物。

  • 新建文件 AnimalIncubators.sol。
  • 建立一个基础合约 AnimalIncubators,并指定 Solidity编译器版本。
  • 在合约中定义dnaDigits 为 uint 数据类型, 并赋值 16,用来表示 dna 的位数。
  • 定义一个 uint 变量等于10^16,便于后续取模。
  • 创建宠物结构体,包含name和dna两个属性。
  • 创建一个公共数组,将宠物们保存在合约中,并且让其它合约也能够看到这些宠物们。
  • 定义一个事件 NewAnimal,包含 3 个参数: AnimalId (uint), name (string),和 dna (uint)。
  • 定义一个 孵化宠物函数,其功能为:孵化一个新宠物并添加入 animals 数组中。
  • 定义 DNA 生成函数,使其能够根据字符串随机生成一个 DNA。
  • 定义 第一个公共函数来把上面定义的若干部件组合起来,使得该函数能够接收宠物的名字,然后用这个名字来生成宠物的 DNA。
源代码

AnimalIncubators.sol

pragma solidity ^0.4.26;

contract AnimalIncubators{
    uint dnaDigits=16;
    uint dnaLength=10**16;
    struct Animal{
        string name;
        uint dna;
    }
    Animal[] public animals;
    event NewAnimal(uint AnimalID,string name,uint dna);
    function _createAnimal(string _name,uint _dna) private{
        animals.push(Animal(_name,_dna));
        uint animalID=animals.length-1;
        NewAnimal(animalID,_name,_dna);
    }
    function _generateRandomDna(string _str)private view returns(uint){
        return uint(keccak256(_str))%dnaLength;
    }
    function createRandomAnimal(string _name) public{
        uint randDna=_generateRandomDna(_name);
        _createAnimal(_name,randDna);
    }
}

实验结果截图

创建名为Drogon的宠物:

image

创建名为Rheagal的宠物:

image

创建名为Viserion的宠物:

image

实验5-2:Solidity 进阶——宠物成长系统

实验一中创建了一个函数用来生成宠物,并且存入区块链上的宠物数据库中。 实验二中,我们会模拟以太猫的繁殖机制,创建一个宠物成长系统,让宠物可以通过进食进行成长,系统会通过宠物和食物的 DNA 计算出新宠物的 DNA。
实验内容

  1. 首先使用地址给宠物指定“主人”,为了存储宠物的所有权,我们会使用到两个映射:一个记录宠物拥有者的地址,另一个记录某地址所拥有宠物的数量。
    – 创建一个叫做 AnimalToOwner 的映射。其键是一个 uint(我们将根据它的 id 存储和查找宠物),值为 address。映射属性为 public。
    – 创建一个名为 ownerAnimalCount 的映射,其中键是 address,值是 uint。
  2. 修改_createAnimal 函数来使用映射
    – 首先,在得到新的宠物 animalId 后,更新 AnimalToOwner 映射,在AnimalId 下面存入 msg.sender。
    – 然后,我们为这个 msg.sender 名下的 ownerAnimalCount 加 1。
  3. 在 createRandomAnimal 开头使用 require 来确保这个函数只有在每个用户第一次调用它的时候执行,用以创建初始宠物。 判断方式:判断该用户的宠物数是否为 0
  4. 创建新文件 AnimalFeeding.sol 文件,在其中创建 AnimalFeeding 合约,继承自 AnimalIncubators。记得设置编译版本与 import。
  5. 在 AnimalFeeding 合约中,增加进食功能: 当一个宠物进食后,它自身的DNA 将与食物的 DNA 结合在一起,形成一个新的宠物 DNA ,
    – 创建一个名为 feedAndGrow 的函数。 包含两个参数:_AnimalId(uint)、_targetDna (uint),分别表示宠物、食物。 设置属性为 public 。
    – 要求只有宠物的主人才能给宠物喂食,在函数开始添加一个 require 语句来确保 msg.sender 只能是这个宠物的主人。
    – 声明一个名为 myAnimal 数据类型为 Animal 的本地变量(这是一个storage 型的指针),将其值设定为在 animals 数组中索引为_AnimalId 所指向的值。
  6. 完成 feedAndGrow 函数:
    – 取得_targetDna 的后 dnaDigits 位
    – 生成新的宠物 DNA:计算宠物与食物 DNA 的平均值
    – 为宠物添加标识:将新的宠物 DNA 最后两位改为“99”。
    – 调用_createAnimal 生成新宠物,新宠物名字为“No-one”。(需要修改_createAnimal 函数属性使对继承可见)。
  7. 在 AnimalFeeding 合约中增加捕食函数:
    function _catchFood(uint _name) internal pure returns (uint) {
    uint rand = uint(keccak256(_name));
    return rand;
    }
  8. 在 AnimalFeeding 合约中,增加进食功能:宠物抓住食物后进食,然后成长为一个新宠物
    – 创建一个名为 feedOnFood 的函数。它需要 2 个 uint 类型的参数,
    _AnimalId 和_FoodId ,这是一个 public 类型的函数。
    – 调用_catchFood 函数,获得一个食物 DNA。
    – 调用 feedAndGrow 函数。传入宠物 id 和食物 dna,调用feedAndGrow。
    需实现效果
    部署 AnimalFeeding 合约,实现以下效果:
    • 同一账户只可调用一次 createRandomAnimal
    • 以三个用户身份添加 Drogon、Rheagal、Viserion 的宠物
    • 让 Drogon 宠物进食成长一次,展示新宠物的主人与 Drogon 相同
实验流程

模拟以太猫的繁殖机制,创建一个宠物成长系统,让宠物可以通过进食进行成长,系统会通过宠物和食物的 DNA 计算出新宠物的 DNA。

部署 AnimalFeeding 合约,实现以下效果:

  • 同一账户只可调用一次 createRandomAnimal。

  • 以三个用户身份添加 Drogon、Rheagal、Viserion 的宠物。

  • 让 Drogon 宠物进食成长一次,展示新宠物的主人与 Drogon 相同。

实验流程如下:

  • 首先使用地址给宠物指定“主人”,为了存储宠物的所有权,先创建两个映射:一个记录宠物拥有者的地址,另一个记录某地址所拥有宠物的数量。
  • 修改_createAnimal 函数来使用映射。
  • 在 createRandomAnimal 开头使用 require 来确保这个函数只有在每个用户第一次调用它的时候执行,用以创建初始宠物。
  • 创建新文件 AnimalFeeding.sol 文件,在其中创建 AnimalFeeding 合约,继承自AnimalIncubators。
  • 在 AnimalFeeding 合约中,增加进食功能: 当一个宠物进食后,它自身的DNA 将与食物的 DNA 结合在一起,形成一个新的宠物 DNA。
  • 完成 feedAndGrow 函数。
  • 在 AnimalFeeding 合约中增加捕食函数。
  • 在 AnimalFeeding 合约中,增加进食功能:宠物抓住食物后进食,然后成长为一个新宠物。
源代码

AnimalIncubators.sol

pragma solidity ^0.4.26;

contract AnimalIncubators{
    uint dnaDigits=16;
    uint dnaLength=10**16;
    struct Animal{
        string name;
        uint dna;
    }
    mapping(uint=>address) public AnimalToOwner;
    mapping(address=>uint) ownerAnimalCount;
    Animal[] public animals;
    event NewAnimal(uint AnimalID,string name,uint dna);
    function _createAnimal(string _name,uint _dna) internal{
        animals.push(Animal(_name,_dna));
        uint _animalID=animals.length-1;
        AnimalToOwner[_animalID]=msg.sender;
        ownerAnimalCount[msg.sender]+=1;
        NewAnimal(_animalID,_name,_dna);
    }
    function _generateRandomDna(string _str)private view returns(uint){
        return uint(keccak256(_str))%dnaLength;
    }
    function createRandomAnimal(string _name) public{
        require(ownerAnimalCount[msg.sender]==0);
        uint randDna=_generateRandomDna(_name);
        _createAnimal(_name,randDna);
    }
}

AnimalFeeding.sol

pragma solidity ^0.4.26;
import "./AnimalIncubators.sol";
contract AnimalFeeding is AnimalIncubators{
    function feedAndGrow(uint _AnimalId,uint _targetDna)public{
        require(keccak256(msg.sender)==keccak256(AnimalToOwner[_AnimalId]));
        Animal storage myAnimal=animals[_AnimalId];
        uint _tarDna=_targetDna%dnaLength;
        uint _petDna=uint((myAnimal.dna+_tarDna)/2);
        uint _newDna=(_petDna/100)*100+99;
        _createAnimal("No-one",_newDna);
    }
    function _catchFood(uint _name) internal pure returns(uint){
        uint rand=uint(keccak256(_name));
        return rand;
    }
    function feedOnFood(uint _AnimalId,uint _FoodId) public{
        uint _foodDna=_catchFood(_FoodId);
        feedAndGrow(_AnimalId,_foodDna);
    }
}
实验结果截图

创建宠物Drogon,运行正常:

image

在同一账户创建宠物Rheagal,发生了报错:

image

由此可说明同一账户只可调用一次 createRandomAnimal。

创建一个新的账户,创建宠物Rheagal:

image

再创建一个新的账户,创建宠物Viserion:

image

返回第一个账户,查询宠物主人地址:

image

对宠物Drogon进行一次投喂,得到新宠物,再次查询宠物主人地址:

image

可以看到新宠物主人的地址没有发生变化,因此新宠物的主人与Drogon相同。

实验5-3:Solidity 高阶理论

Solidity 很像 JavaScript,但是以太坊上的合约与普通的程序最大的区别就是以太
坊上的合约代码一旦上链就无法更改,即使合约出现 bug,也无法进行修改,之恶
能放弃这个合约让用户去使用一个新的被修复过的合约,虽然这可能会造成使用上
的不便,但这也是智能合约的优势之一,一旦你的代码上链,就无法被他人恶意篡
改,其他人调用也只能以预设的逻辑一直执行下去。
实验内容

  1. 将 Ownable 代码复制一份到新文件 ownable.sol 中,让 AnimalIncubators 作为子类继承 Ownable。
  2. 给 createRandomAnimal 函数添加 onlyOwner,再用不同的账户进行调用,看看有什么区别,完成后再删掉它,毕竟其他玩家也要调用。
  3. 在 AnimalIncubators.sol 中给宠物添 2 个新属性: level(uint32)和readyTime(uint32) - 一个是等级,一个是进食的冷却时间。
  4. 给 DApp 添加一个“冷却周期”的设定,让宠物两次进食之间必须等待 1min。
    – 声明一个名为 cooldownTime 的 uint,并将其设置为 1 分钟
    – 修改_createAnimal 中添加。
    注: now 返回类型 uint256 ,需要类型转换。
    再来到 AnimalFeeding.sol 的 feedAndGrow 函数:
    – 修改可见性 internal 以保障合约安全。
    – 在_targetDna 计算前,检查该宠物是否已经冷却完毕。
    – 在函数结束时重新设置宠物冷却周期,以表示其捕食行为重新进入冷却。
  5. 接下来撰写一个属于宠物自己的函数修饰符,让宠物能够在达到一定水平后获得特殊能力:
    – 创建一个新的文件 AnimalHelper.sol, 定义合约 AnimalHelper 继承自 nimalFeeding
    – 创建一个名为 aboveLevel 的 modifier,它接收 2 个参数, _level(uint) 以及 _AnimalId (uint)。
    – 函数逻辑确保宠物 Animals[_AnimalId].level 大于或等于 _level。
    – 修饰符的最后一行为 _;,表示修饰符调用结束后返回,并执行调用函数余下的部分。
  6. 添加一些使用 aboveLevel 修饰符的函数来作为达到 level 的奖励。激励玩家们去升级他们的宠物:
    – 创建一个名为 changeName 的函数。它接收 2 个参数:_AnimalId(uint 类型)以及 _newName(string 类型),可见性为 external。
    它带有一个 aboveLevel 修饰符,调用的时候通过 _level 参数传入 2,当然,别忘了同时传 _AnimalId 参数。 在函数中使用 require 检查msg.sender 是否是宠物主人,如果是则将宠物名改为_newName
    – 在 changeName 下创建另一个名为 changeDna 的函数。它的定义和内容几乎和 changeName 相同,不过它第二个参数是 _newDna(uint 类型),在修饰符 aboveLevel 的 _level 参数中传递 20 。在函数中可以把宠物的 dna 设置为 _newDna 。
  7. 定义一个新函数 getAnimalsByOwner 来获取某个玩家的所有宠物:
    – 函数有一个参数_owner(address),声明为 external view 属性,返回一个 uint 数组。
    – 声明一个名为 result 的 uint [] memory (内存变量数组) ,其长度为该_owner 拥有的宠物数量。
    – 使用 for 循环遍历 Animals 数组,将主人为_owner 的宠物添加入result
    – 返回 result
    这样 getAnimalsByOwner 不花费任何 gas。
    需实现效果
    • 展示宠物的冷却时间。
    • 成功实现 getAnimalsByOwner 函数。
实验流程

为DApp 添加一个“冷却周期”的设定,让宠物两次进食之间必须等待 1min。并成功实现getAnimalsByOwner 函数。

  • 将 Ownable 代码复制一份到新文件 ownable.sol 中,让 AnimalIncubators 作为子类继承 Ownable。
  • 给 createRandomAnimal 函数添加 onlyOwner,用不同的账户进行调用,观察区别,最后删除。
  • 在 AnimalIncubators.sol 中给宠物添 2 个新属性: level(uint32)和 readyTime(uint32)。
  • 给 DApp 添加一个“冷却周期”的设定,让宠物两次进食之间必须等待 1min。
  • 撰写一个属于宠物自己的函数修饰符,让宠物能够在达到一定水平后获得特殊能力。
  • 添加一些使用 aboveLevel 修饰符的函数来作为达到 level 的奖励。激励玩家们升级宠物。
  • 定义一个新函数 getAnimalsByOwner 来获取某个玩家的所有宠物。
源代码

Ownable.sol

/** 
* @title Ownable 
* @dev The Ownable contract has an owner address, and provides basic a
 uthorization 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; 
        } 
    } 

AnimalIncubators.sol

pragma solidity ^0.4.26;
import "./ownable.sol";

contract AnimalIncubators is Ownable{
    uint dnaDigits=16;
    uint dnaLength=10**16;
    uint32 cooldownTime=60;
    struct Animal{
        string name;
        uint dna;
        uint32 level;
        uint32 readyTime;
    }
    mapping(uint=>address) public AnimalToOwner;
    mapping(address=>uint) ownerAnimalCount;
    Animal[] public animals;
    event NewAnimal(uint AnimalID,string name,uint dna);
    function _createAnimal(string _name,uint _dna) internal{
        animals.push(Animal(_name,_dna,0,uint32(now)));
        uint _animalID=animals.length-1;
        AnimalToOwner[_animalID]=msg.sender;
        ownerAnimalCount[msg.sender]+=1;
        NewAnimal(_animalID,_name,_dna);
    }
    function _generateRandomDna(string _str)private view returns(uint){
        return uint(keccak256(_str))%dnaLength;
    }
    function createRandomAnimal(string _name) public{
        require(ownerAnimalCount[msg.sender]==0);
        uint randDna=_generateRandomDna(_name);
        _createAnimal(_name,randDna);
    }

}

AnimalFeeding.sol

pragma solidity ^0.4.26;
import "./AnimalIncubators.sol";
contract AnimalFeeding is AnimalIncubators{
    function feedAndGrow(uint _AnimalId,uint _targetDna)internal{
        require(keccak256(msg.sender)==keccak256(AnimalToOwner[_AnimalId]));
        Animal storage myAnimal=animals[_AnimalId];
        require(now>=myAnimal.readyTime);
        uint _tarDna=_targetDna%dnaLength;
        uint _petDna=uint((myAnimal.dna+_tarDna)/2);
        uint _newDna=(_petDna/100)*100+99;
        _createAnimal("No-one",_newDna);
        myAnimal.readyTime=uint32(now)+cooldownTime;
    }
    function _catchFood(uint _name) internal pure returns(uint){
        uint rand=uint(keccak256(_name));
        return rand;
    }
    function feedOnFood(uint _AnimalId,uint _FoodId) public{
        uint _foodDna=_catchFood(_FoodId);
        feedAndGrow(_AnimalId,_foodDna);
    }
}

AnimalHelper.sol

pragma solidity ^0.4.26;
import "./AnimalFeeding.sol";
contract AnimalHelper is AnimalFeeding{
    modifier aboveLevel(uint _level,uint _AnimalId){
        require(animals[_AnimalId].level>=_level);
        _;
    }
    function changeName(uint _AnimalId,string _newName) external aboveLevel(2,_AnimalId){
        require(keccak256(msg.sender)==keccak256(AnimalToOwner[_AnimalId]));
        animals[_AnimalId].name=_newName;
    }
    function changeDna(uint _AnimalId,uint _newDna) external aboveLevel(20,_AnimalId){
        require(keccak256(msg.sender)==keccak256(AnimalToOwner[_AnimalId]));
        animals[_AnimalId].dna=_newDna;
    }
    function getAnimalsByOwner(address _owner) external view returns (uint[]){
        uint[] memory result=new uint[](ownerAnimalCount[_owner]);
        uint cnt=0;
        for (uint i=1;i<animals.length;i++){
            if(keccak256(AnimalToOwner[i])==keccak256(_owner)){
                result[cnt]=i;
                cnt++;
            }
        }
        return result;
    }
}
实验结果截图

新建一只宠物Drogon,第一次投喂进行,中断没有发生报错:

image

紧接着再次投喂,终端发生报错,执行失败:

image

getAnimalsByOwner 函数的源码在上述AnimalHelper.sol文件中。

选取一个宠物主人的地址,用getAnimalsByOwner函数进行执行:

image

实验5-4:solidity 高阶篇

实验内容

  1. 在 AnimalHelper 中,添加支付系统——宠物主人可以通过支付 ETH(对,氪金)的方式来强化他们的宠物。这些支付的 ETH 将存储在你拥有的合约中,向你展示如何通过你的合约赚钱。
    – 定义一个名为 powerUpFee 的 uint 类型变量, 将值设定为 0.001 ether。
    – 定义一个名为 powerUp 的函数。 它将接收一个 uint 参数 _animalId。
    函数记得修饰 external 以及 payable 属性。
    – 这个函数首先应该 require 确保 msg.value 等于 powerUpFee。
    – 函数将增加指定宠物的 level 属性: animals[_animalId].level++。
    需实现效果:成功升级,并改名。
  2. 在 AnimalHelper 中,添加提现系统
    – 创建一个 withdraw 函数,参考上面的 GetPaid 样例。
    – 以太的价格在过去几年内不停地波动,所以我们应该再创建一个函数,允许我们以合约拥有者的身份来设置 powerUpFee。
    a. 创建一个函数,名为 setPowerUpFee, 其接收一个参数 uint_fee,是 external 并标记为仅 owner 可用。
    b. 这个函数会将 powerUpFee 等于 _fee。
    需实现效果:实现一次提现操作。
  3. 实现战斗升级系统
    新建一个文件 AnimalAttack.sol,在其中新建一个继承自 AnimalHelper 的合约 AnimalAttack,在这之下编辑新的合约的主要部分。
    需实现效果:
    • 你选择一只自己的宠物,然后选择一个对手的宠物去战斗。
    • 如果你是战斗发起方(先手),你将有 75%的几率获胜,防守方将有 25%的几率获胜。
    • 每一只宠物(攻守双方)都会有一个 winCount 和一个 lossCount,用来记录该宠物的战斗结果。
    • 若发起方获胜,这只宠物的 level 将增加。
    • 如果攻击方失败,除了失败次数将加一外,什么都不会发生。
    • 无论输赢,当前宠物的战斗冷却时间都将被重置(很显然,饿了嘛)。
实验流程
  1. 在 AnimalHelper 中,添加支付系统——宠物主人可以通过支付 ETH 的方式来强化他们的宠物。
  2. 在 AnimalHelper 中,添加提现系统。
  3. 实现战斗升级系统。
  4. 选择一只自己的宠物与一个对手的宠物去战斗。
源代码

AnimalIncubators.sol

pragma solidity ^0.4.26;
import "./ownable.sol";

contract AnimalIncubators is Ownable{
    uint dnaDigits=16;
    uint dnaLength=10**16;
    uint32 cooldownTime=60;
    struct Animal{
        string name;
        uint dna;
        uint32 level;
        uint32 readyTime;
        uint winCount;
        uint lossCount;
    }
    mapping(uint=>address) public AnimalToOwner;
    mapping(address=>uint) ownerAnimalCount;
    Animal[] public animals;
    event NewAnimal(uint AnimalID,string name,uint dna);
    function _createAnimal(string _name,uint _dna) internal{
        animals.push(Animal(_name,_dna,0,uint32(now),0,0));
        uint _animalID=animals.length-1;
        AnimalToOwner[_animalID]=msg.sender;
        ownerAnimalCount[msg.sender]+=1;
        NewAnimal(_animalID,_name,_dna);
    }
    function _generateRandomDna(string _str)private view returns(uint){
        return uint(keccak256(_str))%dnaLength;
    }
    function createRandomAnimal(string _name) public{
        require(ownerAnimalCount[msg.sender]==0);
        uint randDna=_generateRandomDna(_name);
        _createAnimal(_name,randDna);
    }

}

AnimalHelper.sol

pragma solidity ^0.4.26;
import "./AnimalFeeding.sol";
contract AnimalHelper is AnimalFeeding{
    uint powerUpFee=0.001 ether;
    modifier aboveLevel(uint _level,uint _AnimalId){
        require(animals[_AnimalId].level>=_level);
        _;
    }
    function changeName(uint _AnimalId,string _newName) external aboveLevel(2,_AnimalId){
        require(keccak256(msg.sender)==keccak256(AnimalToOwner[_AnimalId]));
        animals[_AnimalId].name=_newName;
    }
    function changeDna(uint _AnimalId,uint _newDna) external aboveLevel(20,_AnimalId){
        require(keccak256(msg.sender)==keccak256(AnimalToOwner[_AnimalId]));
        animals[_AnimalId].dna=_newDna;
    }
    function getAnimalsByOwner(address _owner) external view returns (uint[]){
        uint[] memory result=new uint[](ownerAnimalCount[_owner]);
        uint cnt=0;
        for (uint i=1;i<animals.length;i++){
            if(keccak256(AnimalToOwner[i])==keccak256(_owner)){
                result[cnt]=i;
                cnt++;
            }
        }
        return result;
    }
    function powerUp(uint _animalId) external payable{
        require(msg.value==powerUpFee);
        animals[_animalId].level++;
    }
    function withdraw() external onlyOwner{
        owner.transfer(this.balance);
    }
    function setPowerUpFee(uint _fee) external onlyOwner{
        powerUpFee=_fee;
    }
}

AnimalAttack.sol

pragma solidity ^0.4.26;
import "./AnimalHelper.sol";
contract AnimalAttack is AnimalHelper{
    uint randNonce=0;
    function winProbability() public returns(bool){
        uint random=uint(keccak256(now,msg.sender,randNonce))%100;
        randNonce++;
        return (random<75);
    }
    function fight(uint _attackAnimalId,uint _defenceAnimalId) external{
        if(winProbability()){
            animals[_attackAnimalId].level++;
            animals[_attackAnimalId].winCount++;
            animals[_defenceAnimalId].lossCount++;
        }
        else{
            animals[_attackAnimalId].lossCount++;
            animals[_defenceAnimalId].winCount++;
        }
        animals[_attackAnimalId].readyTime=uint32(now);
        animals[_defenceAnimalId].readyTime=uint32(now);
    }
}
实验结果截图

创建两只宠物进行战斗,函数成功执行:

image

标签:宠物,dna,name,AnimalId,Solidity,uint,IDE,合约,函数
From: https://www.cnblogs.com/Silverplan/p/17977122

相关文章

  • HarmonyOS4.0系列——05、状态管理之@Prop、@Link、@Provide、@Consume,以及@Watch装饰
    状态管理看下面这张图Components部分的装饰器为组件级别的状态管理,Application部分为应用的状态管理。开发者可以通过@StorageLink/@LocalStorageLink实现应用和组件状态的双向同步,通过@StorageProp/@LocalStorageProp实现应用和组件状态的单向同步。@PropstaticProp(propName:......
  • 【Pyside2】环境搭建
    (Pyside开发环境搭建)安装Pyside2:<fontcolor=#999AAA>pipinstall--index-url=http://download.qt.io/snapshots/ci/pyside/5.11/latest/pyside2--trusted-hostdownload.qt.io<hrstyle="border:solid;width:100px;height:1px;"color=#000000size=......
  • Mac使用Idea配置传统SSM项目(非maven项目)
    前提软件IDEA2023.3.2JDK1.8Tomcat8Mysql5.7.1步骤打开Web项目配置JDK版本配置Modules中配置Sources的两个root,分别为WebRoot和src两个文件夹Libraries中配置WebRoot/WEB-INF/lib文件夹Facets中指定web的根目录和web.xml文件位置Artifacts配置tomcattoncat......
  • 在 .net 8 Blazor Identity 中添加Claim
    .net8BlazorIdentity使用IndividualAccount模版时,默认的UserInfo只有Id,Email和UserName。如果想让客户端共享更多用户信息,可以使用自定义的ClaimsPrincipalFactory。代码如下:publicclassFlowYogaClaimsPrincipalFactory(UserManager<YourCustomUserClass>userMana......
  • WriterSide部署
    打包会在目录下打包出一个zip包,例如为blog.zip上传到服务器安装rzyuminstall-ylrzsz安装unzipyuminstallunzip安装nodejs和npmsudoyuminstallnodejssudoyuminstallnpm安装http-servernpminstallhttp-server-g启动解压压缩包mkdirblogunzipbl......
  • idea
    Ctrl+Alt+O优化导入的类和包Alt+Insert生成代码(如get,set方法,构造函数等)或者右键(Generate)fori/sout/psvm+TabCtrl+Alt+T 生成trycatch 或者Alt+enterCTRL+ALT+T 把选中的代码放在TRY{}IF{}ELSE{}里Ctrl+O重写方法Ctrl+I实现方法Ctr+shift+U大小写转化ALT+......
  • SpiderFlow爬虫平台漏洞利用分析(CVE-2024-0195)
    1.漏洞介绍SpiderFlow爬虫平台项目中spider-flow-web\src\main\java\org\spiderflow\controller\FunctionController.java文件的FunctionService.saveFunction函数调用了saveFunction函数,该调用了自定义函数validScript,该函数中用户能够控制 functionName、parameters 或 sc......
  • 精品IDEA插件推荐:Apipost-Helper
    Apipost-Helper是由Apipost推出的IDEA插件,写完接口可以进行快速调试,且支持搜索接口、根据method跳转接口,还支持生成标准的API文档,注意:这些操作都可以在代码编辑器内独立完成,非常好用!这里给大家介绍一下Apipost-Helper的安装和使用安装在IDEA编辑器插件中心输入Apipost搜索安装:......
  • idea 项目编译内存溢出解决配置
    https://blog.csdn.net/malin970824/article/details/89843478 以下几种方式都可尝试下:1.在idea安装的bin目录修改配置文件 -Xms512m-Xmx2024m-Xss4M-XX:MaxPermSize=2024m 2.修改settings 3.修改tomcat-server-Xms512m-Xmx2024m-Xss4M-XX:PermSize=512M-XX:......
  • android navigationBarDividerColor 无效
    AndroidnavigationBarDividerColor无效问题解析与解决1.问题背景在开发Android应用程序时,我们经常会使用导航栏(NavigationBar)来提供用户导航和操作的功能。导航栏中的分割线(divider)是一种常见的设计元素,用于分隔不同的导航按钮或操作按钮。在Android中,我们可以使用navigationB......