要开始在 Starknet 上构建你的第一个 NFT,你需要设置你的开发者框架。我们的首选是 Protostar。
设置好 Protostar 后,您就可以构建您的第一个starknet NFT了。我们称我们的项目为“ StarknetERC721 ”。
为此,请运行:
原星初始化
将进一步请求项目名称和库的目录名称。这是成功初始化项目所必需的。
我们需要在我们的src文件夹中创建一个名为ERC721.cairo的新文件。这是我们要编写合约代码的地方。
在我们的新文件中,我们将从
%lang starknet
指令开始,该指令指定我们的文件包含 Starknet 合约的代码。
完成后,我们导入所有必要的库函数
%lang starknet
from starkware.cairo.common.cairo_builtins import HashBuiltin
from starkware.cairo.common.uint256 import Uint256
from starkware.starknet.common.syscalls import get_caller_address
from cairo_contracts.src.openzeppelin.token.erc721.library import ERC721
from cairo_contracts.src.openzeppelin.introspection.erc165.library import ERC165
from cairo_contracts.src.openzeppelin.access.ownable.library import Ownable
这是 ERC721 合约接口,它公开了我们在构建令牌时需要实现的方法:
%lang starknet
form starkware.cairo.common.uint256 import Uint256
@contract_interface
namespace IERC20 {
func name() -> (name: felt) {
}
func symbol() -> (symbol: felt) {
}
func decimals() -> (decimals: felt) {
}
func totalSupply() -> (totalSupply: Uint256) {
}
func balanceOf(account: felt) -> (balance: Uint256) {
}
func allowance(owner: felt, spender: felt) -> (remaining: Uint256) {
}
func transfer(recipient: felt, amount: Uint256) -> (success: felt) {
}
func transferFrom(sender:felt,recipient:felt,amount:Uint256)->(success:felt){
}
func approve(spender: felt, amount: Uint256) -> (success: felt) {
}
}
构造函数主要用于在合约部署时初始化某些状态变量。
要在 Cairo 中创建构造函数,您需要使用**@constructor**装饰器。
对于我们的 ERC20 代币,我们希望在部署时初始化变量,例如name、symbol、decimals和totalSupply。
为此,我们的合约必须实现以下构造函数:
@constructor
func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr} (
name: felt, symbol: felt, owner: felt
) {
ERC721.initializer(name, symbol);
Ownable.initializer(owner);
return ();
}
如上面的代码片段所示,我们的构造函数必须命名为constructor并接受 5 个参数 [_name, _symbol, _decimals, initialSupply, recipient]。
我们还通过使用从 Openzeppelin 导入的ERC20命名空间调用初始化器内部函数,同时传递所需的函数参数 [名称、符号和小数]。初始化函数创建/实例化一个总供应量为 0 的新 ERC20 代币。
为了在部署时创建固定的总供应量,我们调用传入函数参数 [recipient, initialSupply]的**_mint函数。_mint函数铸造**提供给收件人地址的 initialSupply。
实现我们的构造函数逻辑后,我们现在可以实现我们的代币符合 ERC20 标准所需的其他功能,但在我们继续之前,让我们拿出几行来区分 Cairo 中的两种主要类型的功能。
外部函数——外部函数是改变区块链状态的函数,是使用**@external**装饰器创建的。
视图函数——视图函数是 getter 函数。它们不会改变区块链的状态,并且是使用**@view装饰器创建的。**
name 函数是一个视图函数,它在查询时简单地返回令牌的名称。
@view
func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr} () -> (name: felt) {
let (name) = ERC721.name();
return (name,);
}
对于我们的 ERC721 令牌,我们需要在部署时初始化某些变量,例如name、symbol和owner。为此,我们的合约必须实现一个构造函数
我们还通过使用从 Openzeppelin 导入的ERC721命名空间调用初始化器内部函数,同时传递所需的函数参数 [名称和符号]。我们最终通过调用Ownable命名空间的初始化函数为合约分配所有者。
approve 函数也是一个外部函数,用于批准支出者从所有者的钱包中花费一定数量的代币。
它有两个参数 [spender, amount]。
@view
func getApproved{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr} (tokenId: Uint256) -> (approved: felt) {
let (approved) = ERC721.get_approved(tokenId);
return (approved,);
}
此功能将令牌所有权从一个帐户转移到另一个帐户。
@external
func transferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
_from: felt, to: felt, tokenId: Uint256
) {
ERC721.transfer_from(_from, to, tokenId);
return ();
}
将令牌所有权从一个帐户安全转移到另一个帐户。
@external
func safeTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
_from: felt, to: felt, tokenId: Uint256, data_len: felt, data: felt*
) {
ERC721.safe_transfer_from(_from, to, tokenId, data_len, data);
return ();
}
此功能启用或禁用批准操作员管理所有所有者的资产
@external
func setApprovalForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
operator: felt, approved: felt
) {
ERC721.set_approval_for_all(operator, approved);
return ();
}
恭喜,您刚刚完成了第一个 ERC721 合约!您可以在此处找到完整的合同代码。
当我们完成我们的 NFT 合约时,我们需要一个额外的功能来为用户铸造具有不同代币 ID 的新代币。为此,我们将实现一个存储变量 token_counter 和一个外部函数 mint。
token_counter 是一个存储变量,它跟踪创建的令牌数量以确定下一个令牌 ID。
@storage_var
func token_counter() -> (id: felt) {
}
mint 函数是一个实现铸币逻辑的外部函数。它首先检查调用者是合约所有者,然后计算新的代币 ID,将新代币铸造给接收者,最后更新 token_counter 变量。
@external
func mint{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
to: felt
) {
Ownable.assert_only_owner();
let (prevTokenId) = token_counter.read();
let tokenId = prevTokenId + 1;
ERC721._mint(to, Uint256(tokenId, 0));
token_counter.write(tokenId);
return ();
}
最后,按照部署合约的常规步骤,我们将首先构建/编译、声明和部署。
当你构建你的 Starknet NFT 时,你需要在你的 protostar.toml 中指定你的合约的正确路径,并使 Protostar 能够找到你的 lib 文件夹,其中包含你的 Openzeppelin 合约。为此,请添加代码段:
cairo-path = ["lib"]
Your protostar.toml file should look like this:
[project]
protostar-version = "0.9.1"
lib-path = "lib"
cairo-path = ["lib"]
[contracts]
ERC721 = ["src/ERC721.cairo"]
protostar declare ./ERC721.json --network testnet --account xx --max-fee auto
在执行“declare”命令之前,您需要在文件或终端中设置与指定账户地址关联的私钥。
要在终端中设置您的私钥,请运行以下命令:
export PROTOSTAR_ACCOUNT_PRIVATE_KEY=[privatekey]
protostar deploy xx --network testnet --account xx --max-fee auto --inputs 71959616777844 4280903 xx
不要与任何人分享您的私钥,它应该只供您使用
恭喜!您刚刚在 StarkNet 上编写并部署了您的第一个 ERC721 合约。
要与已部署的合约进行交互,请在此处检查 Starkscan 。
