Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add metadata field for tokens #331

Merged
merged 23 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
chore: update NftMetadata & TokenMetadata examples
Signed-off-by: Ricky Saechao <[email protected]>
  • Loading branch information
RickyLB committed Apr 24, 2024
commit ab77d2d37e74eff2091bf1782739b8dd8a11f164
199 changes: 199 additions & 0 deletions Examples/NftUpdateMetadata/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
/*
* ‌
* Hedera Swift SDK
*
* Copyright (C) 2022 - 2023 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 {

private static let metadataKey: PrivateKey = PrivateKey.generateEd25519()
private static let initialMetadata: Data = Data([1])
private static let newMetadata: Data = Data([1, 2])

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

// Defaults the operator account ID and key such that all generated transactions will be paid for
// by this account and be signed by this key
client.setOperator(env.operatorAccountId, env.operatorKey)

// Demonstrate with an immutable token (without admin key)
try await updateNftsMetadata(client, env, try getMutableTokenCreateTransaction(client, env))

// Demonstrate with an immutable token (without admin key)
try await updateNftsMetadata(client, env, try getImmutableTokenCreateTransaction(client, env))

}

internal static func updateNftsMetadata(
_ client: Client, _ env: Environment, _ tokenCreateTransaction: TokenCreateTransaction
) async throws {
let tokenCreateResponse = try await tokenCreateTransaction.sign(env.operatorKey).execute(client)
let tokenCreateReceipt = try await tokenCreateResponse.getReceipt(client)

print("Status of token create transaction: \(tokenCreateReceipt.status)")

let tokenId = tokenCreateReceipt.tokenId!
print("Token id: \(tokenId)")

let tokenInfo = try await TokenInfoQuery()
.tokenId(tokenId)
.execute(client)

print("Token metadata key: \(tokenInfo.metadataKey!)")

let tokenMintTransaction = TokenMintTransaction()
.tokenId(tokenId)
.metadata([initialMetadata])

_ = tokenMintTransaction.metadata.map {
print("Set metadata: \($0.bytes)")
}

let tokenMintReceipt = try await tokenMintTransaction.execute(client).getReceipt(client)

print("Status of token mint transaction: \(tokenMintReceipt.status)")

let nftSerials = tokenMintReceipt.serials!

let accountCreateTransaction = try await AccountCreateTransaction()
.key(.single(env.operatorKey.publicKey))
.maxAutomaticTokenAssociations(10)
.execute(client)

// Create an account to send the NFT to
let newAccountId = try await accountCreateTransaction.getReceipt(client).accountId!

// Associate the NFT to the account only if the account does not have any automatic token association slots open. When we created the account in the previous step we set automatic token associations to 10 so I should have 10 slots open to receive a token
// _ = try await TokenAssociateTransaction()
// .accountId(newAccountId)
// .tokenIds([tokenId])
// .freezeWith(client)
// .sign(env.operatorKey)
// .execute(client)
// .getReceipt(client)

// Transfer the Nft to the new account
_ = try await TransferTransaction()
.nftTransfer(tokenId.nft(nftSerials.first!), env.operatorAccountId, newAccountId)
.execute(client)

// Update metadata of Nft
let tokenUpdateNftsTransaction = try TokenUpdateNftsTransaction()
.tokenId(tokenId)
.serials(nftSerials)
.metadata(newMetadata)
.freezeWith(client)

print("Updated metadata: \(tokenUpdateNftsTransaction.metadata.bytes)")

// Get receipt for update nfts metadata transaction
let tokenUpdateNftsReceipt = try await tokenUpdateNftsTransaction.sign(metadataKey).execute(client).getReceipt(
client)

print("Status of token update nfts metadata transaction: \(tokenUpdateNftsReceipt.status)")

// Check that metadata for the NFT was updated correctly
let metadataList = try await getMetadataList(client, tokenId, nftSerials)

print("Metadata after update: \(metadataList)")
}

internal static func getMutableTokenCreateTransaction(_ client: Client, _ env: Environment) throws
-> TokenCreateTransaction
{
print("Creating a mutable token")

return try TokenCreateTransaction()
.name("Mutable")
.symbol("MUT")
.tokenType(TokenType.nonFungibleUnique)
.treasuryAccountId(env.operatorAccountId)
.adminKey(.single(env.operatorKey.publicKey))
.supplyKey(.single(env.operatorKey.publicKey))
.metadataKey(.single(metadataKey.publicKey))
.expirationTime(Timestamp.now + .hours(2))
.freezeWith(client)
}

internal static func getImmutableTokenCreateTransaction(_ client: Client, _ env: Environment) throws
-> TokenCreateTransaction
{
print("Creating a immutable token")

return try TokenCreateTransaction()
.name("Immutable")
.symbol("IMMUT")
.tokenType(TokenType.nonFungibleUnique)
.treasuryAccountId(env.operatorAccountId)
.supplyKey(.single(env.operatorKey.publicKey))
.metadataKey(.single(metadataKey.publicKey))
.expirationTime(Timestamp.now + .hours(2))
.freezeWith(client)
}
}

func getMetadataList(_ client: Client, _ tokenId: TokenId, _ serials: [UInt64]) async throws -> [Data] {
let metadataList: [Data] = try await withThrowingTaskGroup(
of: Data.self,
body: { group in
var results = [Data]()

// Iterate over serials, launching a new task for each
for serial in serials {
group.addTask {
let nftId = NftId(tokenId: tokenId, serial: UInt64(serial))
// Execute the query and return the result
return try await TokenNftInfoQuery().nftId(nftId).execute(client).metadata
}
}

// Collect results from all tasks
for try await result in group {
results.append(result)
}

return results
})

return metadataList
}

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 be ran against.
///
/// Testnet by default.
internal var networkName: String {
self["HEDERA_NETWORK"]?.stringValue ?? "testnet"
}
}
153 changes: 153 additions & 0 deletions Examples/TokenUpdateMetadata/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* ‌
* 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 {

private static let metadataKey: PrivateKey = PrivateKey.generateEd25519()
private static let initialMetadata: Data = Data([1])
private static let newMetadata: Data = Data([1, 2])

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

// Defaults the operator account ID and key such that all generated transactions will be paid for
// by this account and be signed by this key
client.setOperator(env.operatorAccountId, env.operatorKey)

// Demonstrate with an mutable token (with admin key)
try await updateMutableTokenMetadata(client, env)

// Demonstrate with an immutable token (with metadata key)
try await updateImmutableTokenMetadata(client, env)

}

internal static func updateMutableTokenMetadata(_ client: Client, _ env: Environment) async throws {
print("Creating a mutable token")

let tokenId = try await TokenCreateTransaction()
.name("ffff")
.symbol("F")
.tokenType(TokenType.fungibleCommon) // The same flow can be executed with a TokenType.NON_FUNGIBLE_UNIQUE (i.e. HIP-765)
.decimals(3)
.initialSupply(1_000_000)
.treasuryAccountId(env.operatorAccountId)
.adminKey(.single(env.operatorKey.publicKey))
.metadata(initialMetadata)
.expirationTime(Timestamp.now + .hours(2))
.execute(client)
.getReceipt(client)
.tokenId!

print("Created a mutable token: \(tokenId)")

let tokenInfoAfterCreation = try await TokenInfoQuery()
.tokenId(tokenId)
.execute(client)

print("Mutable token's metadata after creation: \(tokenInfoAfterCreation.metadata.bytes)")

// Update token's metadata
_ = try await TokenUpdateTransaction()
.tokenId(tokenId)
.metadata(newMetadata)
.execute(client)
.getReceipt(client)

let tokenInfoAfterMetadataUpdate = try await TokenInfoQuery()
.tokenId(tokenId)
.execute(client)

// check that metadata was updated correctly
print("Mutable token's metadata after update: \(tokenInfoAfterMetadataUpdate.metadata.bytes)")
}

internal static func updateImmutableTokenMetadata(_ client: Client, _ env: Environment) async throws {
print("Creating a immutable token")

let metadataKey: PrivateKey = PrivateKey.generateEd25519()

let tokenId = try await TokenCreateTransaction()
.name("ffff")
.symbol("F")
.tokenType(TokenType.fungibleCommon) // The same flow can be executed with a TokenType.NON_FUNGIBLE_UNIQUE (i.e. HIP-765)
.treasuryAccountId(env.operatorAccountId)
.decimals(3)
.initialSupply(100000)
.metadataKey(.single(metadataKey.publicKey))
.expirationTime(Timestamp.now + .hours(2))
.metadata(initialMetadata)
.execute(client)
.getReceipt(client)
.tokenId!

print("Created a immutable token: \(tokenId)")

let tokenInfoAfterCreation = try await TokenInfoQuery()
.tokenId(tokenId)
.execute(client)

print("tokenInfo metadatakey: \(tokenInfoAfterCreation.metadataKey!)")

// check that metadata was set correctly
print("Immutable token's metadata after creation: \(tokenInfoAfterCreation.metadata.bytes)")

// Update token's metadata
_ = try await TokenUpdateTransaction()
.tokenId(tokenId)
.metadata(newMetadata)
.freezeWith(client)
.sign(metadataKey)
.execute(client)
.getReceipt(client)

let tokenInfoAfterMetadataUpdate = try await TokenInfoQuery()
.tokenId(tokenId)
.execute(client)

// check that metadata was updated correctly
print("Immutable token's metadata after update: \(tokenInfoAfterMetadataUpdate.metadata.bytes)")
}
}

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 be ran against.
///
/// Testnet by default.
internal var networkName: String {
self["HEDERA_NETWORK"]?.stringValue ?? "testnet"
}
}
Loading