Skip to content

Commit

Permalink
Merge branch 'main' into 00384-implement-tokenrevokekyctransaction
Browse files Browse the repository at this point in the history
Signed-off-by: Rob Walworth <robert.walworth@swirldslabs.com>
  • Loading branch information
rwalworth committed Jul 12, 2023
2 parents 9d697ab + 3498b48 commit 05c83eb
Show file tree
Hide file tree
Showing 16 changed files with 1,319 additions and 35 deletions.
184 changes: 162 additions & 22 deletions sdk/examples/TransferTokensExample.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,18 @@
* limitations under the License.
*
*/
#include "AccountCreateTransaction.h"
#include "AccountDeleteTransaction.h"
#include "AccountId.h"
#include "Client.h"
#include "ED25519PrivateKey.h"
#include "Status.h"
#include "TokenAssociateTransaction.h"
#include "TokenCreateTransaction.h"
#include "TokenDeleteTransaction.h"
#include "TokenGrantKycTransaction.h"
#include "TokenId.h"
#include "TokenWipeTransaction.h"
#include "TransactionRecord.h"
#include "TransactionResponse.h"
#include "TransferTransaction.h"
Expand All @@ -30,39 +39,170 @@ using namespace Hedera;

int main(int argc, char** argv)
{
if (argc < 5)
if (argc < 3)
{
std::cout
<< "Please input operator account ID, operator private key, token ID to transfer, and account ID to transfer to"
<< std::endl;
std::cout << "Please input account ID and private key" << std::endl;
return 1;
}

// 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.
const AccountId operatorAccountId = AccountId::fromString(argv[1]);
const std::shared_ptr<PrivateKey> operatorKey = ED25519PrivateKey::fromString(argv[2]);

Client client = Client::forTestnet();
const AccountId operatorId = AccountId::fromString(argv[1]);
client.setOperator(operatorId, ED25519PrivateKey::fromString(argv[2]).get());
const TokenId tokenId = TokenId::fromString(argv[3]);
const AccountId recipientId = AccountId::fromString(argv[4]);
client.setOperator(operatorAccountId, operatorKey.get());

const int64_t amount = 10LL;
// Generate two accounts.
const std::unique_ptr<PrivateKey> privateKey1 = ED25519PrivateKey::generatePrivateKey();
const std::unique_ptr<PrivateKey> privateKey2 = ED25519PrivateKey::generatePrivateKey();

TransactionResponse txResponse = TransferTransaction()
.addTokenTransfer(tokenId, operatorId, -amount)
.addTokenTransfer(tokenId, recipientId, amount)
.execute(client);
const AccountId accountId1 = AccountCreateTransaction()
.setKey(privateKey1.get())
.setInitialBalance(Hbar(1LL))
.execute(client)
.getReceipt(client)
.mAccountId.value();
std::cout << "Generated account with account ID " << accountId1.toString() << std::endl;

TransactionRecord txRecord = txResponse.getRecord(client);
const AccountId accountId2 = AccountCreateTransaction()
.setKey(privateKey2.get())
.setInitialBalance(Hbar(1LL))
.execute(client)
.getReceipt(client)
.mAccountId.value();
std::cout << "Generated account with account ID " << accountId2.toString() << std::endl;

std::cout << "List of token transfers received in TransactionRecord:" << std::endl;
for (const TokenTransfer& transfer : txRecord.mTokenTransferList)
{
std::cout << "---TRANSFER---" << std::endl;
std::cout << " - Token ID: " << transfer.getTokenId().toString() << std::endl;
std::cout << " - Account ID: " << transfer.getAccountId().toString() << std::endl;
std::cout << " - Amount: " << transfer.getAmount() << std::endl;
}
// Create a token to transfer.
const TokenId tokenId = TokenCreateTransaction()
.setTokenName("ffff")
.setTokenSymbol("F")
.setTreasuryAccountId(operatorAccountId)
.setInitialSupply(100000ULL)
.setAdminKey(operatorKey)
.setKycKey(operatorKey)
.setWipeKey(operatorKey)
.execute(client)
.getReceipt(client)
.mTokenId.value();
std::cout << "Generated token with token ID " << tokenId.toString() << std::endl;

// Associate the token with the two accounts.
std::cout << "Associate the token with account 1: "
<< gStatusToString.at(TokenAssociateTransaction()
.setAccountId(accountId1)
.setTokenIds({ tokenId })
.freezeWith(client)
.sign(privateKey1.get())
.execute(client)
.getReceipt(client)
.mStatus)
<< std::endl;

std::cout << "Associate the token with account 2: "
<< gStatusToString.at(TokenAssociateTransaction()
.setAccountId(accountId2)
.setTokenIds({ tokenId })
.freezeWith(client)
.sign(privateKey2.get())
.execute(client)
.getReceipt(client)
.mStatus)
<< std::endl;

// Grant KYC to these accounts for this token.
std::cout << "Grant KYC to account 1 for the token: "
<< gStatusToString.at(TokenGrantKycTransaction()
.setAccountId(accountId1)
.setTokenId(tokenId)
.freezeWith(client)
.sign(privateKey1.get())
.execute(client)
.getReceipt(client)
.mStatus)
<< std::endl;

std::cout << "Grant KYC to account 2 for the token: "
<< gStatusToString.at(TokenGrantKycTransaction()
.setAccountId(accountId2)
.setTokenId(tokenId)
.freezeWith(client)
.sign(privateKey2.get())
.execute(client)
.getReceipt(client)
.mStatus)
<< std::endl;

// Send ten tokens to account 1.
std::cout << "Send 10 tokens from the treasury to account 1: "
<< gStatusToString.at(TransferTransaction()
.addTokenTransfer(tokenId, operatorAccountId, -10LL)
.addTokenTransfer(tokenId, accountId1, 10LL)
.execute(client)
.getReceipt(client)
.mStatus)
<< std::endl;

// Send the ten tokens from account 1 to account 2.
std::cout << "Send 10 tokens from account 1 to account 2: "
<< gStatusToString.at(TransferTransaction()
.addTokenTransfer(tokenId, accountId1, -10LL)
.addTokenTransfer(tokenId, accountId2, 10LL)
.freezeWith(client)
.sign(privateKey1.get())
.execute(client)
.getReceipt(client)
.mStatus)
<< std::endl;

// Send the ten tokens from account 2 back to account 1.
std::cout << "Send 10 tokens from account 2 back to account 1: "
<< gStatusToString.at(TransferTransaction()
.addTokenTransfer(tokenId, accountId1, 10LL)
.addTokenTransfer(tokenId, accountId2, -10LL)
.freezeWith(client)
.sign(privateKey2.get())
.execute(client)
.getReceipt(client)
.mStatus)
<< std::endl;

// Wipe the account of the 10 tokens.
std::cout << "Wipe the 10 tokens from account 1: "
<< gStatusToString.at(TokenWipeTransaction()
.setTokenId(tokenId)
.setAccountId(accountId1)
.setAmount(10LL)
.execute(client)
.getReceipt(client)
.mStatus)
<< std::endl;

// Delete the tokens and the accounts.
std::cout << "Delete the token: "
<< gStatusToString.at(
TokenDeleteTransaction().setTokenId(tokenId).execute(client).getReceipt(client).mStatus)
<< std::endl;
std::cout << "Delete account 1: "
<< gStatusToString.at(AccountDeleteTransaction()
.setTransferAccountId(operatorAccountId)
.setDeleteAccountId(accountId1)
.freezeWith(client)
.sign(privateKey1.get())
.execute(client)
.getReceipt(client)
.mStatus)
<< std::endl;
std::cout << "Delete account 2: "
<< gStatusToString.at(AccountDeleteTransaction()
.setTransferAccountId(operatorAccountId)
.setDeleteAccountId(accountId2)
.freezeWith(client)
.sign(privateKey2.get())
.execute(client)
.getReceipt(client)
.mStatus)
<< std::endl;

return 0;
}
1 change: 1 addition & 0 deletions sdk/main/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ add_library(${PROJECT_NAME} STATIC
src/TokenDeleteTransaction.cc
src/TokenDissociateTransaction.cc
src/TokenFeeScheduleUpdateTransaction.cc
src/TokenGrantKycTransaction.cc
src/TokenId.cc
src/TokenInfo.cc
src/TokenInfoQuery.cc
Expand Down
149 changes: 149 additions & 0 deletions sdk/main/include/TokenGrantKycTransaction.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*-
*
* Hedera C++ SDK
*
* Copyright (C) 2020 - 2022 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.
*
*/
#ifndef HEDERA_SDK_CPP_TOKEN_GRANT_KYC_TRANSACTION_H_
#define HEDERA_SDK_CPP_TOKEN_GRANT_KYC_TRANSACTION_H_

#include "AccountId.h"
#include "TokenId.h"
#include "Transaction.h"

#include <optional>
#include <vector>

namespace proto
{
class TokenGrantKycTransactionBody;
class TransactionBody;
}

namespace Hedera
{
/**
* Grants KYC to the Hedera accounts for the given Hedera token. This transaction must be signed by the token's KYC Key.
*
* - If the provided account is not found, the transaction will resolve to INVALID_ACCOUNT_ID.
* - If the provided account has been deleted, the transaction will resolve to ACCOUNT_DELETED.
* - If the provided token is not found, the transaction will resolve to INVALID_TOKEN_ID.
* - If the provided token has been deleted, the transaction will resolve to TOKEN_WAS_DELETED.
* - If an association between the provided token and account is not found, the transaction will resolve to
* TOKEN_NOT_ASSOCIATED_TO_ACCOUNT.
* - If no KYC Key is defined, the transaction will resolve to TOKEN_HAS_NO_KYC_KEY.
*
* Once executed the Account is marked as KYC Granted.
*
* Transaction Signing Requirements:
* - KYC key.
* - Transaction fee payer account key.
*/
class TokenGrantKycTransaction : public Transaction<TokenGrantKycTransaction>
{
public:
TokenGrantKycTransaction() = default;

/**
* Construct from a TransactionBody protobuf object.
*
* @param transactionBody The TransactionBody protobuf object from which to construct.
* @throws std::invalid_argument If the input TransactionBody does not represent a TokenGrantKyc transaction.
*/
explicit TokenGrantKycTransaction(const proto::TransactionBody& transactionBody);

/**
* Set the ID of the account to have passed KYC for this token.
*
* @param accountId The ID of the account to have passed KYC for this token.
* @return A reference to this TokenGrantKycTransaction object with the newly-set account ID.
* @throws IllegalStateException If this TokenGrantKycTransaction is frozen.
*/
TokenGrantKycTransaction& setAccountId(const AccountId& accountId);

/**
* Set the ID of the token for which the account has passed KYC.
*
* @param tokenId The ID of the token for which the account has passed KYC.
* @return A reference to this TokenGrantKycTransaction object with the newly-set token ID.
* @throws IllegalStateException If this TokenGrantKycTransaction is frozen.
*/
TokenGrantKycTransaction& setTokenId(const TokenId& tokenId);

/**
* Get the ID of the account to have passed KYC for this token.
*
* @return The ID of the account to have passed KYC for this token.
*/
[[nodiscard]] inline AccountId getAccountId() const { return mAccountId; }

/**
* Get the ID of the token for which the account has passed KYC.
*
* @return The ID of the token for which the account has passed KYC.
*/
[[nodiscard]] inline TokenId getTokenId() const { return mTokenId; }

private:
/**
* Derived from Executable. Construct a Transaction protobuf object from this TokenGrantKycTransaction object.
*
* @param client The Client trying to construct this TokenGrantKycTransaction.
* @param node The Node to which this TokenGrantKycTransaction will be sent. This is unused.
* @return A Transaction protobuf object filled with this TokenGrantKycTransaction object's data.
* @throws UninitializedException If the input client has no operator with which to sign this
* TokenGrantKycTransaction.
*/
[[nodiscard]] proto::Transaction makeRequest(const Client& client,
const std::shared_ptr<internal::Node>& /*node*/) const override;

/**
* Derived from Executable. Submit this TokenGrantKycTransaction to a Node.
*
* @param client The Client submitting this TokenGrantKycTransaction.
* @param deadline The deadline for submitting this TokenGrantKycTransaction.
* @param node Pointer to the Node to which this TokenGrantKycTransaction should be submitted.
* @param response Pointer to the TransactionResponse protobuf object that gRPC should populate with the response
* information from the gRPC server.
* @return The gRPC status of the submission.
*/
[[nodiscard]] grpc::Status submitRequest(const Client& client,
const std::chrono::system_clock::time_point& deadline,
const std::shared_ptr<internal::Node>& node,
proto::TransactionResponse* response) const override;

/**
* Build a TokenGrantKycTransactionBody protobuf object from this TokenGrantKycTransaction object.
*
* @return A pointer to a TokenGrantKycTransactionBody protobuf object filled with this TokenGrantKycTransaction
* object's data.
*/
[[nodiscard]] proto::TokenGrantKycTransactionBody* build() const;

/**
* The ID of the account to have passed KYC for this token.
*/
AccountId mAccountId;

/**
* The ID of the token for which the account has passed KYC.
*/
TokenId mTokenId;
};

} // namespace Hedera

#endif // HEDERA_SDK_CPP_TOKEN_GRANT_KYC_TRANSACTION_H_
2 changes: 2 additions & 0 deletions sdk/main/include/Transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class TokenCreateTransaction;
class TokenDeleteTransaction;
class TokenDissociateTransaction;
class TokenFeeScheduleUpdateTransaction;
class TokenGrantKycTransaction;
class TokenMintTransaction;
class TokenRevokeKycTransaction;
class TokenUpdateTransaction;
Expand Down Expand Up @@ -140,6 +141,7 @@ class Transaction
TokenBurnTransaction,
TokenDissociateTransaction,
TokenFeeScheduleUpdateTransaction,
TokenGrantKycTransaction,
TokenRevokeKycTransaction>>
fromBytes(const std::vector<std::byte>& bytes);

Expand Down
Loading

0 comments on commit 05c83eb

Please sign in to comment.