如何在云端构建全栈 Web3 应用
作为一名开发者,我一直对如何利用新兴技术开发应用程序充满好奇。最近,一项吸引我注意的技术是非同质化代币 (NFT)。NFT 是一种存储在区块链上的独特数字资产。一个常见的用例是将数字艺术存储为 NFT,然后出售给收藏家。我一直坚信代码就是艺术。随着 NFT 需求的复苏,这为构建一款能够将代码嵌入艺术的产品提供了绝佳的机会。GitGallery应运而生,这是一个允许你将 GitHub 提交的内容铸造为 NFT 的网站。这篇文章将深入探讨我是如何构建 GitGallery 的,重点介绍
- 智能合约开发
- 后端 API
- 用户界面
- 应用程序部署
概述
为了提供可扩展、经济高效的 NFT 铸造流程,我利用了Polygon,这是一种协议和框架,可以降低运行基于以太坊的应用程序所需的费用。
此外,整个 Web 应用程序都部署在 Amazon Web Services (AWS) 上。通过利用 AWS,我能够创建一个无服务器应用程序来响应事件,而不是像部署到服务器上那样浪费计算资源。借助AWS Lambda和API Gateway,每月可以免费使用一百万个事件,这提供了一种经济高效的应用程序托管方式。以下部分将详细介绍我用于开发智能合约、后端 API(应用程序编程接口)和用户界面的工具和流程。
智能合约
我开发的智能合约能够创建一个名为 GitNFT 的唯一代币,该代币与 GitHub 的提交相关联。代码使用 Solidity 编程语言开发。我能够导入由 OpenZeppelin 开发的代码,OpenZeppelin 是一家为区块链应用程序提供安全代码的公司。该代码实现了 ERC(以太坊征求意见稿)721 的 NFT 标准,该标准提供了将智能合约部署到区块链并创建 NFT 的功能。
pragma solidity ^0.8.0;
import "./ERC721Tradable.sol";
/**
* @title GitNFT
* GitNFT - a contract for code GitNFTs.
*/
contract GitNFT is ERC721Tradable {
uint256 public nextTokenId;
address public admin;
constructor(address _proxyRegistryAddress)
public
ERC721Tradable("GitNFT", "GitNFT", _proxyRegistryAddress)
{
admin = msg.sender;
}
// only our wallet should be able to mint
function mint(address to) external onlyOwner {
_safeMint(to, nextTokenId);
nextTokenId++;
}
function baseTokenURI() public pure override returns (string memory) {
return "https://www.gitgallery.com/tokenid/";
}
}
代码编写完成后,下一步就是将合约部署到 Polygon 区块链。
我使用部署软件Truffle和以太坊 API Infura编译了我的合约代码,并将这些文件部署到 Polygon 区块链。以下是将合约部署到 Polygon 区块链的代码
const NFT = artifacts.require("GitNFT");
const proxyRegistryAddress = process.env.OWNER_ADDRESS
module.exports = async function (deployer, _network, accounts) {
await deployer.deploy(NFT,proxyRegistryAddress, {gas: 5000000});
const nft = await NFT.deployed();
};
这笔交易需要使用 Polygon 代币 MATIC 支付合约费用,总计约 1 美分。编译代码还会生成一个应用程序二进制接口 (ABI),这是一个 JavaScript 对象表示法 (JSON) 文件,允许其他应用程序与合约进行交互。
后端 API
我使用 Python 开发了 GitGallery 所需的业务逻辑,并使用Flask框架对 API 端点进行编码。该应用程序所需的 4 个主要端点分别是登录、验证、铸造和获取商品。
登录
利用Auth0(一个旨在简化通过第三方身份验证登录的平台),我创建了一个允许用户通过其 GitHub 帐户登录的端点。该代码通过当前 Web 浏览器会话中的 Cookie 存储已登录用户的详细信息,以便跟踪每个请求的状态。当用户注销时,浏览器会话将清除所有 Cookie。
核实
要验证 GitHub 提交,用户需要输入其 GitHub 提交的 URL。通过使用 GitHub GraphQL API,我创建了一个查询,可以验证用户是否为给定项目创建了提交 ID。以下是用于验证提交作者的 GraphQL 查询。
{
repository(owner: "OWNER", name: "NAME") {
object(expression: "COMMIT_ID") {
... on Commit {
author{
name
user{
login
}
}
}
}
}
}
薄荷
一旦提交被验证,用户就可以将其铸造为 NFT。通过使用 ABI 和 Python 以太坊库web3.py,代码可以执行 NFT 合约的 mint 函数。以下是用于铸造 GitNFT 的代码
def web3_mint(userAddress: str):
"""
Purpose:
mint a token for user on blockchain
Args:
userAddress - the user to mint for
Returns:
hash - txn of mint
tokenid - token minted
"""
nonce = w3.eth.get_transaction_count(PUBLIC_KEY)
# Create the contract function
mint_txn = CODE_NFT.functions.mint(userAddress).buildTransaction(
{
"chainId": CHAIN_ID,
"gas": 10000000,
"gasPrice": w3.toWei("1", "gwei"),
"nonce": nonce,
}
)
signed_txn = w3.eth.account.sign_transaction(mint_txn, private_key=PRIVATE_KEY)
w3.eth.send_raw_transaction(signed_txn.rawTransaction)
hash = w3.toHex(w3.keccak(signed_txn.rawTransaction))
receipt = w3.eth.wait_for_transaction_receipt(hash)
hex_tokenid = receipt["logs"][0]["topics"][3].hex() # this is token id in hex
# convert from hex to decmial
tokenid = int(hex_tokenid, 16)
logging.info(f"Got tokenid: {tokenid}")
return hash, tokenid
调用合约的费用由项目分配的 MATIC I 支付。由于铸造成本不到几分钱,因此整个过程几乎免费,更重要的是,用户无需支付任何费用。每个铸造的 NFT 都会生成一个包含提交元数据的 JSON 文件。元数据存储在Amazon S3和DynamoDB中。
获取物品
为了显示特定用户的项目,代码会创建一个 DynamoDB 查询,使用所选用户作为分区键。S3 中的 JSON 用于外部平台,例如 NFT 交易平台 OpenSea。外部应用程序需要特定的格式才能正确显示数据,而 S3 负责处理这些数据,而 GitGallery 上的数据则由 DynamoDB 渲染。
部署
为了将代码部署到 Web 上,我利用了Serverless框架。Serverless 允许我创建一个 YAML(YAML 并非标记语言)文件,该文件指定了将 Flask 应用程序部署为 Docker 镜像所需的所有基础架构和安全措施。Serverless 会在 AWS Lambda 上部署 Docker 镜像,并将端点映射到 API 网关,并赋予访问 S3 和 DynamoDB 等服务所需的身份和访问管理 (IAM) 角色。
用户界面
用户界面是一个使用 HTML、Bootstrap、JavaScript、jinja2 和 JQuery 构建的网站。这些工具允许我创建一个利用服务器端渲染的用户界面,以实现动态路由,例如www.gitgallery.com/gallery/{USER_NAME}/minted
@application.route("/gallery/<user>/minted")
def minted(user):
"""
Purpose:
load gallery page
Args:
N/A
Returns:
html - gallery html
"""
if "profile" in session:
curr_user = session["profile"]
else:
curr_user = None
# check if user exists...
userdata = users.get_item(Key={"username": user})
if not "Item" in userdata:
logging.error(f"No such user {user}")
return redirect(f"/")
user_metadata = userdata["Item"]
# get minted items
minted_items = get_minted_items(user)
return render_template(
"gallery.html",
userinfo=curr_user,
minted_items=minted_items,
user=user,
user_metadata=user_metadata,
)
此外,我利用 ethers.js 库为用户提供了一种使用 MetaMask 钱包签署 GitHub 提交的方式。MetaMask 钱包为用户提供了一个与以太坊区块链交互的接口。以下是用于铸造代币的前端代码。
async function safe_mint(data) {
// console.log("safe mint called")
let userAddress = ""
try {
userAddress = await walletSigner.getAddress()
} catch (error) {
alert("Metamask not detected")
console.log(error)
return
}
let username = data["username"]
let ver_url = data["verify_url"]
let ver_date = data["status_json"]["date"]
// Commint
const base_message = "Signed on GitGallery\n\n" + username + " " + ver_date + "\n " + ver_url
const signature = await walletSigner.signMessage(base_message)
var mintObj = {}
mintObj["verify_json"] = data
mintObj["signature"] = signature
mintObj["userAddress"] = userAddress
var mintString = JSON.stringify(mintObj)
$("body").css("opacity", ".3");
$("#load_spinner").toggle();
$.ajax({
type: "POST",
url: "/mint_token",
dataType: "json",
data: mintString,
contentType: "application/json",
success: function (data) {
console.log("mint returned")
console.log(data)
$("#load_spinner").toggle();
$("body").css("opacity", "1");
//check for error
if ('error' in data) {
alert(data["error"]);
return -1
}
alert("Mint Successful")
//passed? reload page?
location.reload()
},
error: function (xhr, textStatus, thrownError, data) {
alert("Error: " + thrownError);
$("body").css("opacity", "1");
$("#load_spinner").toggle();
}
})
}
结论
将所有节点连接起来,即可生成一个工作流程简单的全栈 NFT 应用程序。用户使用其 GitHub 凭证登录。用户输入 GitHub Commit 的 URL。系统为用户验证提交。然后,用户使用 MetaMask 对提交进行签名。最后,GitHub Commit 被铸造为 NFT,并存储在用户的 MetaMask 钱包中。此工作流程允许在 30 秒内免费创建 NFT。
如果您渴望构建自己的 NFT 项目,我已经创建了一个实践研讨会,重点是利用这些工具来启动您自己的智能合约。
鏂囩珷鏉ユ簮锛�https://dev.to/banjtheman/how-i-built-a-full-stack-web3-app-on-the-cloud-574j