Solidity脚本
介绍
Solidity 脚本是一种使用 Solidity 以声明方式部署合约的方法,而不是使用限制更多且用户友好度较低的 forge create
。
Solidity 脚本就像您在使用 Hardhat 等工具时编写的脚本; Solidity 脚本的不同之处在于它们是用 Solidity 而不是 JavaScript 编写的,并且它们在快速的 Foundry EVM 后端上运行,该后端提供试运行功能。
设置
让我们尝试使用 solidity 脚本部署在 solmate 教程中制作的 NFT 合约。 首先,我们需要通过以下方式创建一个新的 Foundry 项目:
forge init solidity-scripting
由于 solmate 教程中的 NFT 合约继承了 solmate 和 OpenZeppelin 合约,我们必须通过运行以下命令将它们安装为依赖项:
# Enter the project
cd solidity-scripting
# Install Solmate and OpenZeppelin contracts as dependencies
forge install transmissions11/solmate Openzeppelin/openzeppelin-contracts
接下来,我们必须删除 src
文件夹中的 Counter.sol
文件并创建另一个名为 NFT.sol
的文件。 你可以通过运行来做到这一点:
rm src/Counter.sol test/Counter.t.sol && touch src/NFT.sol && ls src
完成后,你应该打开你喜欢的代码编辑器并将下面的代码复制到NFT.sol
文件中。
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.10;
import "solmate/tokens/ERC721.sol";
import "openzeppelin-contracts/utils/Strings.sol";
import "openzeppelin-contracts/access/Ownable.sol";
error MintPriceNotPaid();
error MaxSupply();
error NonExistentTokenURI();
error WithdrawTransfer();
contract NFT is ERC721, Ownable {
using Strings for uint256;
string public baseURI;
uint256 public currentTokenId;
uint256 public constant TOTAL_SUPPLY = 10_000;
uint256 public constant MINT_PRICE = 0.08 ether;
constructor(
string memory _name,
string memory _symbol,
string memory _baseURI
) ERC721(_name, _symbol) {
baseURI = _baseURI;
}
function mintTo(address recipient) public payable returns (uint256) {
if (msg.value != MINT_PRICE) {
revert MintPriceNotPaid();
}
uint256 newTokenId = ++currentTokenId;
if (newTokenId > TOTAL_SUPPLY) {
revert MaxSupply();
}
_safeMint(recipient, newTokenId);
return newTokenId;
}
function tokenURI(uint256 tokenId)
public
view
virtual
override
returns (string memory)
{
if (ownerOf(tokenId) == address(0)) {
revert NonExistentTokenURI();
}
return
bytes(baseURI).length > 0
? string(abi.encodePacked(baseURI, tokenId.toString()))
: "";
}
function withdrawPayments(address payable payee) external onlyOwner {
uint256 balance = address(this).balance;
(bool transferTx, ) = payee.call{value: balance}("");
if (!transferTx) {
revert WithdrawTransfer();
}
}
}
现在,让我们尝试编译我们的合约以确保一切正常。
forge build
如果您的输出看起来像这样,则合同已成功编译。
部署我们的合约
我们将把“NFT”合约部署到 Goerli 测试网,但为此我们需要稍微配置 Foundry,通过设置 Goerli RPC URL 之类的东西,这是一个由 Goerli Eth 资助的账户的私钥 ,以及用于验证 NFT 合约的 Etherscan 密钥。
💡 注意:您可以在 此处 获得一些 Goerli 测试网 ETH。
环境配置
完成所有这些后,创建一个 .env
文件并添加变量。 Foundry 会自动加载项目目录中的 .env
文件。
.env
文件应遵循以下格式:
GOERLI_RPC_URL=
PRIVATE_KEY=
ETHERSCAN_API_KEY=
我们现在需要编辑 foundry.toml
文件。 项目的根目录中应该已经有一个。
将以下行添加到文件末尾:
[rpc_endpoints]
goerli = "${GOERLI_RPC_URL}"
[etherscan]
goerli = { key = "${ETHERSCAN_API_KEY}" }
This creates a RPC alias for Goerli and loads the Etherscan API key.
编写脚本
接下来,我们必须创建一个文件夹并将其命名为script
,并在其中创建一个名为NFT.s.sol
的文件。 这是我们将创建部署脚本本身的地方。
NFT.s.sol
的内容应该是这样的:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "forge-std/Script.sol";
import "../src/NFT.sol";
contract MyScript is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);
NFT nft = new NFT("NFT_tutorial", "TUT", "baseUri");
vm.stopBroadcast();
}
}
现在让我们通读代码并弄清楚它的实际含义和作用。
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
请记住,即使它是一个脚本,它仍然像智能合约一样工作,但从未部署过,所以就像任何其他用 Solidity 编写的智能合约一样,必须指定pragma version
。
import "forge-std/Script.sol";
import "../src/NFT.sol";
就像我们在编写测试时可能会导入 Forge Std 来获取测试实用程序一样,Forge Std 也提供了一些我们在这里导入的脚本实用程序。
下一行只是导入NFT
合约。
contract MyScript is Script {
我们创建一个名为 MyScript
的合约,它从 Forge Std 继承了 Script
。
function run() external {
默认情况下,脚本是通过调用名为run
的函数(我们的入口点)来执行的。
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
这会从我们的 .env 文件中加载私钥。 注意: 在 .env
文件中公开私钥并将它们加载到程序中时必须小心。 这仅建议与非特权部署者一起使用或用于本地/测试设置。 对于生产设置,请查看 Foundry 支持的各种钱包选项。
vm.startBroadcast(deployerPrivateKey);
这是一个特殊的Cheatcode,用于记录我们的主脚本合约进行的调用和合约创建。 我们传递 deployerPrivateKey
以指示它使用该密钥来签署交易。 稍后,我们将广播这些交易以部署我们的 NFT 合约。
NFT nft = new NFT("NFT_tutorial", "TUT", "baseUri");
在这里,我们只是创建我们的 NFT 合约。 因为我们在这行之前调用了 vm.startBroadcast()
,合约创建将被 Forge 记录下来,并且如前所述,我们可以广播交易以在链上部署合约。 默认情况下,广播事务日志将存储在“广播”目录中。 您可以通过在 foundry.toml
文件中设置 broadcast
来更改日志位置。
现在您已经了解了脚本智能合约的功能,让我们运行它。
您应该已经将我们之前提到的变量添加到 .env 中,以便下一部分工作。
在项目运行的根目录:
# To load the variables in the .env file
source .env
# To deploy and verify our contract
forge script script/NFT.s.sol:MyScript --rpc-url $GOERLI_RPC_URL --broadcast --verify -vvvv
Forge 将运行我们的脚本并为我们广播交易——这可能需要一些时间,因为 Forge 还将等待交易收据。 大约一分钟后,您应该会看到类似这样的内容:
这确认您已成功将 NFT
合约部署到 Goerli 测试网,并已在 Etherscan 上对其进行了验证,所有这些都通过一个命令完成。
本地部署
您可以通过将端口配置为 fork-url
来部署到本地测试网 Anvil。
在这里,我们在帐户方面有两种选择。 我们可以在没有任何标志的情况下启动 anvil,并使用提供的私钥之一。 或者,我们可以传递一个助记符给 anvil 来使用。
使用 Anvil 的默认帐户
首先,启动 Anvil:
anvil
使用 Anvil 提供给您的私钥更新您的 .env
文件。
然后运行以下脚本:
forge script script/NFT.s.sol:MyScript --fork-url http://localhost:8545 --broadcast
使用自定义助记符
将以下行添加到您的 .env 文件并使用您的助记符完成它:
MNEMONIC=
预计我们之前设置的PRIVATE_KEY
环境变量是这个助记词中的前10个账户之一。
使用自定义助记符启动 Anvil:
source .env
anvil --m $MNEMONIC
然后运行以下脚本:
forge script script/NFT.s.sol:MyScript --fork-url http://localhost:8545 --broadcast
💡 注意:可以在 此处 找到本教程的完整实现,要进一步阅读有关 solidity 脚本的信息,您可以查看“forge script” 参考。