Skip to content

Commit

Permalink
feat(HIPs-646/657/765): Added implementations for metadata fields (#678)
Browse files Browse the repository at this point in the history
Signed-off-by: gsstoykov <[email protected]>
Co-authored-by: Rob Walworth <[email protected]>
  • Loading branch information
gsstoykov and rwalworth authored Apr 26, 2024
1 parent 1c2110b commit 4636d7a
Show file tree
Hide file tree
Showing 27 changed files with 1,438 additions and 91 deletions.
3 changes: 3 additions & 0 deletions src/sdk/examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ set(SIGN_TRANSACTION_EXAMPLE_NAME ${PROJECT_NAME}-sign-transaction-example)
set(SOLIDITY_PRECOMPILE_EXAMPLE_NAME ${PROJECT_NAME}-solidity-precompile-example)
set(STAKING_EXAMPLE_NAME ${PROJECT_NAME}-staking-example)
set(STAKING_WITH_UPDATE_EXAMPLE_NAME ${PROJECT_NAME}-staking-with-update-example)
set(TOKEN_METADATA_EXAMPLE_NAME ${PROJECT_NAME}-token-metadata-example)
set(TOPIC_WITH_ADMIN_KEY_EXAMPLE_NAME ${PROJECT_NAME}-topic-with-admin-key-example)
set(TRANSFER_CRYPTO_EXAMPLE_NAME ${PROJECT_NAME}-transfer-crypto-example)
set(TRANSFER_TOKENS_EXAMPLE_NAME ${PROJECT_NAME}-transfer-tokens-example)
Expand Down Expand Up @@ -98,6 +99,7 @@ add_executable(${SIGN_TRANSACTION_EXAMPLE_NAME} SignTransactionExample.cc)
add_executable(${SOLIDITY_PRECOMPILE_EXAMPLE_NAME} SolidityPrecompileExample.cc)
add_executable(${STAKING_EXAMPLE_NAME} StakingExample.cc)
add_executable(${STAKING_WITH_UPDATE_EXAMPLE_NAME} StakingWithUpdateExample.cc)
add_executable(${TOKEN_METADATA_EXAMPLE_NAME} TokenMetadataExample.cc)
add_executable(${TOPIC_WITH_ADMIN_KEY_EXAMPLE_NAME} TopicWithAdminKeyExample.cc)
add_executable(${TRANSFER_CRYPTO_EXAMPLE_NAME} TransferCryptoExample.cc)
add_executable(${TRANSFER_TOKENS_EXAMPLE_NAME} TransferTokensExample.cc)
Expand Down Expand Up @@ -170,6 +172,7 @@ target_link_libraries(${SIGN_TRANSACTION_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
target_link_libraries(${SOLIDITY_PRECOMPILE_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
target_link_libraries(${STAKING_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
target_link_libraries(${STAKING_WITH_UPDATE_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
target_link_libraries(${TOKEN_METADATA_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
target_link_libraries(${TOPIC_WITH_ADMIN_KEY_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
target_link_libraries(${TRANSFER_CRYPTO_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
target_link_libraries(${TRANSFER_TOKENS_EXAMPLE_NAME} PUBLIC ${PROJECT_NAME})
Expand Down
163 changes: 163 additions & 0 deletions src/sdk/examples/TokenMetadataExample.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/*-
*
* Hedera C++ SDK
*
* Copyright (C) 2020 - 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.
*
*/
#include "AccountCreateTransaction.h"
#include "AccountDeleteTransaction.h"
#include "AccountId.h"
#include "Client.h"
#include "ED25519PrivateKey.h"
#include "PrivateKey.h"
#include "TokenCreateTransaction.h"
#include "TokenDeleteTransaction.h"
#include "TokenInfo.h"
#include "TokenInfoQuery.h"
#include "TokenUpdateTransaction.h"
#include "TransactionReceipt.h"
#include "TransactionResponse.h"
#include "exceptions/PrecheckStatusException.h"
#include "exceptions/ReceiptStatusException.h"
#include "impl/Utilities.h"

#include <dotenv.h>
#include <iostream>

using namespace Hedera;

int main(int argc, char** argv)
{
if (argc < 2)
{
std::cerr << "Please provide a parameter -ft, -nft for creating a fungible or non-fungible token" << std::endl;
return 0;
}

TokenType tokenType;
if (strcmp(argv[1], "-ft") == 0)
{
tokenType = TokenType::FUNGIBLE_COMMON;
}
else if (strcmp(argv[1], "-nft") == 0)
{
tokenType = TokenType::NON_FUNGIBLE_UNIQUE;
}
else
{
std::cerr << "Please provide a parameter -ft, -nft for creating a fungible or non-fungible token" << std::endl;
return 0;
}

dotenv::init();
const AccountId operatorAccountId = AccountId::fromString(std::getenv("OPERATOR_ID"));
const std::shared_ptr<PrivateKey> operatorPrivateKey = ED25519PrivateKey::fromString(std::getenv("OPERATOR_KEY"));

// Get a client for the Hedera testnet, and set 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 client = Client::forTestnet();
client.setOperator(operatorAccountId, operatorPrivateKey);

// Metadata values for create/update
const std::vector<std::byte> initialMetadata = { std::byte(0xAA), std::byte(0xAB), std::byte(0xAC), std::byte(0xAD) };
const std::vector<std::byte> updatedMetadata = { std::byte(0xBA), std::byte(0xBB), std::byte(0xBC), std::byte(0xBD) };

// Create a FT/NFT with metadata and admin key
TokenId mutableTokenId = TokenCreateTransaction()
.setTokenName("ffff")
.setTokenSymbol("F")
.setDecimals(3)
.setInitialSupply(100000)
.setTokenType(tokenType)
.setMetadata(initialMetadata)
.setTreasuryAccountId(operatorAccountId)
.setAdminKey(operatorPrivateKey)
.freezeWith(&client)
.sign(operatorPrivateKey)
.execute(client)
.getReceipt(client)
.mTokenId.value();

std::cout << "Created a mutable token " << mutableTokenId.toString() << " with metadata: " << std::endl;

for (std::byte b : initialMetadata)
{
std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(b) << " ";
}
std::cout << std::endl;

// Update the token metadata
TransactionReceipt txReceipt = TokenUpdateTransaction()
.setTokenId(mutableTokenId)
.setMetadata(updatedMetadata)
.freezeWith(&client)
.sign(operatorPrivateKey)
.execute(client)
.getReceipt(client);

std::cout << "Updated mutable token " << mutableTokenId.toString() << " metadata:" << std::endl;

TokenInfo tokenInfo = TokenInfoQuery().setTokenId(mutableTokenId).execute(client);

for (std::byte b : tokenInfo.mMetadata)
{
std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(b) << " ";
}
std::cout << std::endl;

std::shared_ptr<PrivateKey> metadataKey = ED25519PrivateKey::generatePrivateKey();

// Create an immutable FT/NFT with metadata and metadata key
TokenId immutableTokenId = TokenCreateTransaction()
.setTokenName("ffff")
.setTokenSymbol("F")
.setDecimals(3)
.setInitialSupply(100000)
.setTokenType(tokenType)
.setMetadata(initialMetadata)
.setTreasuryAccountId(operatorAccountId)
.setMetadataKey(metadataKey)
.execute(client)
.getReceipt(client)
.mTokenId.value();

std::cout << "Created a immutable token " << immutableTokenId.toString() << " with metadata: " << std::endl;

for (std::byte b : initialMetadata)
{
std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(b) << " ";
}
std::cout << std::endl;

// Update the token metadata
txReceipt = TokenUpdateTransaction()
.setTokenId(immutableTokenId)
.setMetadata(updatedMetadata)
.freezeWith(&client)
.sign(metadataKey)
.execute(client)
.getReceipt(client);

std::cout << "Updated immutable token " << immutableTokenId.toString() << " metadata:" << std::endl;

tokenInfo = TokenInfoQuery().setTokenId(immutableTokenId).execute(client);

for (std::byte b : tokenInfo.mMetadata)
{
std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(b) << " ";
}
std::cout << std::endl;
}
1 change: 1 addition & 0 deletions src/sdk/main/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ add_library(${PROJECT_NAME} STATIC
src/TokenType.cc
src/TokenUnfreezeTransaction.cc
src/TokenUnpauseTransaction.cc
src/TokenUpdateNftsTransaction.cc
src/TokenUpdateTransaction.cc
src/TokenWipeTransaction.cc
src/TopicCreateTransaction.cc
Expand Down
3 changes: 2 additions & 1 deletion src/sdk/main/include/RequestType.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ enum class RequestType
GET_ACCOUNT_DETAILS,
ETHEREUM_TRANSACTION,
NODE_STAKE_UPDATE,
UTIL_PRNG
UTIL_PRNG,
TOKEN_UPDATE_NFTS
};

/**
Expand Down
18 changes: 17 additions & 1 deletion src/sdk/main/include/Status.h
Original file line number Diff line number Diff line change
Expand Up @@ -1501,7 +1501,23 @@ enum class Status
/**
* An alias that is assigned to an account or contract cannot be assigned to another account or contract.
*/
ALIAS_ALREADY_ASSIGNED
ALIAS_ALREADY_ASSIGNED,

/**
* A provided metadata key was invalid. Verification includes, for example, checking the size of Ed25519 and
* ECDSA(secp256k1) public keys.
*/
INVALID_METADATA_KEY,

/**
* Token Metadata is not provided
*/
MISSING_TOKEN_METADATA,

/**
* NFT serial numbers are missing in the TokenUpdateNftsTransactionBody
*/
MISSING_SERIAL_NUMBERS
};

/**
Expand Down
40 changes: 40 additions & 0 deletions src/sdk/main/include/TokenCreateTransaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,22 @@ class TokenCreateTransaction : public Transaction<TokenCreateTransaction>
*/
TokenCreateTransaction& setPauseKey(const std::shared_ptr<Key>& key);

/**
* Set the desired metadata for the new token.
*
* @param metadata The desired metadata for the new token.
* @return A reference to this TokenCreateTransaction with the newly-set metadata.
*/
TokenCreateTransaction& setMetadata(const std::vector<std::byte>& metadata);

/**
* Set the desired metadata key for the new token.
*
* @param key The desired metadata key for the new token.
* @return A reference to this TokenCreateTransaction with the newly-set metadata key.
*/
TokenCreateTransaction& setMetadataKey(const std::shared_ptr<Key>& metadataKey);

/**
* Get the desired name for the new token.
*
Expand Down Expand Up @@ -399,6 +415,20 @@ class TokenCreateTransaction : public Transaction<TokenCreateTransaction>
*/
[[nodiscard]] inline std::shared_ptr<Key> getPauseKey() const { return mPauseKey; }

/**
* Get the desired metadata for the new token.
*
* @return The desired metadata for the new token.
*/
[[nodiscard]] inline std::vector<std::byte> getMetadata() const { return mMetadata; }

/**
* Get the desired metadata key for the new token.
*
* @return The desired metadata key for the new token.
*/
[[nodiscard]] inline std::shared_ptr<Key> getMetadataKey() const { return mMetadataKey; }

private:
friend class WrappedTransaction;

Expand Down Expand Up @@ -568,6 +598,16 @@ class TokenCreateTransaction : public Transaction<TokenCreateTransaction>
* PauseNotApplicable, otherwise Unpaused.
*/
std::shared_ptr<Key> mPauseKey = nullptr;

/**
* Metadata of the created token definition.
*/
std::vector<std::byte> mMetadata;

/**
* The key which can change the metadata of a token (token definition, partition definition, and individual NFTs).
*/
std::shared_ptr<Key> mMetadataKey = nullptr;
};

} // namespace Hedera
Expand Down
10 changes: 10 additions & 0 deletions src/sdk/main/include/TokenInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,16 @@ class TokenInfo
* The ID of the ledger from which this response was returned.
*/
LedgerId mLedgerId;

/**
* Represents the metadata of the token definition.
*/
std::vector<std::byte> mMetadata;

/**
* The key which can change the metadata of a token (token definition and individual NFTs).
*/
std::shared_ptr<Key> mMetadataKey = nullptr;
};

} // namespace Hedera
Expand Down
Loading

0 comments on commit 4636d7a

Please sign in to comment.