
WTF Solidity 合约安全: S08. 绕过合约检查
我最近在重新学solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。 推特:@0xAA_Science|@WTFAcademy_ 社区:Discord|微信群|官网 wtf.academy 所有代码和教程开源在github: github.com/AmazingAng/WTFSolidity这一讲,我们将介绍绕过合约长度检查,并介绍预防的方法。绕过合约检查很多 freemint 的项目为了限制科学家(程序员)会用到 isContract() 方法,希望将调用者 msg.sender 限制为外部账户(EOA),而非合约。这个函数利用 extcodesize 获取该地址所存储的 bytecode 长度(runtime),若大于0,则判断为合约,否则就是EOA(用户)。 // 利用 extcodesize 检查是否为合约 function isContract(address account) public view returns (bool) { // extcodesize > 0 的地址一定是合约...

WTF Solidity 合约安全: S09. 拒绝服务
我最近在重新学solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。 推特:@0xAA_Science|@WTFAcademy_ 社区:Discord|微信群|官网 wtf.academy 所有代码和教程开源在github: github.com/AmazingAng/WTFSolidity这一讲,我们将介绍智能合约的拒绝服务(Denial of Service, DoS)漏洞,并介绍预防的方法。NFT项目 Akutar 曾因为 DoS 漏洞损失 11,539 ETH,当时价值 3400 万美元。DoS在 Web2 中,拒绝服务攻击(DoS)是指通过向服务器发送大量垃圾信息或干扰信息的方式,导致服务器无法向正常用户提供服务的现象。而在 Web3,它指的是利用漏洞使得智能合约无法正常提供服务。 在2022年4月,一个很火的 NFT 项目名为 Akutar,他们使用荷兰拍卖进行公开发行,筹集了 11,539.5 ETH,非常成功。之前持有他们社区Pass的参与者会得到 0.5 ETH的退款,但是他们处理...

WTF Solidity 合约安全 S06. 签名重放
我最近在重新学solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。 推特:@0xAA_Science|@WTFAcademy_ 社区:Discord|微信群|官网 wtf.academy 所有代码和教程开源在github: github.com/AmazingAng/WTFSolidity这一讲,我们将介绍智能合约的签名重放(Signature Replay)攻击和预防方法,它曾间接导致了著名做市商 Wintermute 被盗2000万枚 $OP。签名重放上学的时候,老师经常会让家长签字,有时候家长很忙,我就会很“贴心”照着以前的签字抄一遍。某种意义上来说,这就是签名重放。 在区块链中,数字签名可以用于识别数据签名者和验证数据完整性。发送交易时,用户使用私钥签名交易,使得其他人可以验证交易是由相应账户发出的。智能合约也能利用 ECDSA 算法验证用户将在链下创建的签名,然后执行铸造或转账等逻辑。更多关于数字签名的介绍请见WTF Solidity第37讲:数字签名。 数字签名一般有两种常见的重放攻击...
WTF Academy: wtf.academy



WTF Solidity 合约安全: S08. 绕过合约检查
我最近在重新学solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。 推特:@0xAA_Science|@WTFAcademy_ 社区:Discord|微信群|官网 wtf.academy 所有代码和教程开源在github: github.com/AmazingAng/WTFSolidity这一讲,我们将介绍绕过合约长度检查,并介绍预防的方法。绕过合约检查很多 freemint 的项目为了限制科学家(程序员)会用到 isContract() 方法,希望将调用者 msg.sender 限制为外部账户(EOA),而非合约。这个函数利用 extcodesize 获取该地址所存储的 bytecode 长度(runtime),若大于0,则判断为合约,否则就是EOA(用户)。 // 利用 extcodesize 检查是否为合约 function isContract(address account) public view returns (bool) { // extcodesize > 0 的地址一定是合约...

WTF Solidity 合约安全: S09. 拒绝服务
我最近在重新学solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。 推特:@0xAA_Science|@WTFAcademy_ 社区:Discord|微信群|官网 wtf.academy 所有代码和教程开源在github: github.com/AmazingAng/WTFSolidity这一讲,我们将介绍智能合约的拒绝服务(Denial of Service, DoS)漏洞,并介绍预防的方法。NFT项目 Akutar 曾因为 DoS 漏洞损失 11,539 ETH,当时价值 3400 万美元。DoS在 Web2 中,拒绝服务攻击(DoS)是指通过向服务器发送大量垃圾信息或干扰信息的方式,导致服务器无法向正常用户提供服务的现象。而在 Web3,它指的是利用漏洞使得智能合约无法正常提供服务。 在2022年4月,一个很火的 NFT 项目名为 Akutar,他们使用荷兰拍卖进行公开发行,筹集了 11,539.5 ETH,非常成功。之前持有他们社区Pass的参与者会得到 0.5 ETH的退款,但是他们处理...

WTF Solidity 合约安全 S06. 签名重放
我最近在重新学solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。 推特:@0xAA_Science|@WTFAcademy_ 社区:Discord|微信群|官网 wtf.academy 所有代码和教程开源在github: github.com/AmazingAng/WTFSolidity这一讲,我们将介绍智能合约的签名重放(Signature Replay)攻击和预防方法,它曾间接导致了著名做市商 Wintermute 被盗2000万枚 $OP。签名重放上学的时候,老师经常会让家长签字,有时候家长很忙,我就会很“贴心”照着以前的签字抄一遍。某种意义上来说,这就是签名重放。 在区块链中,数字签名可以用于识别数据签名者和验证数据完整性。发送交易时,用户使用私钥签名交易,使得其他人可以验证交易是由相应账户发出的。智能合约也能利用 ECDSA 算法验证用户将在链下创建的签名,然后执行铸造或转账等逻辑。更多关于数字签名的介绍请见WTF Solidity第37讲:数字签名。 数字签名一般有两种常见的重放攻击...
WTF Academy: wtf.academy
Share Dialog
Share Dialog

Subscribe to 0xAA

Subscribe to 0xAA
我最近在重新学solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。
推特:@WTFAcademy_ |@0xAA_Science
WTF Academy社群: 官网 wtf.academy | discord | 微信群申请
所有代码和教程开源在github: github.com/AmazingAng/WTFSolidity
Opensea是以太坊上最大的NFT交易平台,总交易总量达到了$300亿。Opensea在交易中抽成2.5%,因此它通过用户交易至少获利了$7.5亿。另外,它的运作并不去中心化,且不准备发币补偿用户。NFT玩家苦Opensea久已,今天我们就利用智能合约搭建一个零手续费的去中心化NFT交易所:NFTSwap。
卖家:出售NFT的一方,可以挂单list、撤单revoke、修改价格update。
买家:购买NFT的一方,可以购买purchase。
订单:卖家发布的NFT链上订。,一个系列的同一tokenId最多存在一个订单,其中包含挂单价格price和持有人owner信息。当一个订单交易完成或被撤单后,其中信息清零。
合约包含4个事件,对应挂单list、撤单revoke、修改价格update、购买purchase这四个行为:
event List(address indexed seller, address indexed nftAddr, uint256 indexed tokenId, uint256 price);
event Purchase(address indexed buyer, address indexed nftAddr, uint256 indexed tokenId, uint256 price);
event Revoke(address indexed seller, address indexed nftAddr, uint256 indexed tokenId);
event Update(address indexed seller, address indexed nftAddr, uint256 indexed tokenId, uint256 newPrice);
NFT订单抽象为Order结构体,包含挂单价格price和持有人owner信息。nftList映射记录了订单是对应的NFT系列(合约地址)和tokenId信息。
// 定义order结构体
struct Order{
address owner;
uint256 price;
}
// NFT Order映射
mapping(address => mapping(uint256 => Order)) public nftList;
在NFTSwap中,用户使用ETH购买NFT。因此,合约需要实现fallback()函数来接收ETH。
fallback() external payable{}
ERC721的安全转账函数会检查接收合约是否实现了onERC721Received()函数,并返回正确的选择器selector。用户下单之后,需要将NFT发送给NFTSwap合约。因此NFTSwap继承IERC721Receiver接口,并实现onERC721Received()函数:
contract NFTSwap is IERC721Receiver{
// 实现{IERC721Receiver}的onERC721Received,能够接收ERC721代币
function onERC721Received(
address operator,
address from,
uint tokenId,
bytes calldata data
) external override returns (bytes4){
return IERC721Receiver.onERC721Received.selector;
}
合约实现了4个交易相关的函数:
挂单list():卖家创建NFT并创建订单,并释放List事件。参数为NFT合约地址_nftAddr,NFT对应的_tokenId,挂单价格_price(注意:单位是wei)。成功后,NFT会从卖家转到NFTSwap合约中。
// 挂单: 卖家上架NFT,合约地址为_nftAddr,tokenId为_tokenId,价格_price为以太坊(单位是wei)
function list(address _nftAddr, uint256 _tokenId, uint256 _price) public{
IERC721 _nft = IERC721(_nftAddr); // 声明IERC721接口合约变量
require(_nft.getApproved(_tokenId) == address(this), "Need Approval"); // 合约得到授权
require(_price > 0); // 价格大于0
Order storage _order = nftList[_nftAddr][_tokenId]; //设置NF持有人和价格
_order.owner = msg.sender;
_order.price = _price;
// 将NFT转账到合约
_nft.safeTransferFrom(msg.sender, address(this), _tokenId);
// 释放List事件
emit List(msg.sender, _nftAddr, _tokenId, _price);
}
撤单revoke():卖家撤回挂单,并释放Revoke事件。参数为NFT合约地址_nftAddr,NFT对应的_tokenId。成功后,NFT会从NFTSwap合约转回卖家。
// 撤单: 卖家取消挂单
function revoke(address _nftAddr, uint256 _tokenId) public {
Order storage _order = nftList[_nftAddr][_tokenId]; // 取得Order
require(_order.owner == msg.sender, "Not Owner"); // 必须由持有人发起
// 声明IERC721接口合约变量
IERC721 _nft = IERC721(_nftAddr);
require(_nft.ownerOf(_tokenId) == address(this), "Invalid Order"); // NFT在合约中
// 将NFT转给卖家
_nft.safeTransferFrom(address(this), msg.sender, _tokenId);
delete nftList[_nftAddr][_tokenId]; // 删除order
// 释放Revoke事件
emit Revoke(msg.sender, _nftAddr, _tokenId);
}
修改价格update():卖家修改NFT订单价格,并释放Update事件。参数为NFT合约地址_nftAddr,NFT对应的_tokenId,更新后的挂单价格_newPrice(注意:单位是wei)。
// 调整价格: 卖家调整挂单价格
function update(address _nftAddr, uint256 _tokenId, uint256 _newPrice) public {
require(_newPrice > 0, "Invalid Price"); // NFT价格大于0
Order storage _order = nftList[_nftAddr][_tokenId]; // 取得Order
require(_order.owner == msg.sender, "Not Owner"); // 必须由持有人发起
// 声明IERC721接口合约变量
IERC721 _nft = IERC721(_nftAddr);
require(_nft.ownerOf(_tokenId) == address(this), "Invalid Order"); // NFT在合约中
// 调整NFT价格
_order.price = _newPrice;
// 释放Update事件
emit Update(msg.sender, _nftAddr, _tokenId, _newPrice);
}
购买purchase:买家支付ETH购买挂单的NFT,并释放Purchase事件。参数为NFT合约地址_nftAddr,NFT对应的_tokenId。成功后,ETH将转给卖家,NFT将从NFTSwap合约转给买家。
// 购买: 买家购买NFT,合约为_nftAddr,tokenId为_tokenId,调用函数时要附带ETH
function purchase(address _nftAddr, uint256 _tokenId) payable public {
Order storage _order = nftList[_nftAddr][_tokenId]; // 取得Order
require(_order.price > 0, "Invalid Price"); // NFT价格大于0
require(msg.value >= _order.price, "Increase price"); // 购买价格大于标价
// 声明IERC721接口合约变量
IERC721 _nft = IERC721(_nftAddr);
require(_nft.ownerOf(_tokenId) == address(this), "Invalid Order"); // NFT在合约中
// 将NFT转给买家
_nft.safeTransferFrom(address(this), msg.sender, _tokenId);
// 将ETH转给卖家,多余ETH给买家退款
payable(_order.owner).transfer(_order.price);
payable(msg.sender).transfer(msg.value-_order.price);
delete nftList[_nftAddr][_tokenId]; // 删除order
// 释放Purchase事件
emit List(msg.sender, _nftAddr, _tokenId, msg.value);
}
这一讲,我们建立了一个零手续费的去中心化NFT交易所。OpenSea虽然对NFT的发展做了很大贡献,但它的缺点也非常明显:高手续费、不发币回馈用户、交易机制容易被钓鱼导致用户资产丢失。目前Looksrare和dydx等新的NFT交易平台正在挑战OpenSea的位置,Uniswap也在研究新的NFT交易所。相信不久的将来,我们会用到更好的NFT交易所。
我最近在重新学solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。
推特:@WTFAcademy_ |@0xAA_Science
WTF Academy社群: 官网 wtf.academy | discord | 微信群申请
所有代码和教程开源在github: github.com/AmazingAng/WTFSolidity
Opensea是以太坊上最大的NFT交易平台,总交易总量达到了$300亿。Opensea在交易中抽成2.5%,因此它通过用户交易至少获利了$7.5亿。另外,它的运作并不去中心化,且不准备发币补偿用户。NFT玩家苦Opensea久已,今天我们就利用智能合约搭建一个零手续费的去中心化NFT交易所:NFTSwap。
卖家:出售NFT的一方,可以挂单list、撤单revoke、修改价格update。
买家:购买NFT的一方,可以购买purchase。
订单:卖家发布的NFT链上订。,一个系列的同一tokenId最多存在一个订单,其中包含挂单价格price和持有人owner信息。当一个订单交易完成或被撤单后,其中信息清零。
合约包含4个事件,对应挂单list、撤单revoke、修改价格update、购买purchase这四个行为:
event List(address indexed seller, address indexed nftAddr, uint256 indexed tokenId, uint256 price);
event Purchase(address indexed buyer, address indexed nftAddr, uint256 indexed tokenId, uint256 price);
event Revoke(address indexed seller, address indexed nftAddr, uint256 indexed tokenId);
event Update(address indexed seller, address indexed nftAddr, uint256 indexed tokenId, uint256 newPrice);
NFT订单抽象为Order结构体,包含挂单价格price和持有人owner信息。nftList映射记录了订单是对应的NFT系列(合约地址)和tokenId信息。
// 定义order结构体
struct Order{
address owner;
uint256 price;
}
// NFT Order映射
mapping(address => mapping(uint256 => Order)) public nftList;
在NFTSwap中,用户使用ETH购买NFT。因此,合约需要实现fallback()函数来接收ETH。
fallback() external payable{}
ERC721的安全转账函数会检查接收合约是否实现了onERC721Received()函数,并返回正确的选择器selector。用户下单之后,需要将NFT发送给NFTSwap合约。因此NFTSwap继承IERC721Receiver接口,并实现onERC721Received()函数:
contract NFTSwap is IERC721Receiver{
// 实现{IERC721Receiver}的onERC721Received,能够接收ERC721代币
function onERC721Received(
address operator,
address from,
uint tokenId,
bytes calldata data
) external override returns (bytes4){
return IERC721Receiver.onERC721Received.selector;
}
合约实现了4个交易相关的函数:
挂单list():卖家创建NFT并创建订单,并释放List事件。参数为NFT合约地址_nftAddr,NFT对应的_tokenId,挂单价格_price(注意:单位是wei)。成功后,NFT会从卖家转到NFTSwap合约中。
// 挂单: 卖家上架NFT,合约地址为_nftAddr,tokenId为_tokenId,价格_price为以太坊(单位是wei)
function list(address _nftAddr, uint256 _tokenId, uint256 _price) public{
IERC721 _nft = IERC721(_nftAddr); // 声明IERC721接口合约变量
require(_nft.getApproved(_tokenId) == address(this), "Need Approval"); // 合约得到授权
require(_price > 0); // 价格大于0
Order storage _order = nftList[_nftAddr][_tokenId]; //设置NF持有人和价格
_order.owner = msg.sender;
_order.price = _price;
// 将NFT转账到合约
_nft.safeTransferFrom(msg.sender, address(this), _tokenId);
// 释放List事件
emit List(msg.sender, _nftAddr, _tokenId, _price);
}
撤单revoke():卖家撤回挂单,并释放Revoke事件。参数为NFT合约地址_nftAddr,NFT对应的_tokenId。成功后,NFT会从NFTSwap合约转回卖家。
// 撤单: 卖家取消挂单
function revoke(address _nftAddr, uint256 _tokenId) public {
Order storage _order = nftList[_nftAddr][_tokenId]; // 取得Order
require(_order.owner == msg.sender, "Not Owner"); // 必须由持有人发起
// 声明IERC721接口合约变量
IERC721 _nft = IERC721(_nftAddr);
require(_nft.ownerOf(_tokenId) == address(this), "Invalid Order"); // NFT在合约中
// 将NFT转给卖家
_nft.safeTransferFrom(address(this), msg.sender, _tokenId);
delete nftList[_nftAddr][_tokenId]; // 删除order
// 释放Revoke事件
emit Revoke(msg.sender, _nftAddr, _tokenId);
}
修改价格update():卖家修改NFT订单价格,并释放Update事件。参数为NFT合约地址_nftAddr,NFT对应的_tokenId,更新后的挂单价格_newPrice(注意:单位是wei)。
// 调整价格: 卖家调整挂单价格
function update(address _nftAddr, uint256 _tokenId, uint256 _newPrice) public {
require(_newPrice > 0, "Invalid Price"); // NFT价格大于0
Order storage _order = nftList[_nftAddr][_tokenId]; // 取得Order
require(_order.owner == msg.sender, "Not Owner"); // 必须由持有人发起
// 声明IERC721接口合约变量
IERC721 _nft = IERC721(_nftAddr);
require(_nft.ownerOf(_tokenId) == address(this), "Invalid Order"); // NFT在合约中
// 调整NFT价格
_order.price = _newPrice;
// 释放Update事件
emit Update(msg.sender, _nftAddr, _tokenId, _newPrice);
}
购买purchase:买家支付ETH购买挂单的NFT,并释放Purchase事件。参数为NFT合约地址_nftAddr,NFT对应的_tokenId。成功后,ETH将转给卖家,NFT将从NFTSwap合约转给买家。
// 购买: 买家购买NFT,合约为_nftAddr,tokenId为_tokenId,调用函数时要附带ETH
function purchase(address _nftAddr, uint256 _tokenId) payable public {
Order storage _order = nftList[_nftAddr][_tokenId]; // 取得Order
require(_order.price > 0, "Invalid Price"); // NFT价格大于0
require(msg.value >= _order.price, "Increase price"); // 购买价格大于标价
// 声明IERC721接口合约变量
IERC721 _nft = IERC721(_nftAddr);
require(_nft.ownerOf(_tokenId) == address(this), "Invalid Order"); // NFT在合约中
// 将NFT转给买家
_nft.safeTransferFrom(address(this), msg.sender, _tokenId);
// 将ETH转给卖家,多余ETH给买家退款
payable(_order.owner).transfer(_order.price);
payable(msg.sender).transfer(msg.value-_order.price);
delete nftList[_nftAddr][_tokenId]; // 删除order
// 释放Purchase事件
emit List(msg.sender, _nftAddr, _tokenId, msg.value);
}
这一讲,我们建立了一个零手续费的去中心化NFT交易所。OpenSea虽然对NFT的发展做了很大贡献,但它的缺点也非常明显:高手续费、不发币回馈用户、交易机制容易被钓鱼导致用户资产丢失。目前Looksrare和dydx等新的NFT交易平台正在挑战OpenSea的位置,Uniswap也在研究新的NFT交易所。相信不久的将来,我们会用到更好的NFT交易所。
>100 subscribers
>100 subscribers
No activity yet