university.alchemy6
Your Goal: Remove Members 你的目标:移除成员
Create a constructor which will add the deployer address as the first member of the stack club.
创建一个构造函数,该函数将把部署者地址添加为堆栈俱乐部的第一个成员。
Create a removeLastMember function which will remove the last member added to the club.
创建一个removeLastMember函数,该函数将移除最后加入俱乐部的成员。
Function Security 函数安全性
Ensure that the removeLastMember function can only be called by an existing member
确保removeLastMember函数只能由现有成员调用
Ensure that addMember can only be called by an existing member
确保addMember只能由现有成员调用
答案:
1 | |
Your Goal: Create a Vote
Create a new struct called Vote that contains two properties: a Choices choice and an address voter.
Then create a public storage variable called vote which is of the Vote type.
Finally, in the createVote function create a new instance of Vote and store it in the storage variable vote. Use the choice passed in as an argument and the msg.sender for the vote properties.
答案:
1 | |
Your Goal: Return Memory Vote 你的目标:返回内存投票
Create an external, view function called createVote which takes Choices value as a parameter and returns an instance of type Vote.
创建一个名为createVote的外部视图函数,该函数接受Choices值作为参数,并返回一个Vote类型的实例。
This function should use the Choices value and the msg.sender as the values to create the vote.
此函数应使用Choices值和msg.sender作为创建投票的值。
知识点
当你返回结构体或其他引用类型(如数组)时,必须使用数据位置关键字来明确数据的来源:storage、memory或calldata。
然而,对于external和public方法,不能对返回参数使用storage关键字。storage关键字会提供对合约自身持久化存储中某个位置的直接引用,而这一位置无法在合约外部访问。应改用memory或calldata来返回数据的副本。
正确答案
1 | |
Your Goal: Vote Array 你的目标:投票数组
Create a public storage array of the Vote struct called votes.
创建一个名为votes的Vote结构体的公共存储数组。
In the createVote function use the choice parameter and the msg.sender to create a new vote and push it onto the array of votes.
在createVote函数中,使用choice参数和msg.sender创建一个新投票,并将其添加到投票数组中。
1 | |
-1 的原因是「数组索引从 0 开始,length 是元素总数,最后一个元素的索引 = 长度 - 1」,只是补充 Solidity 特有的细节说明:
一、Solidity 中数组的核心规则(关键)
Solidity 数组的索引同样从 0 开始,length 属性返回元素的个数(uint 类型),因此:
长度为 n 的数组,合法索引范围是 0 ~ n-1;
直接访问 votes[votes.length] 会触发数组越界(Solidity 中越界访问会直接 revert 交易,而非返回 undefined);
votes[votes.length - 1] 是获取最后一个元素的唯一安全方式。
Your Goal: Find Vote 你的目标:找到投票
Create an external, view function hasVoted which takes an address and returns a bool indicating if the address has cast a vote or not.
创建一个外部的视图函数hasVoted,它接收一个地址</b1并返回一个布尔值</b2,用于指示该地址是否已投票。
Create an external, view function findChoice which takes an address and returns a Choices value indicating the choice on the vote cast by the address. For this function there is no need to worry about the case where a vote was not cast.
创建一个外部的视图函数findChoice,它接收一个地址</b1并返回一个Choices值,该值表示该地址所投的选票选项。对于这个函数,无需考虑未投票的情况。
答案:
1 | |
Your Goal: Vote Once 你的目标:投票一次
Each address should only be allowed to call createVote once.
每个地址应只允许调用一次createVote。
If they try again, the call should be reverted.
如果他们再次尝试,该调用应被回滚。
答案:
1 | |
在solidity里,function可以没有returns吗?
在 Solidity 中,function 完全可以没有 returns,这是合法且常用的写法。核心规则与其他语言一致,但要结合 Solidity 的特性(如状态修改、可见性、返回值声明要求)具体说明:
一、核心规则:返回值由 returns 声明决定
Solidity 函数是否需要返回值,完全取决于是否显式声明了 returns(注意:Solidity 中 returns 是“返回值声明关键字”,而非语句——返回值通过 return 语句触发,而是否需要返回值由函数定义时的 returns(类型) 决定):
- 若函数未声明
returns→ 无需返回任何值,也不能用return 表达式(但可单独写return;用于提前退出函数); - 若函数声明了
returns(类型)→ 必须通过return 表达式;返回对应类型的值(否则编译报错)。
二、无 returns 函数的常见场景(附示例)
Solidity 中无返回值的函数,通常用于“修改合约状态”或“执行动作”(而非“计算并返回结果”),例如转账、更新状态变量、触发事件等。
1. 基础示例:修改状态变量的函数
1 | |
2. 场景扩展:转账/触发事件的函数
无返回值函数最典型的用途是 transfer 转账或触发事件(这类操作不需要返回结果,仅需执行动作):
1 | |
3. 特殊情况:pure/view 函数也可无 returns
pure(不读不写状态)、view(只读状态)函数通常用于返回计算结果,但也可以无 returns(仅执行无副作用的逻辑,如校验、日志输出):
1 | |
三、与“有 returns”函数的对比(避免踩坑)
| 函数类型 | 示例代码 | 说明 |
|---|---|---|
无 returns(无返回值) |
function doSomething() public { ... } |
无需 return,仅执行动作 |
有 returns(有返回值) |
function getCount() public view returns(uint256) { return count; } |
必须 return 对应类型的值,否则编译报错 |
有 returns(多返回值) |
function getMinMax(uint256 a, uint256 b) public pure returns(uint256, uint256) { return (a<b?a:b, a>b?a:b); } |
需返回所有声明的类型,顺序对应 |
四、关键注意点
return语句的两种用法:- 无
returns函数:可写return;(仅用于提前退出,不能带表达式,如return 1;会编译报错); - 有
returns函数:必须写return 表达式;(表达式类型/数量需与returns声明一致)。
- 无
可见性不影响返回值规则:
- 无论函数是
public/external/internal/private,是否需要returns仅由“是否声明返回类型”决定。
- 无论函数是
Solidity 0.8.x 兼容性:
- 从 0.4.x 到 0.8.x,无
returns函数的规则完全一致,无版本兼容问题。
- 从 0.4.x 到 0.8.x,无
总结
在 Solidity 中:
- **允许函数没有
returns**:这类函数专注于“执行动作”(修改状态、转账、触发事件、校验等),无需返回值,是合约开发的基础用法; - 只有当函数需要“返回计算结果”时,才需要声明
returns(类型)并通过return语句返回对应值。
简单说:Solidity 函数的核心是“是否需要产出结果”——不产出结果就不用写 returns,仅执行动作即可,这是合约开发中最常见的函数类型之一(如转账函数、状态更新函数)。
Your Goal: Change Vote 你的目标:更改投票
Create an external function called changeVote which takes a Choices argument and changes the choice on the existing vote for the msg.sender.
创建一个名为changeVote的外部函数,该函数接受一个Choices参数,并更改msg.sender现有投票的选项。
If they do not have an existing vote, revert the call.
如果他们没有现有的投票,则回滚调用。
答案:
1 | |
Your Goal: Members Mapping 你的目标:成员映射
Create a public mapping called members which maps an address to a bool. The bool will indicate whether or not the address is currently a member!
创建一个名为members的公共映射,它将address映射到bool。bool将指示该地址当前是否为成员!
Create an external function called addMember which takes an address and adds it as a member. You can do this by storing true in the data location corresponding to the address in the members mapping.
创建一个名为addMember的外部函数,该函数接收一个地址</b1并将其添加为成员。你可以通过在members映射中该地址对应的数据源位置存储true来实现这一点。
mappings 是storage,不能传递变量,是key/value 键值对
1 | |
我学solidity开发需要刷算法题吗
学习 Solidity 开发是否需要刷算法题,核心取决于你的职业目标——算法题不是 Solidity 开发的「必修课」,但却是「加分项」,甚至是某些方向的「必备项」,以下分场景拆解:
一、先明确:不同 Solidity 方向对算法的需求差异
| 开发方向 | 算法需求程度 | 核心原因 |
|---|---|---|
| 普通 DApp 开发(如NFT、简单DeFi、链上工具) | 低 | 核心工作是调用合约、处理链下逻辑、对接前端,仅需基础逻辑(循环、条件判断) |
| 核心 DeFi/金融合约(如AMM、借贷、衍生品) | 中-高 | 涉及数学计算(如恒定乘积、利率模型)、状态优化、边界条件处理 |
| 链上游戏/元宇宙 | 中 | 需处理游戏规则、随机数、状态同步,部分场景需简单算法(如路径、排序) |
| 合约优化/安全审计 | 中-高 | 审计需识别逻辑漏洞(如越界、下溢),优化需用算法降低 Gas 消耗 |
| ZK/Layer2/核心协议开发 | 高 | 涉及密码学、零知识证明、数据结构(如Merkle树)、高效排序/查找 |
二、刷算法题的「核心价值」(针对 Solidity 开发)
刷算法题的本质不是「背题」,而是训练逻辑思维、边界条件处理、代码严谨性——这些能力对 Solidity 至关重要(因为链上代码不可逆,一个小漏洞可能导致数百万资产损失),具体价值:
提升逻辑严谨性:
Solidity 对「越界、下溢、空值、循环终止条件」的要求极高(0.8+ 虽有自动检查,但逻辑漏洞仍需人工规避),而算法题的「边界用例分析」(如空数组、极值、异常输入)能直接训练这种能力。
比如:刷「数组越界处理」「整数溢出防护」类算法题,能让你写votes[votes.length - 1]时自然想到先判断数组非空。掌握链上高效数据处理:
以太坊 Gas 费与代码执行步数强相关,算法能力决定你能否用「更优逻辑」降低消耗:- 比如:遍历数组时用「提前终止循环」替代全量遍历;
- 比如:用「映射(Mapping)+ 数组」替代纯数组查找(O(1) 替代 O(n));
- 这些都是算法中「时间/空间复杂度」的基础思维。
应对核心领域的数学/算法需求:
做 DeFi 必须懂「恒定乘积公式(x*y=k)」「无常损失计算」「复利模型」,这些本质是数学+算法;
做 ZK 需理解「Merkle 树构建/验证」「哈希计算优化」,做 Layer2 需懂「批量交易排序/打包」——这些都需要算法思维支撑。应对大厂/核心团队面试:
如果你想进头部 Web3 团队(如 Uniswap、Aave、以太坊基金会),算法题是面试标配:- 基础题:数组/字符串处理、简单排序/查找、数学计算(如质数判断、进制转换);
- 进阶题:链上场景题(如「如何高效计算 NFT 持有者的平均持仓」「如何优化 AMM 的价格计算 Gas」)。
三、如果刷,该怎么刷?(针对性路线)
不用像前端/后端那样刷海量算法,聚焦「Solidity 高频场景」即可,分 3 个层级:
1. 基础层(必刷,所有方向都需要)
- 核心考点:边界条件处理、整数运算、简单循环/条件
- 推荐题(LeetCode 简单题):
- 整数相关:7. 整数反转、9. 回文数(训练 Solidity 整数溢出/下溢防护);
- 数组相关:26. 删除有序数组中的重复项、88. 合并两个有序数组(训练链上数组操作);
- 逻辑判断:13. 罗马数字转整数、20. 有效的括号(训练条件判断严谨性)。
- 核心目的:养成「先考虑异常情况」的习惯(比如数组为空、数值为 0/极值)。
2. 进阶层(DeFi/游戏/优化方向推荐)
- 核心考点:数学计算、时间/空间复杂度优化、哈希/加密基础
- 推荐题(LeetCode 中等题 + 链上场景题):
- 数学计算:69. x 的平方根、50. Pow(x, n)(对应 DeFi 利率/手续费计算);
- 哈希/映射:1. 两数之和(训练 Solidity Mapping 替代数组查找);
- 链上场景题:
- 「如何高效统计一个合约的所有交互地址」(优化遍历逻辑);
- 「如何实现链上随机数且避免被操纵」(结合算法+密码学)。
- 核心目的:理解「Gas 优化 = 算法优化」,用更高效的逻辑减少链上执行成本。
3. 高阶层(ZK/Layer2/核心协议方向)
- 核心考点:密码学算法、数据结构(树/图)、高效排序/查找
- 推荐内容:
- 算法:快速排序、二分查找、Merkle 树构建/验证、布隆过滤器;
- 数学:有限域运算、椭圆曲线加密(ECC)基础、零知识证明(ZK-SNARKs)核心逻辑;
- 参考题:LeetCode 树类题(104. 二叉树的最大深度)、图类题(200. 岛屿数量)。
四、如果不刷算法题,该补什么?
如果你的目标只是「普通 DApp 开发」,可以不刷算法题,但必须补以下核心能力:
- Solidity 语法细节(如 storage/memory 区别、函数可见性、事件/修饰器);
- 链上安全规则(如重入攻击、权限控制、整数溢出/下溢);
- 主流合约范式(如 ERC20/ERC721/ERC1155、代理合约、多签钱包);
- 工具使用(Hardhat/Truffle、Remix、Etherscan 调试、Tenderly 分析)。
最终结论
- 「纯应用层 DApp 开发」:不用专门刷算法题,掌握基础逻辑即可;
- 「DeFi/审计/优化/核心协议开发」:必须刷算法题(至少基础+进阶),算法是核心竞争力;
- 「想进头部 Web3 团队」:算法题是面试门槛,越早刷越有利。
简单说:算法题不是 Solidity 入门的「拦路虎」,但却是进阶的「垫脚石」——哪怕每天只刷 1 道简单题,也能显著提升你写合约的严谨性,减少链上漏洞。