Skip to content

Commit

Permalink
Add Swift example for managing NFT allowances
Browse files Browse the repository at this point in the history
Co-authored-by: Ricky Saechao <[email protected]>
  • Loading branch information
b-l-u-e and RickyLB authored Nov 7, 2024
1 parent 98a1a33 commit 8e27288
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 0 deletions.
181 changes: 181 additions & 0 deletions Examples/AddNftAllowance/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/*
* Hedera Swift SDK
*
* Copyright (C) 2022 - 2024 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import Foundation
import Hedera
import SwiftDotenv

@main
internal enum Program {
internal static func main() async throws {
let env = try Dotenv.load()
let client = try Client.forName(env.networkName)

client.setOperator(env.operatorAccountId, env.operatorKey)

// Step 1: Create an NFT
let nftCreateReceipt = try await TokenCreateTransaction()
.name("HIP-336 NFT1")
.symbol("HIP336NFT1")
.tokenType(TokenType.nonFungibleUnique)
.decimals(0)
.initialSupply(0)
.maxSupply(10)
.tokenSupplyType(.finite)
.treasuryAccountId(env.operatorAccountId)
.adminKey(.single(env.operatorKey.publicKey))
.supplyKey(.single(env.operatorKey.publicKey))
.freezeWith(client)
.execute(client)
.getReceipt(client)

guard let nftTokenId = nftCreateReceipt.tokenId else {
fatalError("Failed to create NFT")
}

print("Created NFT with token ID: \(nftTokenId)")

// Step 2: Mint NFTs
let cids = [
"QmNPCiNA3Dsu3K5FxDPMG5Q3fZRwVTg14EXA92uqEeSRXn",
"QmZ4dgAgt8owvnULxnKxNe8YqpavtVCXmc1Lt2XajFpJs9",
"QmPzY5GxevjyfMUF5vEAjtyRoigzWp47MiKAtLBduLMC1T",
]

let metadataArray = cids.map {
Data($0.utf8)
}

for cid in cids {
let mintReceipt = try await TokenMintTransaction()
.tokenId(nftTokenId)
.metadata(metadataArray)
.freezeWith(client)
.execute(client)
.getReceipt(client)

guard let serials = mintReceipt.serials, !serials.isEmpty else {
fatalError("Failed to mint NFTs")
}

for (index, serial) in serials.enumerated() {
print("Minted NFT (token ID: \(nftTokenId)) with serial: \(serials.first!)")
}

}

// Step 3: Create spender and receiver accounts
let spenderKey = PrivateKey.generateEd25519()
let receiverKey = PrivateKey.generateEd25519()

let spenderAccountId = try await AccountCreateTransaction()
.key(Key.single(spenderKey.publicKey))
.initialBalance(Hbar(2))
.execute(client)
.getReceipt(client)
.accountId!

let receiverAccountId = try await AccountCreateTransaction()
.key(Key.single(receiverKey.publicKey))
.initialBalance(Hbar(2))
.execute(client)
.getReceipt(client)
.accountId!

print("Created spender account ID: \(spenderAccountId), receiver account ID: \(receiverAccountId)")

// Step 4: Associate spender and receiver with the NFT
try await TokenAssociateTransaction()
.accountId(spenderAccountId)
.tokenIds([nftTokenId])
.freezeWith(client)
.sign(spenderKey)
.execute(client)
.getReceipt(client)

try await TokenAssociateTransaction()
.accountId(receiverAccountId)
.tokenIds([nftTokenId])
.freezeWith(client)
.sign(receiverKey)
.execute(client)
.getReceipt(client)

print("Associated spender and receiver accounts with NFT")

// Step 5: Approve NFT allowance for spender
try await AccountAllowanceApproveTransaction()
.approveTokenNftAllowance(NftId(tokenId: nftTokenId, serial: 1), env.operatorAccountId, spenderAccountId)
.approveTokenNftAllowance(NftId(tokenId: nftTokenId, serial: 2), env.operatorAccountId, spenderAccountId)
.execute(client)
.getReceipt(client)

print("Approved NFT allowance for spender")

// Step 6: Transfer NFT using approved allowance
let transferReceipt = try await TransferTransaction()
.approvedNftTransfer(NftId(tokenId: nftTokenId, serial: 1), env.operatorAccountId, receiverAccountId)
.freezeWith(client)
.transactionId(TransactionId.generateFrom(spenderAccountId))
.sign(spenderKey)
.execute(client)
.getReceipt(client)

print("Transfer successful with status: \(transferReceipt.status)")

// Step 7: Revoke allowance
_ = try await AccountAllowanceDeleteTransaction()
.deleteAllTokenNftAllowances(NftId(tokenId: nftTokenId, serial: 2), env.operatorAccountId)
.execute(client)

print("Revoked NFT allowance")

// Step 8: Attempt transfer after revoking allowance
do {
_ = try await TransferTransaction()
.approvedNftTransfer(NftId(tokenId: nftTokenId, serial: 2), env.operatorAccountId, receiverAccountId)
.freezeWith(client)
.sign(spenderKey)
.execute(client)

print("Transfer after revoking allowance should have failed")
} catch {
print("Expected failure: \(error)")
}

// Cleanup resources by deleting tokens, accounts, etc.
// Implement cleanup steps...
}
}

extension Environment {
/// Account ID for the operator to use in this example.
internal var operatorAccountId: AccountId {
AccountId(self["OPERATOR_ID"]!.stringValue)!
}

/// Private key for the operator to use in this example.
internal var operatorKey: PrivateKey {
PrivateKey(self["OPERATOR_KEY"]!.stringValue)!
}

/// The name of the Hedera network this example should run against.
internal var networkName: String {
self["HEDERA_NETWORK"]?.stringValue ?? "testnet"
}
}
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import PackageDescription
let exampleTargets = [
"AccountAlias",
"AccountAllowance",
"AddNftAllowance",
"ConsensusPubSub",
"ConsensusPubSubChunked",
"ConsensusPubSubWithSubmitKey",
Expand Down

0 comments on commit 8e27288

Please sign in to comment.