
5 分钟内构建 cNFT 铸造移动应用
你将学到什么
本教程将介绍如何构建一个 NFT(非同质化代币)铸造 Android 应用。我们的目标是让用户轻松拍摄图像,并在 Solana 上将其铸造为压缩 NFT。为此,我们将使用 Helius Mint API。借助此 API,你可以更快地铸造压缩 NFT,同时减少样板代码,从而节省大量开发和规划时间。我们的 Android 应用将使用 Kotlin 和 Jetpack Compose 编写。
什么是压缩 NFT?
压缩 NFT(cNFT)利用状态压缩和并发 Merkle 树来降低存储成本。压缩 NFT 的成本比未压缩 NFT 低 2,400 至 24,000 倍,同时仍保留完全相同的结构。
准备工作
在开始本教程之前,你应基本了解使用 Kotlin 进行 Android 开发,并且熟悉 Solana。
工具
- Android Studio:Giraffe | 版本 2022.3.1
- 模拟器或移动设备:用于测试应用。请确保已安装兼容 MWA 的钱包应用(推荐使用 Solfare)。
API 密钥
要使用 Helius Mint API,你需要一个 API 密钥。获取方式如下:
- 访问 Helius 开发者门户。
- 使用你的 Solana 钱包、Google 或 GitHub 账户创建账号。
- 登录后,按照提示生成 API 密钥。
- 请务必妥善保管此密钥,因为本教程中的 API 调用都会使用它。
构建应用
第 1 步:设置项目
我们已经为这个项目准备了一个入门代码库。
请按照以下步骤开始:
- 访问 GitHub 上的入门项目。
- Fork 该仓库,并将其克隆到本地环境。
将代码下载到本地后,继续执行以下操作:
- 在 Android Studio 中打开项目。
- 等待 Gradle 完成构建。
- 运行代码。
浏览入门应用,熟悉其功能。你会看到应用中已集成钱包连接按钮,可用于连接任何兼容 Mobile Wallet Adapter(MWA)的钱包应用。Mobile Wallet Adapter(MWA)是一种协议规范,用于连接移动 dApp 与移动钱包应用,以便执行 Solana 交易和消息签名。Mobile Wallet Adapter 支持这些钱包。此外,你还会看到“拍照”按钮,目前可用于拍摄照片。
要在代码库中安全存储 Helius API 密钥,请前往 local.properties 文件并添加 API 密钥。此文件专用于保存敏感信息,并会被 Git 忽略,从而确保你的 API 密钥不会泄露。
api_key=<your-api-key>
接下来,我们将编写相关逻辑,把拍摄的图像铸造为压缩 NFT(cNFT)。
第 2 步:铸造逻辑
我们的目标是将“拍照”按钮与相机功能集成。完成集成后,用户就能拍摄图像并将其铸造为 NFT。我们已提供用于打开相机和拍摄图像的入门代码。现在,我们将重点创建一个管理铸造流程的函数。
Helius Mint API
此方法用于铸造压缩 NFT。执行后,它会暂停并等待交易确认;在网络活动高峰期,这一过程最长可能需要 60 秒。请访问文档了解更多信息。
首先前往 java/com/example/hmint/MainViewModel.kt,并在 MainViewModel 类中创建 mintCNft 函数:
@HiltViewModel
class MainViewModel @Inject constructor(
private val walletAdapter: MobileWalletAdapter,
private val walletConnectionUseCase: WalletConnectionUseCase,
) : ViewModel() {
// ...
fun mintCNft(imageUri: Uri, nftName: String, user: String) =
viewModelScope.launch {
_state.update {
_state.value.copy(
isLoading = true,
)
}
}
}我们将在此函数中启动一个协程来处理铸造逻辑。首先,获取 API 密钥,定义必要的变量和 requestBody。之后,你可以根据需求自定义 requestBody,添加其他属性和数据:
withContext(viewModelScope.coroutineContext + Dispatchers.IO) {
val apiKey = System.getProperty("api_key") ?: ""
val url = URL("https://mainnet.helius-rpc.com/?api-key=$apiKey")
val mediaType = "application/json".toMediaTypeOrNull()
val requestBody = """
{
"jsonrpc": "2.0",
"id": "helius-test",
"method": "mintCompressedNft",
"params": {
"name": "$nftName",
"symbol": "HM",
"owner": "$user",
"description": "This is a test cNFT minted by hMint app",
"attributes": [
{
"trait_type": "hMint",
"value": "1"
}
],
"imageUrl": "$imageUri",
"sellerFeeBasisPoints": 6900
}
}
""${'"'}.trimIndent()
}接下来,我们将构建请求并初始化一个 OkHttpClient:
val body = requestBody.toRequestBody(mediaType)
val request = Request.Builder()
.url(url)
.post(body)
.addHeader("accept", "application/json")
.addHeader("content-type", "application/json")
.build()
val client = OkHttpClient()在调用客户端之前,我们先使用数据类定义响应。创建一个名为 data 的新包,并在其中创建新文件 NFTResponse.kt,以定义 API 调用返回的响应结构。
package com.example.hmint.data
data class NftMintResponse(
val jsonrpc: String,
val id: String,
val result: MintResult
)
data class MintResult(
val signature: String,
val minted: Boolean,
val assetId: String
)然后,在 MainViewModel 类中,为 WalletViewState 数据类添加 mintResponse 值。收到响应后,我们将更新此值:
data class WalletViewState(
// ...
val mintResponse: String = ""
)回到 mintCNft 函数,我们将通过客户端执行请求,并相应地处理失败情况或响应。收到响应后,我们将使用 Gson,把数据解析到之前创建的 NFTMintResponse 数据类中,然后相应更新 mintResponse 的值:
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
// Handle failure
e.printStackTrace()
Log.d(TAG, "NFT Mint Failed")
_state.update {
_state.value.copy(
isLoading = false,
mintResponse = "Mint Failed"
)
}
}
override fun onResponse(call: Call, response: Response) {
if (response.isSuccessful) {
val responseBody = response.body?.string() ?: ""
val gson = Gson()
val nft = gson.fromJson(responseBody, NftMintResponse::class.java)
Log.d(TAG, "NFT Mint Successful: $nft")
_state.update {
_state.value.copy(
isLoading = false,
mintResponse = "Minted Successfully"
)
}
} else {
// Handle non-successful response
Log.d(TAG, "NFT Mint Failed: ${response.code}")
_state.update {
_state.value.copy(
isLoading = false,
mintResponse = "Minted Failed"
)
}
}
}
})在 UI 中,我们希望向用户反馈铸造函数的执行结果。因此,请前往 java/com/example/hmint/composables/MintButton.kt,其中包含负责图像拍摄的 composable、UI 代码和逻辑。在声明了 cameraLauncher 的 MintButton 中,调用 mintCNft 函数。此外,请添加代码,在 mintResponse 更新时显示 Toast。
@Composable
fun MintButton(
mainViewModel: MainViewModel = hiltViewModel()
) {
val cameraLauncher =
rememberLauncherForActivityResult(ActivityResultContracts.TakePicture()) {
// ...
mainViewModel.mintCNft(uri, "hMint", viewState.userAddress)
}
viewState.mintResponse.let { message ->
if(message.isNotEmpty()){
Toast.makeText(LocalContext.current, message, Toast.LENGTH_SHORT).show()
}
}
// ...
}第 3 步:运行应用
编码阶段已经完成,现在来运行应用。首先连接你的钱包,然后拍摄图像,应用会流畅地为你铸造 NFT。
总结
做得好!你已成功开发出一个可供用户铸造压缩 NFT 的 Android 应用。借助 Helius Mint API,铸造 cNFT 变得前所未有地简单,只需一次调用即可完成。
下一步是什么?
这款应用还有许多值得探索和增强的空间,其中一项改进就是集成 DAS API。数字资产标准(DAS)API是一套开源规范和系统,为代币和 NFT 等数字资产提供统一的交互接口。它支持多种资产类型,包括同质化代币、标准 NFT 和压缩 NFT。 你可以使用该 API 获取所有已铸造的压缩 NFT,并在应用中显示它们,让用户全面查看自己的数字资产。
相关资源
如果你想深入掌握使用 Jetpack Compose 进行 Android 开发、集成 Solana 区块链以及与 API 交互,可以查看以下资源:
- Jetpack Compose
- Kotlin 中的 Solana Mobile Adapter
- 使用 Hilt 进行依赖注入
- Android 中的 OkHttpClient
- Solana 上的压缩技术
- Helius MINT API
如果你需要任何帮助或支持,欢迎随时通过 Discord 联系我们。完整代码库已发布在 GitHub,供你进一步参考。
相关文章
订阅 Helius
及时了解 Solana 开发的最新动态,并在我们发布新内容时收到更新


