university.alchemy9

Your Goal: Members 你的目标:成员

Create a public constructor which takes an array of address. These addresses, plus the deployer of the function, should all be allowed to create new proposals and vote on those proposals.
创建一个公共的constructor,该函数接受一个address数组作为参数。这些地址以及该函数的部署者都应被允许创建新提案并对这些提案进行投票。
If anyone else attempts to create a proposal or vote, the call should be reverted.
如果有其他人试图创建提案或进行投票,调用应被回滚。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract Voting {
struct Proposal {
address target;
bytes data;
uint yesCount;
uint noCount;
}

Proposal[] public proposals;

// Mapping to track members who can create proposals and vote
mapping(address => bool) public members;

// Constructor to initialize the contract with allowed members
constructor(address[] memory _members) {
// Add the deployer as a member
members[msg.sender] = true;

// Add all addresses from the input array as members
for (uint i = 0; i < _members.length; i++) {
members[_members[i]] = true;
}
}

// Modifier to ensure only members can create proposals and vote
modifier onlyMember() {
require(members[msg.sender], "Not authorized to perform this action");
_;
}

event ProposalCreated(uint proposalId);
event VoteCast(uint proposalId, address voter);
// Mapping to track if an address has voted on a particular proposal
mapping(uint => mapping(address => bool)) public hasVoted;

// Mapping to store the vote (true = yes, false = no) of each address for each proposal
mapping(uint => mapping(address => bool)) public votes;

// Function to create a new proposal
function newProposal(address addr, bytes calldata data) onlyMember external {
Proposal memory newProposalInstance = Proposal(addr, data, 0, 0);
proposals.push(newProposalInstance);
emit ProposalCreated(proposals.length - 1); // proposalId is the index of the newly added proposal
//每当创建新的Proposal结构体时,就触发该事件。
}

// Function to cast or change a vote
function castVote(uint proposalId, bool support) onlyMember external {
// Ensure the proposal exists
require(proposalId < proposals.length, "Proposal does not exist");

// Retrieve the proposal to update its vote counts
Proposal storage targetProposal = proposals[proposalId];

// If the voter has already voted, we need to change their vote
if (hasVoted[proposalId][msg.sender]) {
emit VoteCast(proposalId, msg.sender);
// If the voter previously voted "yes" and now votes "no"
if (votes[proposalId][msg.sender] == true && !support) {
targetProposal.yesCount -= 1; // Decrease yesCount
targetProposal.noCount += 1; // Increase noCount
}
// If the voter previously voted "no" and now votes "yes"
else if (votes[proposalId][msg.sender] == false && support) {
targetProposal.noCount -= 1; // Decrease noCount
targetProposal.yesCount += 1; // Increase yesCount
}
} else {
emit VoteCast(proposalId, msg.sender);
// If the voter has not voted yet, just increase the respective count
if (support) {
targetProposal.yesCount += 1;
} else {
targetProposal.noCount += 1;
}
}

// Update the voter's choice
votes[proposalId][msg.sender] = support;

// Mark that this address has voted for this proposal
hasVoted[proposalId][msg.sender] = true;
}
}

Your Goal: Execute 你的目标:执行

Let’s make our minimum voting threshold be 10 participants. Once 10 members have voted yes on a proposal, execute it.
让我们将最低投票门槛设为10名参与者。一旦有10名成员对某项提案投了赞成票,就执行该提案。

Update the castVote function to execute the proposal when the 10 yes votes have been registered.
更新castVote函数,以便在登记到10张赞成票时执行该提案。
Execute the vote by sending the data to the target address using the call syntax.
使用call语法将data发送到target地址来执行投票。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract Voting {
struct Proposal {
address target;
bytes data;
uint yesCount;
uint noCount;
}

Proposal[] public proposals;

// Constructor to initialize the contract with allowed members
constructor(address[] memory _members) {
// Add the deployer as a member
members[msg.sender] = true;

// Add all addresses from the input array as members
for (uint i = 0; i < _members.length; i++) {
members[_members[i]] = true;
}
}

// Modifier to ensure only members can create proposals and vote
modifier onlyMember() {
require(members[msg.sender], "Not authorized to perform this action");
_;
}
event ProposalExecuted(uint proposalId);
event ProposalCreated(uint proposalId);
event VoteCast(uint proposalId, address voter);
// Mapping to track if an address has voted on a particular proposal
mapping(uint => mapping(address => bool)) public hasVoted;

// Mapping to store the vote (true = yes, false = no) of each address for each proposal
mapping(uint => mapping(address => bool)) public votes;

mapping(uint => uint) public supportingVotes;

// Mapping to track members who can create proposals and vote
mapping(address => bool) public members;

// Function to create a new proposal
function newProposal(address addr, bytes calldata data) onlyMember external {
Proposal memory newProposalInstance = Proposal(addr, data, 0, 0);
proposals.push(newProposalInstance);
emit ProposalCreated(proposals.length - 1); // proposalId is the index of the newly added proposal
//每当创建新的Proposal结构体时,就触发该事件。
}

// Function to cast or change a vote
function castVote(uint proposalId, bool support) onlyMember external {
//number10 = 0
//这个变量从0开始,一旦有10名成员对某项提案投了赞成票,就执行该提案。
// Ensure the proposal exists
require(proposalId < proposals.length, "Proposal does not exist");

// Retrieve the proposal to update its vote counts
Proposal storage targetProposal = proposals[proposalId];

// If the voter has already voted, we need to change their vote
if (hasVoted[proposalId][msg.sender]) {
emit VoteCast(proposalId, msg.sender);
// If the voter previously voted "yes" and now votes "no"
if (votes[proposalId][msg.sender] == true && !support) {
targetProposal.yesCount -= 1; // Decrease yesCount
targetProposal.noCount += 1; // Increase noCount
supportingVotes[proposalId] -= 1; // Decrease supporting votes
//number10 += 1;
//如果投票了,此变量就加一
}
// If the voter previously voted "no" and now votes "yes"
else if (votes[proposalId][msg.sender] == false && support) {
targetProposal.noCount -= 1; // Decrease noCount
targetProposal.yesCount += 1; // Increase yesCount
supportingVotes[proposalId] += 1; // Increase supporting votes
//number10 += 1;
//如果投票了,此变量就加一
}
} else {
emit VoteCast(proposalId, msg.sender);
// If the voter has not voted yet, just increase the respective count
if (support) {
targetProposal.yesCount += 1;
supportingVotes[proposalId] += 1; // Increase supporting votes
} else {
targetProposal.noCount += 1;
}
}

// Update the voter's choice
votes[proposalId][msg.sender] = support;

// Mark that this address has voted for this proposal
hasVoted[proposalId][msg.sender] = true;

// Check if the proposal has enough supporting votes to be executed
if (supportingVotes[proposalId] >= 10) {
executeProposal(proposalId);
}
emit VoteCast(proposalId, msg.sender);

}
// Function to execute a proposal once it has enough supporting votes
function executeProposal(uint proposalId) internal {
// Ensure the proposal exists
require(proposalId < proposals.length, "Proposal does not exist");

Proposal storage targetProposal = proposals[proposalId];

// Execute the proposal by calling the target address with the proposal's data
(bool success, ) = targetProposal.target.call(targetProposal.data);

// If the call is successful, emit the execution event
require(success, "Proposal execution failed");

emit ProposalExecuted(proposalId);
}
}

继承

child合约可以继承所有的函数和状态变量,除了private

使用override关键字,可以覆盖一些方法

现在它有一个constructor,该函数接受一个health参数。请问constructor是放在contract外面还是{里面}

在 Solidity 中,constructor(构造函数)必须放在 contract { } 内部,它是合约的 “初始化方法”,属于合约的核心成员,不能定义在合约外部(外部定义会直接编译报错)。

Your Goal: Specific Health 你的目标:特定生命值

You’ll notice that the Hero.sol file has changed on this stage! Now it has a constructor which takes a health argument.
你会注意到本阶段的Hero.sol文件发生了变化!现在它有一个constructor,该函数接受一个health参数。

Let’s modify our SuperHeroes so that Warrior has an initial health of 200 while the Mage has an initial health of 50.
让我们修改我们的超级英雄,使Warrior的初始生命值为200,而Mage的初始生命值为50。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import "./Hero.sol";


// TODO: create Mage/Warrior Heroes
contract Mage is Hero(50) {
//uint Mage = 50;

}

contract Warrior is Hero(200) {
//uint Warrior = 200;
}

这是上层合约

1
2
3
4
5
6
7
8
9
10
11
12
13
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

contract Hero {
uint public health;
constructor(uint _health) {
health = _health;
}

function takeDamage(uint damage) public {
health -= damage;
}
}

Your Goal: SuperHero Attacks 你的目标:超级英雄攻击

You’ll notice the Hero.sol tab has changed once again! This time there’s three important things to notice:
你会注意到Hero.sol标签又一次发生了变化!这次有三件重要的事情需要注意:

The Hero contract is an abstract contract. It has a virtual function called attack which we’ll need to override in both Warrior and Mage.
Hero合约是一个抽象合约。它有一个名为attack的virtual函数,我们需要在战士和法师中都重写这个函数。
An enum called AttackTypes has been added to the Hero contract to differentiate between the different types of attacks our heroes can do.
Hero合约中添加了一个名为AttackTypes的enum,用于区分我们的英雄可以执行的不同攻击类型。
There’s a contract called Enemy which has a method called takeAttack on it.
有一个名为Enemy的合约,它上面有一个名为takeAttack的方法。
Your job is to implement the attack function on the Warrior and Mage contracts:
你的任务是在Warrior(战士)和Mage(法师)合约上实现attack(攻击)函数:

Add an override function called attack to both the Warrior and Mage contracts. This function should take an Enemy parameter which will be an Enemy contract.
向Warrior和Mage合约中添加一个名为attack的override函数。该函数应接受一个Enemy参数,该参数将是一个Enemy合约。
Invoke takeAttack function on the Enemy contract and change the parameter based on the hero:
调用Enemy合约上的takeAttack函数,并根据英雄修改参数:
For the Warrior, invoke the enemy’s takeAttack with the Brawl attack type.
对于战士,使用Brawl攻击类型调用敌人的takeAttack。
For the Mage, invoke the enemy’s takeAttack with the Spell attack type.
对于法师,使用“法术”攻击类型调用敌人的takeAttack。

答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import "./Hero.sol";
import "./Enemy.sol";


// TODO: create Mage/Warrior Heroes
contract Mage is Hero(50) {
//uint Mage = 50;
function attack(Enemy enemy) public override {
//向Warrior和Mage合约中添加一个名为attack的override函数。
//function attackHero() external {
// 调用 Hero 合约中的 takeAttack 函数
enemy.takeAttack(Hero.AttackTypes.Spell); // 使用 Spell 类型进行攻击
}

}

contract Warrior is Hero(200) {
//uint Warrior = 200;
function attack(Enemy enemy) public override {
//向Warrior和Mage合约中添加一个名为attack的override函数。
enemy.takeAttack(Hero.AttackTypes.Brawl); // 使用 Spell 类型进行攻击
}
}

abstract contract什么是抽象合约?

在 Solidity 中,抽象合约abstract contract)是一种不能被直接部署的合约,它通常用于定义一些共享的功能或接口,供其他合约继承和实现。

1. 定义抽象合约

抽象合约本质上是包含未实现的函数的合约。一个抽象合约可以包含已实现的函数,也可以包含未实现的函数。抽象合约无法直接部署在区块链上,因为它缺少某些实现,通常需要通过继承的方式被子合约继承并完成未实现的部分。

2. 为什么需要抽象合约

  • 代码重用:抽象合约可以定义一些通用的功能或接口,子合约可以继承这些功能并实现自己的业务逻辑。
  • 接口定义:可以将一些公共函数声明为抽象函数,要求继承者提供具体实现,类似于接口(interface)。
  • 限制性设计:通过抽象合约,可以强制要求继承者实现某些方法,确保子合约的行为一致性。

3. 抽象合约的语法

在 Solidity 中,abstract 关键字用于声明抽象合约。例如:

1
2
3
4
5
6
7
8
9
10
11
12
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

abstract contract AbstractVoting {
// 一个抽象函数(未实现的函数)
function vote(uint proposalId, bool support) external virtual;

// 一个已实现的函数
function getVoteCount(uint proposalId) public view returns (uint) {
return 0; // 示例返回值
}
}

4. 抽象合约的关键点

  • 抽象函数:在抽象合约中,函数可以被声明为 virtual 且没有实现(没有函数体),这类函数被称为“抽象函数”。继承这个抽象合约的子合约必须提供对这些函数的具体实现。

    1
    function vote(uint proposalId, bool support) external virtual;
  • 已实现的函数:抽象合约中也可以包含已实现的函数,子合约可以直接继承这些已实现的功能,无需重写。

    1
    2
    3
    function getVoteCount(uint proposalId) public view returns (uint) {
    return 0; // 示例返回值
    }
  • 无法直接部署:抽象合约不能被直接部署到区块链上,因为它包含了未实现的抽象函数。只有继承了这个抽象合约并实现了所有抽象函数的合约才能被部署。

5. 如何使用抽象合约

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

// 定义一个抽象合约
abstract contract AbstractVoting {
// 抽象函数,子合约必须实现
function vote(uint proposalId, bool support) public virtual;

// 已实现的函数,子合约可以继承
function getProposalCount() public pure returns (uint) {
return 5; // 示例返回
}
}

// 继承抽象合约并实现抽象函数
contract Voting is AbstractVoting {
// 存储提案的计数器
uint public proposalCount = 0;

// 实现抽象合约中的 vote 函数
function vote(uint proposalId, bool support) public override {
// 投票逻辑
if (support) {
proposalCount++;
}
}
}

解释:

  1. AbstractVoting 合约

    • 定义了一个抽象函数 vote(uint proposalId, bool support),子合约必须实现这个函数。
    • 定义了一个已实现的函数 getProposalCount(),返回一个示例值。这个函数在继承的合约中可以直接使用,无需重写。
  2. Voting 合约

    • 继承了 AbstractVoting 抽象合约。
    • 实现了抽象函数 vote(uint proposalId, bool support),这是强制要求的,因为 AbstractVoting 中的 vote 函数是抽象的,没有实现。
    • 使用 override 关键字来表示重写了父合约中的抽象函数。

6. 抽象合约 vs. 接口

抽象合约和接口有一些相似之处,但它们之间有几个关键区别:

  • 抽象合约:可以包含已实现的函数和抽象函数,子合约可以继承并重写这些函数。
  • 接口(Interface):只能包含未实现的函数,没有状态变量和已实现的函数,所有函数都必须是抽象的。子合约必须实现接口中声明的所有函数。

7. 总结

  • 抽象合约是一个不能直接部署的合约,它用于提供基础功能和接口,供其他合约继承和实现。
  • 它包含抽象函数(没有实现),以及可以实现的函数。
  • 子合约必须实现抽象合约中声明的所有抽象函数。

super.modify是什么意思?

在 Solidity 中,super.modify() 是用于调用父合约(即 Base 合约)中已实现的方法。它允许在子合约中调用父合约的函数,尤其是在函数被重写(override)的情况下。

解释 super.modify() 的含义:

在你提供的代码中,Derived 合约重写了 Base 合约中的 modify 函数,并在其内部调用了 super.modify()

代码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
contract Base {
uint public value = 10;

function modify() virtual external {
value *= 2; // 将 value 的值乘以 2
}
}

contract Derived is Base {
function modify() virtual override external {
value += 20; // 将 value 的值增加 20
super.modify(); // 调用父合约 Base 的 modify() 函数
}
}

super.modify() 的作用:

  • super 是 Solidity 中的一个关键字,用于引用父合约的函数。它允许你调用父合约中被子合约重写的函数。
  • Derived 合约中,super.modify() 调用的是 Base 合约中的 modify() 函数,而不是 Derived 合约中的 modify() 函数。

具体步骤:

  1. Derived 合约中的 modify()

    • value += 20;:首先,value 被增加了 20,value 从 10 变为 30。
    • 然后,调用 super.modify()
  2. super.modify()

    • 这会调用父合约 Base 中的 modify() 函数。
    • Base 合约中,modify() 函数是将 value 乘以 2。所以,此时 value 将从 30 变为 60。
  3. 总结

    • super.modify() 使得 Derived 合约在执行完自己定义的 modify() 函数后,能够调用父合约 Base 中的 modify() 函数,从而实现修改 value 的值。

举个例子,详细说明:

  1. 初始化Base 合约中的 value 初始值为 10。

  2. 调用 Derived 合约的 modify() 函数

    • value += 20;value 的值增加 20,变成 30。
    • 然后,super.modify() 调用父合约的 modify(),即 Base.modify(),这会将 value 乘以 2,变成 60。

因此,在 Derived 合约中的 modify() 执行完后,value 的最终值是 60。

总结:

  • super.modify() 调用父合约中的 modify() 函数。
  • 你可以通过 super 来调用父合约的函数,尤其在子合约中重写父合约的函数时,它非常有用,可以确保在子合约中执行父合约的功能。

Your Goal: Add the Super! 你的目标:添加Super!

The attack function is now implemented by the Hero base contract. It will take the enemy as an argument and decrement energy from our hero after every attack.
attack函数现在由Hero基合约实现。它将接收enemy作为参数,并在每次攻击后减少我们英雄的energy。

Let’s invoke this base contract function from within the attack function for both (derived) hero contracts: Mage and Warrior.
让我们从两个(派生的)英雄合约(法师和战士)的attack函数中调用这个基础合约函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import "./Hero.sol";
import "./Enemy.sol";


// TODO: create Mage/Warrior Heroes
contract Mage is Hero(50) {
//uint Mage = 50;
function attack(Enemy enemy) public override {
//向Warrior和Mage合约中添加一个名为attack的override函数。
//function attackHero() external {
// 调用 Hero 合约中的 takeAttack 函数
enemy.takeAttack(Hero.AttackTypes.Spell);
super.attack(enemy); //加上这一行,即可调用上层合约
}

}

contract Warrior is Hero(200) {
//uint Warrior = 200;
function attack(Enemy enemy) public override {
//向Warrior和Mage合约中添加一个名为attack的override函数。
enemy.takeAttack(Hero.AttackTypes.Brawl);
super.attack(enemy); //加上这一行,即可调用上层合约
}
}

Your goal is to fill out the Ownable base contract, which will be used by the Collectible contract!

你的目标是填写Ownable基础合约,它将被Collectible合约使用!
The owner should be defined in the Ownable base contract
owner 应在 Ownable 基础合约中定义
Ensure that markPrice can only be called by the owner (the deployer of the collectible)
确保只有owner(收藏品的部署者)可以调用markPrice

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

contract Ownable {
address public owner; // 声明 owner 变量,类型为 address
constructor() {
owner = msg.sender;
}
//owner 应在 Ownable 基础合约中定义
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
//确保只有owner(收藏品的部署者)可以调用markPrice
_;
}

}

Your Goal: Collectible Transferable 你的目标:可收集的 可转移的

The Collectible contract now also inherits from Transferable, a contract which has not been created yet!
现在,Collectible合约还继承自Transferable合约,而后者尚未创建!

Your goal is to create a new contract Transferable that will allow the Collectible to transfer its ownership to another address.
你的目标是创建一个新的合约Transferable,它将允许Collectible将其所有权转移到另一个地址。
In the Transferable contract, create a function called transfer which takes an address for the new owner.
在Transferable合约中,创建一个名为transfer的函数,该函数接收新所有者的address作为参数。
Have this function transfer ownership from the current owner to the new owner passed in.
让此函数将所有权从当前所有者转移到传入的新所有者。
Ensure that this function can only be called by the current owner.
确保此函数只能由当前所有者调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

contract Ownable {
address public owner; // 声明 owner 变量,类型为 address
constructor() {
owner = msg.sender;
}
//owner 应在 Ownable 基础合约中定义
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
//确保只有owner(收藏品的部署者)可以调用markPrice
_;
}
}

contract Transferable is Ownable{
//你的目标是创建一个新的合约Transferable,它将允许Collectible将其所有权转移到另一个地址。
function transfer(address newOwner) external onlyOwner {
//在Transferable合约中,创建一个名为transfer的函数,该函数接收新所有者的address作为参数。
//让此函数将所有权从当前所有者转移到传入的新所有者。
require(newOwner != address(0), "New owner cannot be the zero address");
owner = newOwner;
}
}

课程完结