# How to Construct Starknet Erc721 Contract **Published by:** [ens4me.eth](https://paragraph.com/@ens4me/) **Published on:** 2023-11-20 **URL:** https://paragraph.com/@ens4me/how-to-construct-starknet-erc721-contract ## Content 要开始在 Starknet 上构建你的第一个 NFT,你需要设置你的开发者框架。我们的首选是 Protostar。 设置好 Protostar 后,您就可以构建您的第一个starknet NFT了。我们称我们的项目为“ StarknetERC721 ”。 为此,请运行: 原星初始化 将进一步请求项目名称和库的目录名称。这是成功初始化项目所必需的。 创建一个新文件 我们需要在我们的src文件夹中创建一个名为ERC721.cairo的新文件。这是我们要编写合约代码的地方。 Entry: 在我们的新文件中,我们将从 %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 合约接口 这是 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。 在Cairo的功能 实现我们的构造函数逻辑后,我们现在可以实现我们的代币符合 ERC20 标准所需的其他功能,但在我们继续之前,让我们拿出几行来区分 Cairo 中的两种主要类型的功能。 外部函数——外部函数是改变区块链状态的函数,是使用**@external**装饰器创建的。 视图函数——视图函数是 getter 函数。它们不会改变区块链的状态,并且是使用**@view装饰器创建的。** 实现其他功能 1.姓名 name 函数是一个视图函数,它在查询时简单地返回令牌的名称。 @view func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr} () -> (name: felt) { let (name) = ERC721.name(); return (name,); } Write Constract 构造器 对于我们的 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 当我们完成我们的 NFT 合约时,我们需要一个额外的功能来为用户铸造具有不同代币 ID 的新代币。为此,我们将实现一个存储变量 token_counter 和一个外部函数 mint。 令牌计数器 token_counter 是一个存储变量,它跟踪创建的令牌数量以确定下一个令牌 ID。 @storage_var func token_counter() -> (id: felt) { } mint 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 (); } 部署 最后,按照部署合约的常规步骤,我们将首先构建/编译、声明和部署。 Build 当你构建你的 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: 在执行“declare”命令之前,您需要在文件或终端中设置与指定账户地址关联的私钥。 要在终端中设置您的私钥,请运行以下命令: export PROTOSTAR_ACCOUNT_PRIVATE_KEY=[privatekey] protostar deploy xx --network testnet --account xx --max-fee auto --inputs 71959616777844 4280903 xx 不要与任何人分享您的私钥,它应该只供您使用 结论 恭喜!您刚刚在 StarkNet 上编写并部署了您的第一个 ERC721 合约。 要与已部署的合约进行交互,请在此处检查 Starkscan 。 ## Publication Information - [ens4me.eth](https://paragraph.com/@ens4me/): Publication homepage - [All Posts](https://paragraph.com/@ens4me/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@ens4me): Subscribe to updates