diff --git a/channel.go b/channel.go index 5e79c3422..10cda8b5e 100644 --- a/channel.go +++ b/channel.go @@ -12,6 +12,7 @@ type channel struct { topic proto.ConsensusServiceClient freeze proto.FreezeServiceClient network proto.NetworkServiceClient + token proto.TokenServiceClient client *grpc.ClientConn } @@ -68,3 +69,11 @@ func (channel channel) getNetwork() proto.NetworkServiceClient { return channel.network } + +func (channel channel) getToken() proto.TokenServiceClient { + if channel.token == nil { + channel.token = proto.NewTokenServiceClient(channel.client) + } + + return channel.token +} diff --git a/token_associate_transaction.go b/token_associate_transaction.go new file mode 100644 index 000000000..1678773e8 --- /dev/null +++ b/token_associate_transaction.go @@ -0,0 +1,241 @@ +package hedera + +import ( + "time" + + "github.com/hashgraph/hedera-sdk-go/proto" +) + +// Associates the provided account with the provided tokens. Must be signed by the provided Account's 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 any of the provided tokens is not found, the transaction will resolve to +// INVALID_TOKEN_REF. +// If any of the provided tokens has been deleted, the transaction will resolve to +// TOKEN_WAS_DELETED. +// If an association between the provided account and any of the tokens already exists, the +// transaction will resolve to +// TOKEN_ALREADY_ASSOCIATED_TO_ACCOUNT. +// If the provided account's associations count exceed the constraint of maximum token +// associations per account, the transaction will resolve to +// TOKENS_PER_ACCOUNT_LIMIT_EXCEEDED. +// On success, associations between the provided account and tokens are made and the account is +// ready to interact with the tokens. +type TokenAssociateTransaction struct { + Transaction + pb *proto.TokenAssociateTransactionBody +} + +func NewTokenAssociateTransaction() *TokenAssociateTransaction { + pb := &proto.TokenAssociateTransactionBody{} + + transaction := TokenAssociateTransaction{ + pb: pb, + Transaction: newTransaction(), + } + + return &transaction +} + +// The account to be associated with the provided tokens +func (transaction *TokenAssociateTransaction) SetAccountID(accountID AccountID) *TokenAssociateTransaction { + transaction.pb.Account = accountID.toProtobuf() + return transaction +} + +// The tokens to be associated with the provided account +func (transaction *TokenAssociateTransaction) SetTokenIDs(tokenIDs ...TokenID) *TokenAssociateTransaction { + transaction.pb.Tokens = make([]*proto.TokenID, len(tokenIDs)) + + for i, tokenID := range tokenIDs { + transaction.pb.Tokens[i] = tokenID.toProtobuf() + } + + return transaction +} + +// +// The following methods must be copy-pasted/overriden at the bottom of **every** _transaction.go file +// We override the embedded fluent setter methods to return the outer type +// + +func tokenAssociateTransaction_getMethod(request request, channel *channel) method { + return method{ + transaction: channel.getToken().AssociateTokens, + } +} + +func (transaction *TokenAssociateTransaction) IsFrozen() bool { + return transaction.isFrozen() +} + +// Sign uses the provided privateKey to sign the transaction. +func (transaction *TokenAssociateTransaction) Sign( + privateKey PrivateKey, +) *TokenAssociateTransaction { + return transaction.SignWith(privateKey.PublicKey(), privateKey.Sign) +} + +func (transaction *TokenAssociateTransaction) SignWithOperator( + client *Client, +) (*TokenAssociateTransaction, error) { + // If the transaction is not signed by the operator, we need + // to sign the transaction with the operator + + if client.operator == nil { + return nil, errClientOperatorSigning + } + + if !transaction.IsFrozen() { + transaction.FreezeWith(client) + } + + return transaction.SignWith(client.operator.publicKey, client.operator.signer), nil +} + +// SignWith executes the TransactionSigner and adds the resulting signature data to the Transaction's signature map +// with the publicKey as the map key. +func (transaction *TokenAssociateTransaction) SignWith( + publicKey PublicKey, + signer TransactionSigner, +) *TokenAssociateTransaction { + if !transaction.IsFrozen() { + transaction.Freeze() + } + + if transaction.keyAlreadySigned(publicKey) { + return transaction + } + + for index := 0; index < len(transaction.transactions); index++ { + signature := signer(transaction.transactions[index].GetBodyBytes()) + + transaction.signatures[index].SigPair = append( + transaction.signatures[index].SigPair, + publicKey.toSignaturePairProtobuf(signature), + ) + } + + return transaction +} + +// Execute executes the Transaction with the provided client +func (transaction *TokenAssociateTransaction) Execute( + client *Client, +) (TransactionResponse, error) { + if !transaction.IsFrozen() { + transaction.FreezeWith(client) + } + + transactionID := transaction.id + + if !client.GetOperatorID().isZero() && client.GetOperatorID().equals(transactionID.TokenID) { + transaction.SignWith( + client.GetOperatorKey(), + client.operator.signer, + ) + } + + resp, err := execute( + client, + request{ + transaction: &transaction.Transaction, + }, + transaction_shouldRetry, + transaction_makeRequest, + transaction_advanceRequest, + transaction_getNodeId, + tokenAssociateTransaction_getMethod, + transaction_mapResponseStatus, + transaction_mapResponse, + ) + + if err != nil { + return TransactionResponse{}, err + } + + return TransactionResponse{ + TransactionID: transaction.id, + NodeID: resp.transaction.NodeID, + }, nil +} + +func (transaction *TokenAssociateTransaction) onFreeze( + pbBody *proto.TransactionBody, +) bool { + pbBody.Data = &proto.TransactionBody_TokenAssociate{ + TokenAssociate: transaction.pb, + } + + return true +} + +func (transaction *TokenAssociateTransaction) Freeze() (*TokenAssociateTransaction, error) { + return transaction.FreezeWith(nil) +} + +func (transaction *TokenAssociateTransaction) FreezeWith(client *Client) (*TokenAssociateTransaction, error) { + transaction.initFee(client) + if err := transaction.initTransactionID(client); err != nil { + return transaction, err + } + + if !transaction.onFreeze(transaction.pbBody) { + return transaction, nil + } + + return transaction, transaction_freezeWith(&transaction.Transaction, client) +} + +func (transaction *TokenAssociateTransaction) GetMaxTransactionFee() Hbar { + return transaction.Transaction.GetMaxTransactionFee() +} + +// SetMaxTransactionFee sets the max transaction fee for this TokenAssociateTransaction. +func (transaction *TokenAssociateTransaction) SetMaxTransactionFee(fee Hbar) *TokenAssociateTransaction { + transaction.Transaction.SetMaxTransactionFee(fee) + return transaction +} + +func (transaction *TokenAssociateTransaction) GetTransactionMemo() string { + return transaction.Transaction.GetTransactionMemo() +} + +// SetTransactionMemo sets the memo for this TokenAssociateTransaction. +func (transaction *TokenAssociateTransaction) SetTransactionMemo(memo string) *TokenAssociateTransaction { + transaction.Transaction.SetTransactionMemo(memo) + return transaction +} + +func (transaction *TokenAssociateTransaction) GetTransactionValidDuration() time.Duration { + return transaction.Transaction.GetTransactionValidDuration() +} + +// SetTransactionValidDuration sets the valid duration for this TokenAssociateTransaction. +func (transaction *TokenAssociateTransaction) SetTransactionValidDuration(duration time.Duration) *TokenAssociateTransaction { + transaction.Transaction.SetTransactionValidDuration(duration) + return transaction +} + +func (transaction *TokenAssociateTransaction) GetTransactionID() TransactionID { + return transaction.Transaction.GetTransactionID() +} + +// SetTransactionID sets the TransactionID for this TokenAssociateTransaction. +func (transaction *TokenAssociateTransaction) SetTransactionID(transactionID TransactionID) *TokenAssociateTransaction { + transaction.id = transactionID + transaction.Transaction.SetTransactionID(transactionID) + return transaction +} + +func (transaction *TokenAssociateTransaction) GetNodeID() AccountID { + return transaction.Transaction.GetNodeID() +} + +// SetNodeTokenID sets the node TokenID for this TokenAssociateTransaction. +func (transaction *TokenAssociateTransaction) SetNodeAccountID(nodeID AccountID) *TokenAssociateTransaction { + transaction.Transaction.SetNodeAccountID(nodeID) + return transaction +} diff --git a/token_burn_transaction.go b/token_burn_transaction.go new file mode 100644 index 000000000..5ba741411 --- /dev/null +++ b/token_burn_transaction.go @@ -0,0 +1,229 @@ +package hedera + +import ( + "time" + + "github.com/hashgraph/hedera-sdk-go/proto" +) + +// Burns tokens from the Token's treasury Account. If no Supply Key is defined, the transaction +// will resolve to TOKEN_HAS_NO_SUPPLY_KEY. +// The operation decreases the Total Supply of the Token. Total supply cannot go below +// zero. +// The amount provided must be in the lowest denomination possible. Example: +// Token A has 2 decimals. In order to burn 100 tokens, one must provide amount of 10000. In order +// to burn 100.55 tokens, one must provide amount of 10055. +type TokenBurnTransaction struct { + Transaction + pb *proto.TokenBurnTransactionBody +} + +func NewTokenBurnTransaction() *TokenBurnTransaction { + pb := &proto.TokenBurnTransactionBody{} + + transaction := TokenBurnTransaction{ + pb: pb, + Transaction: newTransaction(), + } + + return &transaction +} + +// The token for which to burn tokens. If token does not exist, transaction results in +// INVALID_TOKEN_ID +func (transaction *TokenBurnTransaction) SetTokenID(tokenID TokenID) *TokenBurnTransaction { + transaction.pb.Token = tokenID.toProtobuf() + return transaction +} + +// The amount to burn from the Treasury Account. Amount must be a positive non-zero number, not +// bigger than the token balance of the treasury account (0; balance], represented in the lowest +// denomination. +func (transaction *TokenBurnTransaction) SetAmount(amount uint64) *TokenBurnTransaction { + transaction.pb.Amount = amount + return transaction +} + +// +// The following methods must be copy-pasted/overriden at the bottom of **every** _transaction.go file +// We override the embedded fluent setter methods to return the outer type +// + +func tokenBurnTransaction_getMethod(request request, channel *channel) method { + return method{ + transaction: channel.getToken().BurnToken, + } +} + +func (transaction *TokenBurnTransaction) IsFrozen() bool { + return transaction.isFrozen() +} + +// Sign uses the provided privateKey to sign the transaction. +func (transaction *TokenBurnTransaction) Sign( + privateKey PrivateKey, +) *TokenBurnTransaction { + return transaction.SignWith(privateKey.PublicKey(), privateKey.Sign) +} + +func (transaction *TokenBurnTransaction) SignWithOperator( + client *Client, +) (*TokenBurnTransaction, error) { + // If the transaction is not signed by the operator, we need + // to sign the transaction with the operator + + if client.operator == nil { + return nil, errClientOperatorSigning + } + + if !transaction.IsFrozen() { + transaction.FreezeWith(client) + } + + return transaction.SignWith(client.operator.publicKey, client.operator.signer), nil +} + +// SignWith executes the TransactionSigner and adds the resulting signature data to the Transaction's signature map +// with the publicKey as the map key. +func (transaction *TokenBurnTransaction) SignWith( + publicKey PublicKey, + signer TransactionSigner, +) *TokenBurnTransaction { + if !transaction.IsFrozen() { + transaction.Freeze() + } + + if transaction.keyAlreadySigned(publicKey) { + return transaction + } + + for index := 0; index < len(transaction.transactions); index++ { + signature := signer(transaction.transactions[index].GetBodyBytes()) + + transaction.signatures[index].SigPair = append( + transaction.signatures[index].SigPair, + publicKey.toSignaturePairProtobuf(signature), + ) + } + + return transaction +} + +// Execute executes the Transaction with the provided client +func (transaction *TokenBurnTransaction) Execute( + client *Client, +) (TransactionResponse, error) { + if !transaction.IsFrozen() { + transaction.FreezeWith(client) + } + + transactionID := transaction.id + + if !client.GetOperatorID().isZero() && client.GetOperatorID().equals(transactionID.TokenID) { + transaction.SignWith( + client.GetOperatorKey(), + client.operator.signer, + ) + } + + resp, err := execute( + client, + request{ + transaction: &transaction.Transaction, + }, + transaction_shouldRetry, + transaction_makeRequest, + transaction_advanceRequest, + transaction_getNodeId, + tokenBurnTransaction_getMethod, + transaction_mapResponseStatus, + transaction_mapResponse, + ) + + if err != nil { + return TransactionResponse{}, err + } + + return TransactionResponse{ + TransactionID: transaction.id, + NodeID: resp.transaction.NodeID, + }, nil +} + +func (transaction *TokenBurnTransaction) onFreeze( + pbBody *proto.TransactionBody, +) bool { + pbBody.Data = &proto.TransactionBody_TokenBurn{ + TokenBurn: transaction.pb, + } + + return true +} + +func (transaction *TokenBurnTransaction) Freeze() (*TokenBurnTransaction, error) { + return transaction.FreezeWith(nil) +} + +func (transaction *TokenBurnTransaction) FreezeWith(client *Client) (*TokenBurnTransaction, error) { + transaction.initFee(client) + if err := transaction.initTransactionID(client); err != nil { + return transaction, err + } + + if !transaction.onFreeze(transaction.pbBody) { + return transaction, nil + } + + return transaction, transaction_freezeWith(&transaction.Transaction, client) +} + +func (transaction *TokenBurnTransaction) GetMaxTransactionFee() Hbar { + return transaction.Transaction.GetMaxTransactionFee() +} + +// SetMaxTransactionFee sets the max transaction fee for this TokenBurnTransaction. +func (transaction *TokenBurnTransaction) SetMaxTransactionFee(fee Hbar) *TokenBurnTransaction { + transaction.Transaction.SetMaxTransactionFee(fee) + return transaction +} + +func (transaction *TokenBurnTransaction) GetTransactionMemo() string { + return transaction.Transaction.GetTransactionMemo() +} + +// SetTransactionMemo sets the memo for this TokenBurnTransaction. +func (transaction *TokenBurnTransaction) SetTransactionMemo(memo string) *TokenBurnTransaction { + transaction.Transaction.SetTransactionMemo(memo) + return transaction +} + +func (transaction *TokenBurnTransaction) GetTransactionValidDuration() time.Duration { + return transaction.Transaction.GetTransactionValidDuration() +} + +// SetTransactionValidDuration sets the valid duration for this TokenBurnTransaction. +func (transaction *TokenBurnTransaction) SetTransactionValidDuration(duration time.Duration) *TokenBurnTransaction { + transaction.Transaction.SetTransactionValidDuration(duration) + return transaction +} + +func (transaction *TokenBurnTransaction) GetTransactionID() TransactionID { + return transaction.Transaction.GetTransactionID() +} + +// SetTransactionID sets the TransactionID for this TokenBurnTransaction. +func (transaction *TokenBurnTransaction) SetTransactionID(transactionID TransactionID) *TokenBurnTransaction { + transaction.id = transactionID + transaction.Transaction.SetTransactionID(transactionID) + return transaction +} + +func (transaction *TokenBurnTransaction) GetNodeID() AccountID { + return transaction.Transaction.GetNodeID() +} + +// SetNodeTokenID sets the node TokenID for this TokenBurnTransaction. +func (transaction *TokenBurnTransaction) SetNodeAccountID(nodeID AccountID) *TokenBurnTransaction { + transaction.Transaction.SetNodeAccountID(nodeID) + return transaction +} diff --git a/token_create_transaction.go b/token_create_transaction.go new file mode 100644 index 000000000..107f8feae --- /dev/null +++ b/token_create_transaction.go @@ -0,0 +1,311 @@ +package hedera + +import ( + "time" + + "github.com/hashgraph/hedera-sdk-go/proto" +) + +// Create a new token. After the token is created, the Token ID for it is in the receipt. +// The specified Treasury Account is receiving the initial supply of tokens as-well as the tokens +// from the Token Mint operation once executed. The balance of the treasury account is decreased +// when the Token Burn operation is executed. +// +// The supply that is going to be put in circulation is going to be the initial supply provided. +// The maximum supply a token can have is 2^63-1. +// +// Example: +// Token A has initial supply set to 10_000 and decimals set to 2. The tokens that will be put +// into circulation are going be 100. +// Token B has initial supply set to 10_012_345_678 and decimals set to 8. The number of tokens +// that will be put into circulation are going to be 100.12345678 +// +// Creating immutable token: Token can be created as immutable if the adminKey is omitted. In this +// case, the name, symbol, treasury, management keys, expiry and renew properties cannot be +// updated. If a token is created as immutable, anyone is able to extend the expiry time by paying the fee. +type TokenCreateTransaction struct { + Transaction + pb *proto.TokenCreateTransactionBody +} + +func NewTokenCreateTransaction() *TokenCreateTransaction { + pb := &proto.TokenCreateTransactionBody{} + + transaction := TokenCreateTransaction{ + pb: pb, + Transaction: newTransaction(), + } + + transaction.SetAutoRenewPeriod(7890000) + + return &transaction +} + +// The publicly visible name of the token, specified as a string of only ASCII characters +func (transaction *TokenCreateTransaction) SetName(name string) *TokenCreateTransaction { + transaction.pb.Name = name + return transaction +} + +// The publicly visible token symbol. It is UTF-8 capitalized alphabetical string identifying the token +func (transaction *TokenCreateTransaction) SetSymbol(symbol string) *TokenCreateTransaction { + transaction.pb.Symbol = symbol + return transaction +} + +// The number of decimal places a token is divisible by. This field can never be changed! +func (transaction *TokenCreateTransaction) SetDecimals(decimals uint) *TokenCreateTransaction { + transaction.pb.Decimals = uint32(decimals) + return transaction +} + +// Specifies the initial supply of tokens to be put in circulation. The initial supply is sent to the Treasury Account. The supply is in the lowest denomination possible. +func (transaction *TokenCreateTransaction) SetTreasury(treasury AccountID) *TokenCreateTransaction { + transaction.pb.Treasury = treasury.toProtobuf() + return transaction +} + +// The account which will act as a treasury for the token. This account will receive the specified initial supply +func (transaction *TokenCreateTransaction) SetAdminKey(publicKey PublicKey) *TokenCreateTransaction { + transaction.pb.AdminKey = publicKey.toProtoKey() + return transaction +} + +// The key which can perform update/delete operations on the token. If empty, the token can be perceived as immutable (not being able to be updated/deleted) +func (transaction *TokenCreateTransaction) SetKycKey(publicKey PublicKey) *TokenCreateTransaction { + transaction.pb.KycKey = publicKey.toProtoKey() + return transaction +} + +// The key which can grant or revoke KYC of an account for the token's transactions. If empty, KYC is not required, and KYC grant or revoke operations are not possible. +func (transaction *TokenCreateTransaction) SetFreezeKey(publicKey PublicKey) *TokenCreateTransaction { + transaction.pb.FreezeKey = publicKey.toProtoKey() + return transaction +} + +// The key which can sign to freeze or unfreeze an account for token transactions. If empty, freezing is not possible +func (transaction *TokenCreateTransaction) SetWipeKey(publicKey PublicKey) *TokenCreateTransaction { + transaction.pb.WipeKey = publicKey.toProtoKey() + return transaction +} + +// The key which can wipe the token balance of an account. If empty, wipe is not possible +func (transaction *TokenCreateTransaction) SetSupplyKey(publicKey PublicKey) *TokenCreateTransaction { + transaction.pb.SupplyKey = publicKey.toProtoKey() + return transaction +} + +// The key which can change the supply of a token. The key is used to sign Token Mint/Burn operations +// SetInitialBalance sets the initial number of Hbar to put into the token +func (transaction *TokenCreateTransaction) SetInitialSupply(initialSupply uint64) *TokenCreateTransaction { + transaction.pb.InitialSupply = initialSupply + return transaction +} + +// The default Freeze status (frozen or unfrozen) of Hedera accounts relative to this token. If true, an account must be unfrozen before it can receive the token +func (transaction *TokenCreateTransaction) SetFreezeDefault(freezeDefault bool) *TokenCreateTransaction { + transaction.pb.FreezeDefault = freezeDefault + return transaction +} + +// The epoch second at which the token should expire; if an auto-renew account and period are specified, this is coerced to the current epoch second plus the autoRenewPeriod +func (transaction *TokenCreateTransaction) SetExpirationTime(expirationTime uint64) *TokenCreateTransaction { + transaction.pb.Expiry = expirationTime + return transaction +} + +// An account which will be automatically charged to renew the token's expiration, at autoRenewPeriod interval +func (transaction *TokenCreateTransaction) SetAutoRenewAccount(autoRenewAccount AccountID) *TokenCreateTransaction { + transaction.pb.AutoRenewAccount = autoRenewAccount.toProtobuf() + return transaction +} + +// The interval at which the auto-renew account will be charged to extend the token's expiry +func (transaction *TokenCreateTransaction) SetAutoRenewPeriod(autoRenewPeriod uint64) *TokenCreateTransaction { + transaction.pb.AutoRenewPeriod = autoRenewPeriod + return transaction +} + +// +// The following methods must be copy-pasted/overriden at the bottom of **every** _transaction.go file +// We override the embedded fluent setter methods to return the outer type +// + +func tokenCreateTransaction_getMethod(request request, channel *channel) method { + return method{ + transaction: channel.getToken().CreateToken, + } +} + +func (transaction *TokenCreateTransaction) IsFrozen() bool { + return transaction.isFrozen() +} + +// Sign uses the provided privateKey to sign the transaction. +func (transaction *TokenCreateTransaction) Sign( + privateKey PrivateKey, +) *TokenCreateTransaction { + return transaction.SignWith(privateKey.PublicKey(), privateKey.Sign) +} + +func (transaction *TokenCreateTransaction) SignWithOperator( + client *Client, +) (*TokenCreateTransaction, error) { + // If the transaction is not signed by the operator, we need + // to sign the transaction with the operator + + if client.operator == nil { + return nil, errClientOperatorSigning + } + + if !transaction.IsFrozen() { + transaction.FreezeWith(client) + } + + return transaction.SignWith(client.operator.publicKey, client.operator.signer), nil +} + +// SignWith executes the TransactionSigner and adds the resulting signature data to the Transaction's signature map +// with the publicKey as the map key. +func (transaction *TokenCreateTransaction) SignWith( + publicKey PublicKey, + signer TransactionSigner, +) *TokenCreateTransaction { + if !transaction.IsFrozen() { + transaction.Freeze() + } + + if transaction.keyAlreadySigned(publicKey) { + return transaction + } + + for index := 0; index < len(transaction.transactions); index++ { + signature := signer(transaction.transactions[index].GetBodyBytes()) + + transaction.signatures[index].SigPair = append( + transaction.signatures[index].SigPair, + publicKey.toSignaturePairProtobuf(signature), + ) + } + + return transaction +} + +// Execute executes the Transaction with the provided client +func (transaction *TokenCreateTransaction) Execute( + client *Client, +) (TransactionResponse, error) { + if !transaction.IsFrozen() { + transaction.FreezeWith(client) + } + + transactionID := transaction.id + + if !client.GetOperatorID().isZero() && client.GetOperatorID().equals(transactionID.TokenID) { + transaction.SignWith( + client.GetOperatorKey(), + client.operator.signer, + ) + } + + resp, err := execute( + client, + request{ + transaction: &transaction.Transaction, + }, + transaction_shouldRetry, + transaction_makeRequest, + transaction_advanceRequest, + transaction_getNodeId, + tokenCreateTransaction_getMethod, + transaction_mapResponseStatus, + transaction_mapResponse, + ) + + if err != nil { + return TransactionResponse{}, err + } + + return TransactionResponse{ + TransactionID: transaction.id, + NodeID: resp.transaction.NodeID, + }, nil +} + +func (transaction *TokenCreateTransaction) onFreeze( + pbBody *proto.TransactionBody, +) bool { + pbBody.Data = &proto.TransactionBody_TokenCreation{ + TokenCreation: transaction.pb, + } + + return true +} + +func (transaction *TokenCreateTransaction) Freeze() (*TokenCreateTransaction, error) { + return transaction.FreezeWith(nil) +} + +func (transaction *TokenCreateTransaction) FreezeWith(client *Client) (*TokenCreateTransaction, error) { + transaction.initFee(client) + if err := transaction.initTransactionID(client); err != nil { + return transaction, err + } + + if !transaction.onFreeze(transaction.pbBody) { + return transaction, nil + } + + return transaction, transaction_freezeWith(&transaction.Transaction, client) +} + +func (transaction *TokenCreateTransaction) GetMaxTransactionFee() Hbar { + return transaction.Transaction.GetMaxTransactionFee() +} + +// SetMaxTransactionFee sets the max transaction fee for this TokenCreateTransaction. +func (transaction *TokenCreateTransaction) SetMaxTransactionFee(fee Hbar) *TokenCreateTransaction { + transaction.Transaction.SetMaxTransactionFee(fee) + return transaction +} + +func (transaction *TokenCreateTransaction) GetTransactionMemo() string { + return transaction.Transaction.GetTransactionMemo() +} + +// SetTransactionMemo sets the memo for this TokenCreateTransaction. +func (transaction *TokenCreateTransaction) SetTransactionMemo(memo string) *TokenCreateTransaction { + transaction.Transaction.SetTransactionMemo(memo) + return transaction +} + +func (transaction *TokenCreateTransaction) GetTransactionValidDuration() time.Duration { + return transaction.Transaction.GetTransactionValidDuration() +} + +// SetTransactionValidDuration sets the valid duration for this TokenCreateTransaction. +func (transaction *TokenCreateTransaction) SetTransactionValidDuration(duration time.Duration) *TokenCreateTransaction { + transaction.Transaction.SetTransactionValidDuration(duration) + return transaction +} + +func (transaction *TokenCreateTransaction) GetTransactionID() TransactionID { + return transaction.Transaction.GetTransactionID() +} + +// SetTransactionID sets the TransactionID for this TokenCreateTransaction. +func (transaction *TokenCreateTransaction) SetTransactionID(transactionID TransactionID) *TokenCreateTransaction { + transaction.id = transactionID + transaction.Transaction.SetTransactionID(transactionID) + return transaction +} + +func (transaction *TokenCreateTransaction) GetNodeID() AccountID { + return transaction.Transaction.GetNodeID() +} + +// SetNodeTokenID sets the node TokenID for this TokenCreateTransaction. +func (transaction *TokenCreateTransaction) SetNodeAccountID(nodeID AccountID) *TokenCreateTransaction { + transaction.Transaction.SetNodeAccountID(nodeID) + return transaction +} diff --git a/token_delete_transaction.go b/token_delete_transaction.go new file mode 100644 index 000000000..643d6ef8e --- /dev/null +++ b/token_delete_transaction.go @@ -0,0 +1,217 @@ +package hedera + +import ( + "time" + + "github.com/hashgraph/hedera-sdk-go/proto" +) + +// Deletes an already created Token. +// If no value is given for a field, that field is left unchanged. For an immutable tokens +// (that is, a token created without an adminKey), only the expiry may be deleted. Setting any +// other field in that case will cause the transaction status to resolve to TOKEN_IS_IMMUTABlE. +type TokenDeleteTransaction struct { + Transaction + pb *proto.TokenDeleteTransactionBody +} + +func NewTokenDeleteTransaction() *TokenDeleteTransaction { + pb := &proto.TokenDeleteTransactionBody{} + + transaction := TokenDeleteTransaction{ + pb: pb, + Transaction: newTransaction(), + } + + return &transaction +} + +// The Token to be deleted +func (transaction *TokenDeleteTransaction) SetTokenID(tokenID TokenID) *TokenDeleteTransaction { + transaction.pb.Token = tokenID.toProtobuf() + return transaction +} + +// +// The following methods must be copy-pasted/overriden at the bottom of **every** _transaction.go file +// We override the embedded fluent setter methods to return the outer type +// + +func tokenDeleteTransaction_getMethod(request request, channel *channel) method { + return method{ + transaction: channel.getToken().DeleteToken, + } +} + +func (transaction *TokenDeleteTransaction) IsFrozen() bool { + return transaction.isFrozen() +} + +// Sign uses the provided privateKey to sign the transaction. +func (transaction *TokenDeleteTransaction) Sign( + privateKey PrivateKey, +) *TokenDeleteTransaction { + return transaction.SignWith(privateKey.PublicKey(), privateKey.Sign) +} + +func (transaction *TokenDeleteTransaction) SignWithOperator( + client *Client, +) (*TokenDeleteTransaction, error) { + // If the transaction is not signed by the operator, we need + // to sign the transaction with the operator + + if client.operator == nil { + return nil, errClientOperatorSigning + } + + if !transaction.IsFrozen() { + transaction.FreezeWith(client) + } + + return transaction.SignWith(client.operator.publicKey, client.operator.signer), nil +} + +// SignWith executes the TransactionSigner and adds the resulting signature data to the Transaction's signature map +// with the publicKey as the map key. +func (transaction *TokenDeleteTransaction) SignWith( + publicKey PublicKey, + signer TransactionSigner, +) *TokenDeleteTransaction { + if !transaction.IsFrozen() { + transaction.Freeze() + } + + if transaction.keyAlreadySigned(publicKey) { + return transaction + } + + for index := 0; index < len(transaction.transactions); index++ { + signature := signer(transaction.transactions[index].GetBodyBytes()) + + transaction.signatures[index].SigPair = append( + transaction.signatures[index].SigPair, + publicKey.toSignaturePairProtobuf(signature), + ) + } + + return transaction +} + +// Execute executes the Transaction with the provided client +func (transaction *TokenDeleteTransaction) Execute( + client *Client, +) (TransactionResponse, error) { + if !transaction.IsFrozen() { + transaction.FreezeWith(client) + } + + transactionID := transaction.id + + if !client.GetOperatorID().isZero() && client.GetOperatorID().equals(transactionID.TokenID) { + transaction.SignWith( + client.GetOperatorKey(), + client.operator.signer, + ) + } + + resp, err := execute( + client, + request{ + transaction: &transaction.Transaction, + }, + transaction_shouldRetry, + transaction_makeRequest, + transaction_advanceRequest, + transaction_getNodeId, + tokenDeleteTransaction_getMethod, + transaction_mapResponseStatus, + transaction_mapResponse, + ) + + if err != nil { + return TransactionResponse{}, err + } + + return TransactionResponse{ + TransactionID: transaction.id, + NodeID: resp.transaction.NodeID, + }, nil +} + +func (transaction *TokenDeleteTransaction) onFreeze( + pbBody *proto.TransactionBody, +) bool { + pbBody.Data = &proto.TransactionBody_TokenDeletion{ + TokenDeletion: transaction.pb, + } + + return true +} + +func (transaction *TokenDeleteTransaction) Freeze() (*TokenDeleteTransaction, error) { + return transaction.FreezeWith(nil) +} + +func (transaction *TokenDeleteTransaction) FreezeWith(client *Client) (*TokenDeleteTransaction, error) { + transaction.initFee(client) + if err := transaction.initTransactionID(client); err != nil { + return transaction, err + } + + if !transaction.onFreeze(transaction.pbBody) { + return transaction, nil + } + + return transaction, transaction_freezeWith(&transaction.Transaction, client) +} + +func (transaction *TokenDeleteTransaction) GetMaxTransactionFee() Hbar { + return transaction.Transaction.GetMaxTransactionFee() +} + +// SetMaxTransactionFee sets the max transaction fee for this TokenDeleteTransaction. +func (transaction *TokenDeleteTransaction) SetMaxTransactionFee(fee Hbar) *TokenDeleteTransaction { + transaction.Transaction.SetMaxTransactionFee(fee) + return transaction +} + +func (transaction *TokenDeleteTransaction) GetTransactionMemo() string { + return transaction.Transaction.GetTransactionMemo() +} + +// SetTransactionMemo sets the memo for this TokenDeleteTransaction. +func (transaction *TokenDeleteTransaction) SetTransactionMemo(memo string) *TokenDeleteTransaction { + transaction.Transaction.SetTransactionMemo(memo) + return transaction +} + +func (transaction *TokenDeleteTransaction) GetTransactionValidDuration() time.Duration { + return transaction.Transaction.GetTransactionValidDuration() +} + +// SetTransactionValidDuration sets the valid duration for this TokenDeleteTransaction. +func (transaction *TokenDeleteTransaction) SetTransactionValidDuration(duration time.Duration) *TokenDeleteTransaction { + transaction.Transaction.SetTransactionValidDuration(duration) + return transaction +} + +func (transaction *TokenDeleteTransaction) GetTransactionID() TransactionID { + return transaction.Transaction.GetTransactionID() +} + +// SetTransactionID sets the TransactionID for this TokenDeleteTransaction. +func (transaction *TokenDeleteTransaction) SetTransactionID(transactionID TransactionID) *TokenDeleteTransaction { + transaction.id = transactionID + transaction.Transaction.SetTransactionID(transactionID) + return transaction +} + +func (transaction *TokenDeleteTransaction) GetNodeID() AccountID { + return transaction.Transaction.GetNodeID() +} + +// SetNodeTokenID sets the node TokenID for this TokenDeleteTransaction. +func (transaction *TokenDeleteTransaction) SetNodeAccountID(nodeID AccountID) *TokenDeleteTransaction { + transaction.Transaction.SetNodeAccountID(nodeID) + return transaction +} diff --git a/token_dissociate_transaction.go b/token_dissociate_transaction.go new file mode 100644 index 000000000..f7810a4d3 --- /dev/null +++ b/token_dissociate_transaction.go @@ -0,0 +1,224 @@ +package hedera + +import ( + "time" + + "github.com/hashgraph/hedera-sdk-go/proto" +) + +type TokenDissociateTransaction struct { + Transaction + pb *proto.TokenDissociateTransactionBody +} + +func NewTokenDissociateTransaction() *TokenDissociateTransaction { + pb := &proto.TokenDissociateTransactionBody{} + + transaction := TokenDissociateTransaction{ + pb: pb, + Transaction: newTransaction(), + } + + return &transaction +} + +// The account to be dissociated with the provided tokens +func (transaction *TokenDissociateTransaction) SetAccountID(accountID AccountID) *TokenDissociateTransaction { + transaction.pb.Account = accountID.toProtobuf() + return transaction +} + +// The tokens to be dissociated with the provided account +func (transaction *TokenDissociateTransaction) SetTokenIDs(tokenIDs ...TokenID) *TokenDissociateTransaction { + transaction.pb.Tokens = make([]*proto.TokenID, len(tokenIDs)) + + for i, tokenID := range tokenIDs { + transaction.pb.Tokens[i] = tokenID.toProtobuf() + } + + return transaction +} + +// +// The following methods must be copy-pasted/overriden at the bottom of **every** _transaction.go file +// We override the embedded fluent setter methods to return the outer type +// + +func tokenDissociateTransaction_getMethod(request request, channel *channel) method { + return method{ + transaction: channel.getToken().DissociateTokens, + } +} + +func (transaction *TokenDissociateTransaction) IsFrozen() bool { + return transaction.isFrozen() +} + +// Sign uses the provided privateKey to sign the transaction. +func (transaction *TokenDissociateTransaction) Sign( + privateKey PrivateKey, +) *TokenDissociateTransaction { + return transaction.SignWith(privateKey.PublicKey(), privateKey.Sign) +} + +func (transaction *TokenDissociateTransaction) SignWithOperator( + client *Client, +) (*TokenDissociateTransaction, error) { + // If the transaction is not signed by the operator, we need + // to sign the transaction with the operator + + if client.operator == nil { + return nil, errClientOperatorSigning + } + + if !transaction.IsFrozen() { + transaction.FreezeWith(client) + } + + return transaction.SignWith(client.operator.publicKey, client.operator.signer), nil +} + +// SignWith executes the TransactionSigner and adds the resulting signature data to the Transaction's signature map +// with the publicKey as the map key. +func (transaction *TokenDissociateTransaction) SignWith( + publicKey PublicKey, + signer TransactionSigner, +) *TokenDissociateTransaction { + if !transaction.IsFrozen() { + transaction.Freeze() + } + + if transaction.keyAlreadySigned(publicKey) { + return transaction + } + + for index := 0; index < len(transaction.transactions); index++ { + signature := signer(transaction.transactions[index].GetBodyBytes()) + + transaction.signatures[index].SigPair = append( + transaction.signatures[index].SigPair, + publicKey.toSignaturePairProtobuf(signature), + ) + } + + return transaction +} + +// Execute executes the Transaction with the provided client +func (transaction *TokenDissociateTransaction) Execute( + client *Client, +) (TransactionResponse, error) { + if !transaction.IsFrozen() { + transaction.FreezeWith(client) + } + + transactionID := transaction.id + + if !client.GetOperatorID().isZero() && client.GetOperatorID().equals(transactionID.TokenID) { + transaction.SignWith( + client.GetOperatorKey(), + client.operator.signer, + ) + } + + resp, err := execute( + client, + request{ + transaction: &transaction.Transaction, + }, + transaction_shouldRetry, + transaction_makeRequest, + transaction_advanceRequest, + transaction_getNodeId, + tokenDissociateTransaction_getMethod, + transaction_mapResponseStatus, + transaction_mapResponse, + ) + + if err != nil { + return TransactionResponse{}, err + } + + return TransactionResponse{ + TransactionID: transaction.id, + NodeID: resp.transaction.NodeID, + }, nil +} + +func (transaction *TokenDissociateTransaction) onFreeze( + pbBody *proto.TransactionBody, +) bool { + pbBody.Data = &proto.TransactionBody_TokenDissociate{ + TokenDissociate: transaction.pb, + } + + return true +} + +func (transaction *TokenDissociateTransaction) Freeze() (*TokenDissociateTransaction, error) { + return transaction.FreezeWith(nil) +} + +func (transaction *TokenDissociateTransaction) FreezeWith(client *Client) (*TokenDissociateTransaction, error) { + transaction.initFee(client) + if err := transaction.initTransactionID(client); err != nil { + return transaction, err + } + + if !transaction.onFreeze(transaction.pbBody) { + return transaction, nil + } + + return transaction, transaction_freezeWith(&transaction.Transaction, client) +} + +func (transaction *TokenDissociateTransaction) GetMaxTransactionFee() Hbar { + return transaction.Transaction.GetMaxTransactionFee() +} + +// SetMaxTransactionFee sets the max transaction fee for this TokenDissociateTransaction. +func (transaction *TokenDissociateTransaction) SetMaxTransactionFee(fee Hbar) *TokenDissociateTransaction { + transaction.Transaction.SetMaxTransactionFee(fee) + return transaction +} + +func (transaction *TokenDissociateTransaction) GetTransactionMemo() string { + return transaction.Transaction.GetTransactionMemo() +} + +// SetTransactionMemo sets the memo for this TokenDissociateTransaction. +func (transaction *TokenDissociateTransaction) SetTransactionMemo(memo string) *TokenDissociateTransaction { + transaction.Transaction.SetTransactionMemo(memo) + return transaction +} + +func (transaction *TokenDissociateTransaction) GetTransactionValidDuration() time.Duration { + return transaction.Transaction.GetTransactionValidDuration() +} + +// SetTransactionValidDuration sets the valid duration for this TokenDissociateTransaction. +func (transaction *TokenDissociateTransaction) SetTransactionValidDuration(duration time.Duration) *TokenDissociateTransaction { + transaction.Transaction.SetTransactionValidDuration(duration) + return transaction +} + +func (transaction *TokenDissociateTransaction) GetTransactionID() TransactionID { + return transaction.Transaction.GetTransactionID() +} + +// SetTransactionID sets the TransactionID for this TokenDissociateTransaction. +func (transaction *TokenDissociateTransaction) SetTransactionID(transactionID TransactionID) *TokenDissociateTransaction { + transaction.id = transactionID + transaction.Transaction.SetTransactionID(transactionID) + return transaction +} + +func (transaction *TokenDissociateTransaction) GetNodeID() AccountID { + return transaction.Transaction.GetNodeID() +} + +// SetNodeTokenID sets the node TokenID for this TokenDissociateTransaction. +func (transaction *TokenDissociateTransaction) SetNodeAccountID(nodeID AccountID) *TokenDissociateTransaction { + transaction.Transaction.SetNodeAccountID(nodeID) + return transaction +} diff --git a/token_freeze_transaction.go b/token_freeze_transaction.go new file mode 100644 index 000000000..460c2b09f --- /dev/null +++ b/token_freeze_transaction.go @@ -0,0 +1,230 @@ +package hedera + +import ( + "time" + + "github.com/hashgraph/hedera-sdk-go/proto" +) + +// Freezes transfers of the specified token for the account. Must be signed by the Token's freezeKey. +// 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 Freeze Key is defined, the transaction will resolve to TOKEN_HAS_NO_FREEZE_KEY. +// Once executed the Account is marked as Frozen and will not be able to receive or send tokens +// unless unfrozen. The operation is idempotent. +type TokenFreezeTransaction struct { + Transaction + pb *proto.TokenFreezeAccountTransactionBody +} + +func NewTokenFreezeTransaction() *TokenFreezeTransaction { + pb := &proto.TokenFreezeAccountTransactionBody{} + + transaction := TokenFreezeTransaction{ + pb: pb, + Transaction: newTransaction(), + } + + return &transaction +} + +// The token for which this account will be frozen. If token does not exist, transaction results +// in INVALID_TOKEN_ID +func (transaction *TokenFreezeTransaction) SetTokenID(tokenID TokenID) *TokenFreezeTransaction { + transaction.pb.Token = tokenID.toProtobuf() + return transaction +} + +// The account to be frozen +func (transaction *TokenFreezeTransaction) SetAccountID(accountID AccountID) *TokenFreezeTransaction { + transaction.pb.Account = accountID.toProtobuf() + return transaction +} + +// +// The following methods must be copy-pasted/overriden at the bottom of **every** _transaction.go file +// We override the embedded fluent setter methods to return the outer type +// + +func tokenFreezeTransaction_getMethod(request request, channel *channel) method { + return method{ + transaction: channel.getToken().FreezeTokenAccount, + } +} + +func (transaction *TokenFreezeTransaction) IsFrozen() bool { + return transaction.isFrozen() +} + +// Sign uses the provided privateKey to sign the transaction. +func (transaction *TokenFreezeTransaction) Sign( + privateKey PrivateKey, +) *TokenFreezeTransaction { + return transaction.SignWith(privateKey.PublicKey(), privateKey.Sign) +} + +func (transaction *TokenFreezeTransaction) SignWithOperator( + client *Client, +) (*TokenFreezeTransaction, error) { + // If the transaction is not signed by the operator, we need + // to sign the transaction with the operator + + if client.operator == nil { + return nil, errClientOperatorSigning + } + + if !transaction.IsFrozen() { + transaction.FreezeWith(client) + } + + return transaction.SignWith(client.operator.publicKey, client.operator.signer), nil +} + +// SignWith executes the TransactionSigner and adds the resulting signature data to the Transaction's signature map +// with the publicKey as the map key. +func (transaction *TokenFreezeTransaction) SignWith( + publicKey PublicKey, + signer TransactionSigner, +) *TokenFreezeTransaction { + if !transaction.IsFrozen() { + transaction.Freeze() + } + + if transaction.keyAlreadySigned(publicKey) { + return transaction + } + + for index := 0; index < len(transaction.transactions); index++ { + signature := signer(transaction.transactions[index].GetBodyBytes()) + + transaction.signatures[index].SigPair = append( + transaction.signatures[index].SigPair, + publicKey.toSignaturePairProtobuf(signature), + ) + } + + return transaction +} + +// Execute executes the Transaction with the provided client +func (transaction *TokenFreezeTransaction) Execute( + client *Client, +) (TransactionResponse, error) { + if !transaction.IsFrozen() { + transaction.FreezeWith(client) + } + + transactionID := transaction.id + + if !client.GetOperatorID().isZero() && client.GetOperatorID().equals(transactionID.TokenID) { + transaction.SignWith( + client.GetOperatorKey(), + client.operator.signer, + ) + } + + resp, err := execute( + client, + request{ + transaction: &transaction.Transaction, + }, + transaction_shouldRetry, + transaction_makeRequest, + transaction_advanceRequest, + transaction_getNodeId, + tokenFreezeTransaction_getMethod, + transaction_mapResponseStatus, + transaction_mapResponse, + ) + + if err != nil { + return TransactionResponse{}, err + } + + return TransactionResponse{ + TransactionID: transaction.id, + NodeID: resp.transaction.NodeID, + }, nil +} + +func (transaction *TokenFreezeTransaction) onFreeze( + pbBody *proto.TransactionBody, +) bool { + pbBody.Data = &proto.TransactionBody_TokenFreeze{ + TokenFreeze: transaction.pb, + } + + return true +} + +func (transaction *TokenFreezeTransaction) Freeze() (*TokenFreezeTransaction, error) { + return transaction.FreezeWith(nil) +} + +func (transaction *TokenFreezeTransaction) FreezeWith(client *Client) (*TokenFreezeTransaction, error) { + transaction.initFee(client) + if err := transaction.initTransactionID(client); err != nil { + return transaction, err + } + + if !transaction.onFreeze(transaction.pbBody) { + return transaction, nil + } + + return transaction, transaction_freezeWith(&transaction.Transaction, client) +} + +func (transaction *TokenFreezeTransaction) GetMaxTransactionFee() Hbar { + return transaction.Transaction.GetMaxTransactionFee() +} + +// SetMaxTransactionFee sets the max transaction fee for this TokenFreezeTransaction. +func (transaction *TokenFreezeTransaction) SetMaxTransactionFee(fee Hbar) *TokenFreezeTransaction { + transaction.Transaction.SetMaxTransactionFee(fee) + return transaction +} + +func (transaction *TokenFreezeTransaction) GetTransactionMemo() string { + return transaction.Transaction.GetTransactionMemo() +} + +// SetTransactionMemo sets the memo for this TokenFreezeTransaction. +func (transaction *TokenFreezeTransaction) SetTransactionMemo(memo string) *TokenFreezeTransaction { + transaction.Transaction.SetTransactionMemo(memo) + return transaction +} + +func (transaction *TokenFreezeTransaction) GetTransactionValidDuration() time.Duration { + return transaction.Transaction.GetTransactionValidDuration() +} + +// SetTransactionValidDuration sets the valid duration for this TokenFreezeTransaction. +func (transaction *TokenFreezeTransaction) SetTransactionValidDuration(duration time.Duration) *TokenFreezeTransaction { + transaction.Transaction.SetTransactionValidDuration(duration) + return transaction +} + +func (transaction *TokenFreezeTransaction) GetTransactionID() TransactionID { + return transaction.Transaction.GetTransactionID() +} + +// SetTransactionID sets the TransactionID for this TokenFreezeTransaction. +func (transaction *TokenFreezeTransaction) SetTransactionID(transactionID TransactionID) *TokenFreezeTransaction { + transaction.id = transactionID + transaction.Transaction.SetTransactionID(transactionID) + return transaction +} + +func (transaction *TokenFreezeTransaction) GetNodeID() AccountID { + return transaction.Transaction.GetNodeID() +} + +// SetNodeTokenID sets the node TokenID for this TokenFreezeTransaction. +func (transaction *TokenFreezeTransaction) SetNodeAccountID(nodeID AccountID) *TokenFreezeTransaction { + transaction.Transaction.SetNodeAccountID(nodeID) + return transaction +} diff --git a/token_grant_kyc_transaction.go b/token_grant_kyc_transaction.go new file mode 100644 index 000000000..153c9a4f5 --- /dev/null +++ b/token_grant_kyc_transaction.go @@ -0,0 +1,228 @@ +package hedera + +import ( + "time" + + "github.com/hashgraph/hedera-sdk-go/proto" +) + +// Grants KYC to the account for the given token. Must be signed by the Token's kycKey. +// 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. +type TokenGrantKycTransaction struct { + Transaction + pb *proto.TokenGrantKycTransactionBody +} + +func NewTokenGrantKycTransaction() *TokenGrantKycTransaction { + pb := &proto.TokenGrantKycTransactionBody{} + + transaction := TokenGrantKycTransaction{ + pb: pb, + Transaction: newTransaction(), + } + + return &transaction +} + +// The token for which this account will be granted KYC. If token does not exist, transaction results in INVALID_TOKEN_ID +func (transaction *TokenGrantKycTransaction) SetTokenID(tokenID TokenID) *TokenGrantKycTransaction { + transaction.pb.Token = tokenID.toProtobuf() + return transaction +} + +// The account to be KYCed +func (transaction *TokenGrantKycTransaction) SetAccountID(accountID AccountID) *TokenGrantKycTransaction { + transaction.pb.Account = accountID.toProtobuf() + return transaction +} + +// +// The following methods must be copy-pasted/overriden at the bottom of **every** _transaction.go file +// We override the embedded fluent setter methods to return the outer type +// + +func tokenGrantKycTransaction_getMethod(request request, channel *channel) method { + return method{ + transaction: channel.getToken().GrantKycToTokenAccount, + } +} + +func (transaction *TokenGrantKycTransaction) IsFrozen() bool { + return transaction.isFrozen() +} + +// Sign uses the provided privateKey to sign the transaction. +func (transaction *TokenGrantKycTransaction) Sign( + privateKey PrivateKey, +) *TokenGrantKycTransaction { + return transaction.SignWith(privateKey.PublicKey(), privateKey.Sign) +} + +func (transaction *TokenGrantKycTransaction) SignWithOperator( + client *Client, +) (*TokenGrantKycTransaction, error) { + // If the transaction is not signed by the operator, we need + // to sign the transaction with the operator + + if client.operator == nil { + return nil, errClientOperatorSigning + } + + if !transaction.IsFrozen() { + transaction.FreezeWith(client) + } + + return transaction.SignWith(client.operator.publicKey, client.operator.signer), nil +} + +// SignWith executes the TransactionSigner and adds the resulting signature data to the Transaction's signature map +// with the publicKey as the map key. +func (transaction *TokenGrantKycTransaction) SignWith( + publicKey PublicKey, + signer TransactionSigner, +) *TokenGrantKycTransaction { + if !transaction.IsFrozen() { + transaction.Freeze() + } + + if transaction.keyAlreadySigned(publicKey) { + return transaction + } + + for index := 0; index < len(transaction.transactions); index++ { + signature := signer(transaction.transactions[index].GetBodyBytes()) + + transaction.signatures[index].SigPair = append( + transaction.signatures[index].SigPair, + publicKey.toSignaturePairProtobuf(signature), + ) + } + + return transaction +} + +// Execute executes the Transaction with the provided client +func (transaction *TokenGrantKycTransaction) Execute( + client *Client, +) (TransactionResponse, error) { + if !transaction.IsFrozen() { + transaction.FreezeWith(client) + } + + transactionID := transaction.id + + if !client.GetOperatorID().isZero() && client.GetOperatorID().equals(transactionID.TokenID) { + transaction.SignWith( + client.GetOperatorKey(), + client.operator.signer, + ) + } + + resp, err := execute( + client, + request{ + transaction: &transaction.Transaction, + }, + transaction_shouldRetry, + transaction_makeRequest, + transaction_advanceRequest, + transaction_getNodeId, + tokenGrantKycTransaction_getMethod, + transaction_mapResponseStatus, + transaction_mapResponse, + ) + + if err != nil { + return TransactionResponse{}, err + } + + return TransactionResponse{ + TransactionID: transaction.id, + NodeID: resp.transaction.NodeID, + }, nil +} + +func (transaction *TokenGrantKycTransaction) onFreeze( + pbBody *proto.TransactionBody, +) bool { + pbBody.Data = &proto.TransactionBody_TokenGrantKyc{ + TokenGrantKyc: transaction.pb, + } + + return true +} + +func (transaction *TokenGrantKycTransaction) Freeze() (*TokenGrantKycTransaction, error) { + return transaction.FreezeWith(nil) +} + +func (transaction *TokenGrantKycTransaction) FreezeWith(client *Client) (*TokenGrantKycTransaction, error) { + transaction.initFee(client) + if err := transaction.initTransactionID(client); err != nil { + return transaction, err + } + + if !transaction.onFreeze(transaction.pbBody) { + return transaction, nil + } + + return transaction, transaction_freezeWith(&transaction.Transaction, client) +} + +func (transaction *TokenGrantKycTransaction) GetMaxTransactionFee() Hbar { + return transaction.Transaction.GetMaxTransactionFee() +} + +// SetMaxTransactionFee sets the max transaction fee for this TokenGrantKycTransaction. +func (transaction *TokenGrantKycTransaction) SetMaxTransactionFee(fee Hbar) *TokenGrantKycTransaction { + transaction.Transaction.SetMaxTransactionFee(fee) + return transaction +} + +func (transaction *TokenGrantKycTransaction) GetTransactionMemo() string { + return transaction.Transaction.GetTransactionMemo() +} + +// SetTransactionMemo sets the memo for this TokenGrantKycTransaction. +func (transaction *TokenGrantKycTransaction) SetTransactionMemo(memo string) *TokenGrantKycTransaction { + transaction.Transaction.SetTransactionMemo(memo) + return transaction +} + +func (transaction *TokenGrantKycTransaction) GetTransactionValidDuration() time.Duration { + return transaction.Transaction.GetTransactionValidDuration() +} + +// SetTransactionValidDuration sets the valid duration for this TokenGrantKycTransaction. +func (transaction *TokenGrantKycTransaction) SetTransactionValidDuration(duration time.Duration) *TokenGrantKycTransaction { + transaction.Transaction.SetTransactionValidDuration(duration) + return transaction +} + +func (transaction *TokenGrantKycTransaction) GetTransactionID() TransactionID { + return transaction.Transaction.GetTransactionID() +} + +// SetTransactionID sets the TransactionID for this TokenGrantKycTransaction. +func (transaction *TokenGrantKycTransaction) SetTransactionID(transactionID TransactionID) *TokenGrantKycTransaction { + transaction.id = transactionID + transaction.Transaction.SetTransactionID(transactionID) + return transaction +} + +func (transaction *TokenGrantKycTransaction) GetNodeID() AccountID { + return transaction.Transaction.GetNodeID() +} + +// SetNodeTokenID sets the node TokenID for this TokenGrantKycTransaction. +func (transaction *TokenGrantKycTransaction) SetNodeAccountID(nodeID AccountID) *TokenGrantKycTransaction { + transaction.Transaction.SetNodeAccountID(nodeID) + return transaction +} diff --git a/token_mint_transaction.go b/token_mint_transaction.go new file mode 100644 index 000000000..88abbc8b6 --- /dev/null +++ b/token_mint_transaction.go @@ -0,0 +1,229 @@ +package hedera + +import ( + "time" + + "github.com/hashgraph/hedera-sdk-go/proto" +) + +// Mints tokens from the Token's treasury Account. If no Supply Key is defined, the transaction +// will resolve to TOKEN_HAS_NO_SUPPLY_KEY. +// The operation decreases the Total Supply of the Token. Total supply cannot go below +// zero. +// The amount provided must be in the lowest denomination possible. Example: +// Token A has 2 decimals. In order to mint 100 tokens, one must provide amount of 10000. In order +// to mint 100.55 tokens, one must provide amount of 10055. +type TokenMintTransaction struct { + Transaction + pb *proto.TokenMintTransactionBody +} + +func NewTokenMintTransaction() *TokenMintTransaction { + pb := &proto.TokenMintTransactionBody{} + + transaction := TokenMintTransaction{ + pb: pb, + Transaction: newTransaction(), + } + + return &transaction +} + +// The token for which to mint tokens. If token does not exist, transaction results in +// INVALID_TOKEN_ID +func (transaction *TokenMintTransaction) SetTokenID(tokenID TokenID) *TokenMintTransaction { + transaction.pb.Token = tokenID.toProtobuf() + return transaction +} + +// The amount to mint from the Treasury Account. Amount must be a positive non-zero number, not +// bigger than the token balance of the treasury account (0; balance], represented in the lowest +// denomination. +func (transaction *TokenMintTransaction) SetAmount(amount uint64) *TokenMintTransaction { + transaction.pb.Amount = amount + return transaction +} + +// +// The following methods must be copy-pasted/overriden at the bottom of **every** _transaction.go file +// We override the embedded fluent setter methods to return the outer type +// + +func tokenMintTransaction_getMethod(request request, channel *channel) method { + return method{ + transaction: channel.getToken().MintToken, + } +} + +func (transaction *TokenMintTransaction) IsFrozen() bool { + return transaction.isFrozen() +} + +// Sign uses the provided privateKey to sign the transaction. +func (transaction *TokenMintTransaction) Sign( + privateKey PrivateKey, +) *TokenMintTransaction { + return transaction.SignWith(privateKey.PublicKey(), privateKey.Sign) +} + +func (transaction *TokenMintTransaction) SignWithOperator( + client *Client, +) (*TokenMintTransaction, error) { + // If the transaction is not signed by the operator, we need + // to sign the transaction with the operator + + if client.operator == nil { + return nil, errClientOperatorSigning + } + + if !transaction.IsFrozen() { + transaction.FreezeWith(client) + } + + return transaction.SignWith(client.operator.publicKey, client.operator.signer), nil +} + +// SignWith executes the TransactionSigner and adds the resulting signature data to the Transaction's signature map +// with the publicKey as the map key. +func (transaction *TokenMintTransaction) SignWith( + publicKey PublicKey, + signer TransactionSigner, +) *TokenMintTransaction { + if !transaction.IsFrozen() { + transaction.Freeze() + } + + if transaction.keyAlreadySigned(publicKey) { + return transaction + } + + for index := 0; index < len(transaction.transactions); index++ { + signature := signer(transaction.transactions[index].GetBodyBytes()) + + transaction.signatures[index].SigPair = append( + transaction.signatures[index].SigPair, + publicKey.toSignaturePairProtobuf(signature), + ) + } + + return transaction +} + +// Execute executes the Transaction with the provided client +func (transaction *TokenMintTransaction) Execute( + client *Client, +) (TransactionResponse, error) { + if !transaction.IsFrozen() { + transaction.FreezeWith(client) + } + + transactionID := transaction.id + + if !client.GetOperatorID().isZero() && client.GetOperatorID().equals(transactionID.TokenID) { + transaction.SignWith( + client.GetOperatorKey(), + client.operator.signer, + ) + } + + resp, err := execute( + client, + request{ + transaction: &transaction.Transaction, + }, + transaction_shouldRetry, + transaction_makeRequest, + transaction_advanceRequest, + transaction_getNodeId, + tokenMintTransaction_getMethod, + transaction_mapResponseStatus, + transaction_mapResponse, + ) + + if err != nil { + return TransactionResponse{}, err + } + + return TransactionResponse{ + TransactionID: transaction.id, + NodeID: resp.transaction.NodeID, + }, nil +} + +func (transaction *TokenMintTransaction) onFreeze( + pbBody *proto.TransactionBody, +) bool { + pbBody.Data = &proto.TransactionBody_TokenMint{ + TokenMint: transaction.pb, + } + + return true +} + +func (transaction *TokenMintTransaction) Freeze() (*TokenMintTransaction, error) { + return transaction.FreezeWith(nil) +} + +func (transaction *TokenMintTransaction) FreezeWith(client *Client) (*TokenMintTransaction, error) { + transaction.initFee(client) + if err := transaction.initTransactionID(client); err != nil { + return transaction, err + } + + if !transaction.onFreeze(transaction.pbBody) { + return transaction, nil + } + + return transaction, transaction_freezeWith(&transaction.Transaction, client) +} + +func (transaction *TokenMintTransaction) GetMaxTransactionFee() Hbar { + return transaction.Transaction.GetMaxTransactionFee() +} + +// SetMaxTransactionFee sets the max transaction fee for this TokenMintTransaction. +func (transaction *TokenMintTransaction) SetMaxTransactionFee(fee Hbar) *TokenMintTransaction { + transaction.Transaction.SetMaxTransactionFee(fee) + return transaction +} + +func (transaction *TokenMintTransaction) GetTransactionMemo() string { + return transaction.Transaction.GetTransactionMemo() +} + +// SetTransactionMemo sets the memo for this TokenMintTransaction. +func (transaction *TokenMintTransaction) SetTransactionMemo(memo string) *TokenMintTransaction { + transaction.Transaction.SetTransactionMemo(memo) + return transaction +} + +func (transaction *TokenMintTransaction) GetTransactionValidDuration() time.Duration { + return transaction.Transaction.GetTransactionValidDuration() +} + +// SetTransactionValidDuration sets the valid duration for this TokenMintTransaction. +func (transaction *TokenMintTransaction) SetTransactionValidDuration(duration time.Duration) *TokenMintTransaction { + transaction.Transaction.SetTransactionValidDuration(duration) + return transaction +} + +func (transaction *TokenMintTransaction) GetTransactionID() TransactionID { + return transaction.Transaction.GetTransactionID() +} + +// SetTransactionID sets the TransactionID for this TokenMintTransaction. +func (transaction *TokenMintTransaction) SetTransactionID(transactionID TransactionID) *TokenMintTransaction { + transaction.id = transactionID + transaction.Transaction.SetTransactionID(transactionID) + return transaction +} + +func (transaction *TokenMintTransaction) GetNodeID() AccountID { + return transaction.Transaction.GetNodeID() +} + +// SetNodeTokenID sets the node TokenID for this TokenMintTransaction. +func (transaction *TokenMintTransaction) SetNodeAccountID(nodeID AccountID) *TokenMintTransaction { + transaction.Transaction.SetNodeAccountID(nodeID) + return transaction +} diff --git a/token_revoke_kcy_transaction.go b/token_revoke_kcy_transaction.go new file mode 100644 index 000000000..673ee7478 --- /dev/null +++ b/token_revoke_kcy_transaction.go @@ -0,0 +1,228 @@ +package hedera + +import ( + "time" + + "github.com/hashgraph/hedera-sdk-go/proto" +) + +// Revokes KYC to the account for the given token. Must be signed by the Token's kycKey. +// 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 Revoked +type TokenRevokeKycTransaction struct { + Transaction + pb *proto.TokenRevokeKycTransactionBody +} + +func NewTokenRevokeKycTransaction() *TokenRevokeKycTransaction { + pb := &proto.TokenRevokeKycTransactionBody{} + + transaction := TokenRevokeKycTransaction{ + pb: pb, + Transaction: newTransaction(), + } + + return &transaction +} + +// The token for which this account will get his KYC revoked. If token does not exist, transaction results in INVALID_TOKEN_ID +func (transaction *TokenRevokeKycTransaction) SetTokenID(tokenID TokenID) *TokenRevokeKycTransaction { + transaction.pb.Token = tokenID.toProtobuf() + return transaction +} + +// The account to be KYC Revoked +func (transaction *TokenRevokeKycTransaction) SetAccountID(accountID AccountID) *TokenRevokeKycTransaction { + transaction.pb.Account = accountID.toProtobuf() + return transaction +} + +// +// The following methods must be copy-pasted/overriden at the bottom of **every** _transaction.go file +// We override the embedded fluent setter methods to return the outer type +// + +func tokenRevokeKycTransaction_getMethod(request request, channel *channel) method { + return method{ + transaction: channel.getToken().RevokeKycToTokenAccount, + } +} + +func (transaction *TokenRevokeKycTransaction) IsFrozen() bool { + return transaction.isFrozen() +} + +// Sign uses the provided privateKey to sign the transaction. +func (transaction *TokenRevokeKycTransaction) Sign( + privateKey PrivateKey, +) *TokenRevokeKycTransaction { + return transaction.SignWith(privateKey.PublicKey(), privateKey.Sign) +} + +func (transaction *TokenRevokeKycTransaction) SignWithOperator( + client *Client, +) (*TokenRevokeKycTransaction, error) { + // If the transaction is not signed by the operator, we need + // to sign the transaction with the operator + + if client.operator == nil { + return nil, errClientOperatorSigning + } + + if !transaction.IsFrozen() { + transaction.FreezeWith(client) + } + + return transaction.SignWith(client.operator.publicKey, client.operator.signer), nil +} + +// SignWith executes the TransactionSigner and adds the resulting signature data to the Transaction's signature map +// with the publicKey as the map key. +func (transaction *TokenRevokeKycTransaction) SignWith( + publicKey PublicKey, + signer TransactionSigner, +) *TokenRevokeKycTransaction { + if !transaction.IsFrozen() { + transaction.Freeze() + } + + if transaction.keyAlreadySigned(publicKey) { + return transaction + } + + for index := 0; index < len(transaction.transactions); index++ { + signature := signer(transaction.transactions[index].GetBodyBytes()) + + transaction.signatures[index].SigPair = append( + transaction.signatures[index].SigPair, + publicKey.toSignaturePairProtobuf(signature), + ) + } + + return transaction +} + +// Execute executes the Transaction with the provided client +func (transaction *TokenRevokeKycTransaction) Execute( + client *Client, +) (TransactionResponse, error) { + if !transaction.IsFrozen() { + transaction.FreezeWith(client) + } + + transactionID := transaction.id + + if !client.GetOperatorID().isZero() && client.GetOperatorID().equals(transactionID.TokenID) { + transaction.SignWith( + client.GetOperatorKey(), + client.operator.signer, + ) + } + + resp, err := execute( + client, + request{ + transaction: &transaction.Transaction, + }, + transaction_shouldRetry, + transaction_makeRequest, + transaction_advanceRequest, + transaction_getNodeId, + tokenRevokeKycTransaction_getMethod, + transaction_mapResponseStatus, + transaction_mapResponse, + ) + + if err != nil { + return TransactionResponse{}, err + } + + return TransactionResponse{ + TransactionID: transaction.id, + NodeID: resp.transaction.NodeID, + }, nil +} + +func (transaction *TokenRevokeKycTransaction) onFreeze( + pbBody *proto.TransactionBody, +) bool { + pbBody.Data = &proto.TransactionBody_TokenRevokeKyc{ + TokenRevokeKyc: transaction.pb, + } + + return true +} + +func (transaction *TokenRevokeKycTransaction) Freeze() (*TokenRevokeKycTransaction, error) { + return transaction.FreezeWith(nil) +} + +func (transaction *TokenRevokeKycTransaction) FreezeWith(client *Client) (*TokenRevokeKycTransaction, error) { + transaction.initFee(client) + if err := transaction.initTransactionID(client); err != nil { + return transaction, err + } + + if !transaction.onFreeze(transaction.pbBody) { + return transaction, nil + } + + return transaction, transaction_freezeWith(&transaction.Transaction, client) +} + +func (transaction *TokenRevokeKycTransaction) GetMaxTransactionFee() Hbar { + return transaction.Transaction.GetMaxTransactionFee() +} + +// SetMaxTransactionFee sets the max transaction fee for this TokenRevokeKycTransaction. +func (transaction *TokenRevokeKycTransaction) SetMaxTransactionFee(fee Hbar) *TokenRevokeKycTransaction { + transaction.Transaction.SetMaxTransactionFee(fee) + return transaction +} + +func (transaction *TokenRevokeKycTransaction) GetTransactionMemo() string { + return transaction.Transaction.GetTransactionMemo() +} + +// SetTransactionMemo sets the memo for this TokenRevokeKycTransaction. +func (transaction *TokenRevokeKycTransaction) SetTransactionMemo(memo string) *TokenRevokeKycTransaction { + transaction.Transaction.SetTransactionMemo(memo) + return transaction +} + +func (transaction *TokenRevokeKycTransaction) GetTransactionValidDuration() time.Duration { + return transaction.Transaction.GetTransactionValidDuration() +} + +// SetTransactionValidDuration sets the valid duration for this TokenRevokeKycTransaction. +func (transaction *TokenRevokeKycTransaction) SetTransactionValidDuration(duration time.Duration) *TokenRevokeKycTransaction { + transaction.Transaction.SetTransactionValidDuration(duration) + return transaction +} + +func (transaction *TokenRevokeKycTransaction) GetTransactionID() TransactionID { + return transaction.Transaction.GetTransactionID() +} + +// SetTransactionID sets the TransactionID for this TokenRevokeKycTransaction. +func (transaction *TokenRevokeKycTransaction) SetTransactionID(transactionID TransactionID) *TokenRevokeKycTransaction { + transaction.id = transactionID + transaction.Transaction.SetTransactionID(transactionID) + return transaction +} + +func (transaction *TokenRevokeKycTransaction) GetNodeID() AccountID { + return transaction.Transaction.GetNodeID() +} + +// SetNodeTokenID sets the node TokenID for this TokenRevokeKycTransaction. +func (transaction *TokenRevokeKycTransaction) SetNodeAccountID(nodeID AccountID) *TokenRevokeKycTransaction { + transaction.Transaction.SetNodeAccountID(nodeID) + return transaction +} diff --git a/token_transfer_transaction.go b/token_transfer_transaction.go new file mode 100644 index 000000000..881785c7f --- /dev/null +++ b/token_transfer_transaction.go @@ -0,0 +1 @@ +package hedera diff --git a/token_unfreeze_transaction.go b/token_unfreeze_transaction.go new file mode 100644 index 000000000..d06642c4a --- /dev/null +++ b/token_unfreeze_transaction.go @@ -0,0 +1,229 @@ +package hedera + +import ( + "time" + + "github.com/hashgraph/hedera-sdk-go/proto" +) + +// Unfreezes transfers of the specified token for the account. Must be signed by the Token's freezeKey. +// 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 Freeze Key is defined, the transaction will resolve to TOKEN_HAS_NO_FREEZE_KEY. +// Once executed the Account is marked as Unfrozen and will be able to receive or send tokens. The +// operation is idempotent. +type TokenUnfreezeTransaction struct { + Transaction + pb *proto.TokenUnfreezeAccountTransactionBody +} + +func NewTokenUnfreezeTransaction() *TokenUnfreezeTransaction { + pb := &proto.TokenUnfreezeAccountTransactionBody{} + + transaction := TokenUnfreezeTransaction{ + pb: pb, + Transaction: newTransaction(), + } + + return &transaction +} + +// The token for which this account will be unfrozen. If token does not exist, transaction results in INVALID_TOKEN_ID +func (transaction *TokenUnfreezeTransaction) SetTokenID(tokenID TokenID) *TokenUnfreezeTransaction { + transaction.pb.Token = tokenID.toProtobuf() + return transaction +} + +// The account to be unfrozen +func (transaction *TokenUnfreezeTransaction) SetAccountID(accountID AccountID) *TokenUnfreezeTransaction { + transaction.pb.Account = accountID.toProtobuf() + return transaction +} + +// +// The following methods must be copy-pasted/overriden at the bottom of **every** _transaction.go file +// We override the embedded fluent setter methods to return the outer type +// + +func tokenUnfreezeTransaction_getMethod(request request, channel *channel) method { + return method{ + transaction: channel.getToken().UnfreezeTokenAccount, + } +} + +func (transaction *TokenUnfreezeTransaction) IsFrozen() bool { + return transaction.isFrozen() +} + +// Sign uses the provided privateKey to sign the transaction. +func (transaction *TokenUnfreezeTransaction) Sign( + privateKey PrivateKey, +) *TokenUnfreezeTransaction { + return transaction.SignWith(privateKey.PublicKey(), privateKey.Sign) +} + +func (transaction *TokenUnfreezeTransaction) SignWithOperator( + client *Client, +) (*TokenUnfreezeTransaction, error) { + // If the transaction is not signed by the operator, we need + // to sign the transaction with the operator + + if client.operator == nil { + return nil, errClientOperatorSigning + } + + if !transaction.IsFrozen() { + transaction.UnfreezeWith(client) + } + + return transaction.SignWith(client.operator.publicKey, client.operator.signer), nil +} + +// SignWith executes the TransactionSigner and adds the resulting signature data to the Transaction's signature map +// with the publicKey as the map key. +func (transaction *TokenUnfreezeTransaction) SignWith( + publicKey PublicKey, + signer TransactionSigner, +) *TokenUnfreezeTransaction { + if !transaction.IsFrozen() { + transaction.Unfreeze() + } + + if transaction.keyAlreadySigned(publicKey) { + return transaction + } + + for index := 0; index < len(transaction.transactions); index++ { + signature := signer(transaction.transactions[index].GetBodyBytes()) + + transaction.signatures[index].SigPair = append( + transaction.signatures[index].SigPair, + publicKey.toSignaturePairProtobuf(signature), + ) + } + + return transaction +} + +// Execute executes the Transaction with the provided client +func (transaction *TokenUnfreezeTransaction) Execute( + client *Client, +) (TransactionResponse, error) { + if !transaction.IsFrozen() { + transaction.UnfreezeWith(client) + } + + transactionID := transaction.id + + if !client.GetOperatorID().isZero() && client.GetOperatorID().equals(transactionID.TokenID) { + transaction.SignWith( + client.GetOperatorKey(), + client.operator.signer, + ) + } + + resp, err := execute( + client, + request{ + transaction: &transaction.Transaction, + }, + transaction_shouldRetry, + transaction_makeRequest, + transaction_advanceRequest, + transaction_getNodeId, + tokenUnfreezeTransaction_getMethod, + transaction_mapResponseStatus, + transaction_mapResponse, + ) + + if err != nil { + return TransactionResponse{}, err + } + + return TransactionResponse{ + TransactionID: transaction.id, + NodeID: resp.transaction.NodeID, + }, nil +} + +func (transaction *TokenUnfreezeTransaction) onUnfreeze( + pbBody *proto.TransactionBody, +) bool { + pbBody.Data = &proto.TransactionBody_TokenUnfreeze{ + TokenUnfreeze: transaction.pb, + } + + return true +} + +func (transaction *TokenUnfreezeTransaction) Unfreeze() (*TokenUnfreezeTransaction, error) { + return transaction.UnfreezeWith(nil) +} + +func (transaction *TokenUnfreezeTransaction) UnfreezeWith(client *Client) (*TokenUnfreezeTransaction, error) { + transaction.initFee(client) + if err := transaction.initTransactionID(client); err != nil { + return transaction, err + } + + if !transaction.onUnfreeze(transaction.pbBody) { + return transaction, nil + } + + return transaction, transaction_unfreezeWith(&transaction.Transaction, client) +} + +func (transaction *TokenUnfreezeTransaction) GetMaxTransactionFee() Hbar { + return transaction.Transaction.GetMaxTransactionFee() +} + +// SetMaxTransactionFee sets the max transaction fee for this TokenUnfreezeTransaction. +func (transaction *TokenUnfreezeTransaction) SetMaxTransactionFee(fee Hbar) *TokenUnfreezeTransaction { + transaction.Transaction.SetMaxTransactionFee(fee) + return transaction +} + +func (transaction *TokenUnfreezeTransaction) GetTransactionMemo() string { + return transaction.Transaction.GetTransactionMemo() +} + +// SetTransactionMemo sets the memo for this TokenUnfreezeTransaction. +func (transaction *TokenUnfreezeTransaction) SetTransactionMemo(memo string) *TokenUnfreezeTransaction { + transaction.Transaction.SetTransactionMemo(memo) + return transaction +} + +func (transaction *TokenUnfreezeTransaction) GetTransactionValidDuration() time.Duration { + return transaction.Transaction.GetTransactionValidDuration() +} + +// SetTransactionValidDuration sets the valid duration for this TokenUnfreezeTransaction. +func (transaction *TokenUnfreezeTransaction) SetTransactionValidDuration(duration time.Duration) *TokenUnfreezeTransaction { + transaction.Transaction.SetTransactionValidDuration(duration) + return transaction +} + +func (transaction *TokenUnfreezeTransaction) GetTransactionID() TransactionID { + return transaction.Transaction.GetTransactionID() +} + +// SetTransactionID sets the TransactionID for this TokenUnfreezeTransaction. +func (transaction *TokenUnfreezeTransaction) SetTransactionID(transactionID TransactionID) *TokenUnfreezeTransaction { + transaction.id = transactionID + transaction.Transaction.SetTransactionID(transactionID) + return transaction +} + +func (transaction *TokenUnfreezeTransaction) GetNodeID() AccountID { + return transaction.Transaction.GetNodeID() +} + +// SetNodeTokenID sets the node TokenID for this TokenUnfreezeTransaction. +func (transaction *TokenUnfreezeTransaction) SetNodeAccountID(nodeID AccountID) *TokenUnfreezeTransaction { + transaction.Transaction.SetNodeAccountID(nodeID) + return transaction +} diff --git a/token_update_transaction.go b/token_update_transaction.go new file mode 100644 index 000000000..b223c0f58 --- /dev/null +++ b/token_update_transaction.go @@ -0,0 +1,293 @@ +package hedera + +import ( + "time" + + "github.com/hashgraph/hedera-sdk-go/proto" +) + +// Updates an already created Token. +// If no value is given for a field, that field is left unchanged. For an immutable tokens +// (that is, a token created without an adminKey), only the expiry may be updated. Setting any +// other field in that case will cause the transaction status to resolve to TOKEN_IS_IMMUTABlE. +type TokenUpdateTransaction struct { + Transaction + pb *proto.TokenUpdateTransactionBody +} + +func NewTokenUpdateTransaction() *TokenUpdateTransaction { + pb := &proto.TokenUpdateTransactionBody{} + + transaction := TokenUpdateTransaction{ + pb: pb, + Transaction: newTransaction(), + } + + return &transaction +} + +// The Token to be updated +func (transaction *TokenUpdateTransaction) SetTokenID(tokenID TokenID) *TokenUpdateTransaction { + transaction.pb.Token = tokenID.toProtobuf() + return transaction +} + +// The new Symbol of the Token. Must be UTF-8 capitalized alphabetical string identifying the token. +func (transaction *TokenUpdateTransaction) SetSymbol(symbol string) *TokenUpdateTransaction { + transaction.pb.Symbol = symbol + return transaction +} + +// The new Name of the Token. Must be a string of ASCII characters. +func (transaction *TokenUpdateTransaction) SetName(name string) *TokenUpdateTransaction { + transaction.pb.Name = name + return transaction +} + +// The new Treasury account of the Token. If the provided treasury account is not existing or +// deleted, the response will be INVALID_TREASURY_ACCOUNT_FOR_TOKEN. If successful, the Token +// balance held in the previous Treasury Account is transferred to the new one. +func (transaction *TokenUpdateTransaction) SetTreasury(treasury AccountID) *TokenUpdateTransaction { + transaction.pb.Treasury = treasury.toProtobuf() + return transaction +} + +// The new Admin key of the Token. If Token is immutable, transaction will resolve to +// TOKEN_IS_IMMUTABlE. +func (transaction *TokenUpdateTransaction) SetAdminKey(publicKey PublicKey) *TokenUpdateTransaction { + transaction.pb.AdminKey = publicKey.toProtoKey() + return transaction +} + +// The new KYC key of the Token. If Token does not have currently a KYC key, transaction will +// resolve to TOKEN_HAS_NO_KYC_KEY. +func (transaction *TokenUpdateTransaction) SetKycKey(publicKey PublicKey) *TokenUpdateTransaction { + transaction.pb.KycKey = publicKey.toProtoKey() + return transaction +} + +// The new Freeze key of the Token. If the Token does not have currently a Freeze key, transaction +// will resolve to TOKEN_HAS_NO_FREEZE_KEY. +func (transaction *TokenUpdateTransaction) SetFreezeKey(publicKey PublicKey) *TokenUpdateTransaction { + transaction.pb.FreezeKey = publicKey.toProtoKey() + return transaction +} + +// The new Wipe key of the Token. If the Token does not have currently a Wipe key, transaction +// will resolve to TOKEN_HAS_NO_WIPE_KEY. +func (transaction *TokenUpdateTransaction) SetWipeKey(publicKey PublicKey) *TokenUpdateTransaction { + transaction.pb.WipeKey = publicKey.toProtoKey() + return transaction +} + +// The new Supply key of the Token. If the Token does not have currently a Supply key, transaction +// will resolve to TOKEN_HAS_NO_SUPPLY_KEY. +func (transaction *TokenUpdateTransaction) SetSupplyKey(publicKey PublicKey) *TokenUpdateTransaction { + transaction.pb.SupplyKey = publicKey.toProtoKey() + return transaction +} + +// The new account which will be automatically charged to renew the token's expiration, at +// autoRenewPeriod interval. +func (transaction *TokenUpdateTransaction) SetAutoRenewAccount(autoRenewAccount AccountID) *TokenUpdateTransaction { + transaction.pb.AutoRenewAccount = autoRenewAccount.toProtobuf() + return transaction +} + +// The new interval at which the auto-renew account will be charged to extend the token's expiry. +func (transaction *TokenUpdateTransaction) SetAutoRenewPeriod(autoRenewPeriod uint64) *TokenUpdateTransaction { + transaction.pb.AutoRenewPeriod = autoRenewPeriod + return transaction +} + +// The new expiry time of the token. Expiry can be updated even if admin key is not set. If the +// provided expiry is earlier than the current token expiry, transaction wil resolve to +// INVALID_EXPIRATION_TIME +func (transaction *TokenUpdateTransaction) SetExpirationTime(expirationTime uint64) *TokenUpdateTransaction { + transaction.pb.Expiry = expirationTime + return transaction +} + +// +// The following methods must be copy-pasted/overriden at the bottom of **every** _transaction.go file +// We override the embedded fluent setter methods to return the outer type +// + +func tokenUpdateTransaction_getMethod(request request, channel *channel) method { + return method{ + transaction: channel.getToken().UpdateToken, + } +} + +func (transaction *TokenUpdateTransaction) IsFrozen() bool { + return transaction.isFrozen() +} + +// Sign uses the provided privateKey to sign the transaction. +func (transaction *TokenUpdateTransaction) Sign( + privateKey PrivateKey, +) *TokenUpdateTransaction { + return transaction.SignWith(privateKey.PublicKey(), privateKey.Sign) +} + +func (transaction *TokenUpdateTransaction) SignWithOperator( + client *Client, +) (*TokenUpdateTransaction, error) { + // If the transaction is not signed by the operator, we need + // to sign the transaction with the operator + + if client.operator == nil { + return nil, errClientOperatorSigning + } + + if !transaction.IsFrozen() { + transaction.FreezeWith(client) + } + + return transaction.SignWith(client.operator.publicKey, client.operator.signer), nil +} + +// SignWith executes the TransactionSigner and adds the resulting signature data to the Transaction's signature map +// with the publicKey as the map key. +func (transaction *TokenUpdateTransaction) SignWith( + publicKey PublicKey, + signer TransactionSigner, +) *TokenUpdateTransaction { + if !transaction.IsFrozen() { + transaction.Freeze() + } + + if transaction.keyAlreadySigned(publicKey) { + return transaction + } + + for index := 0; index < len(transaction.transactions); index++ { + signature := signer(transaction.transactions[index].GetBodyBytes()) + + transaction.signatures[index].SigPair = append( + transaction.signatures[index].SigPair, + publicKey.toSignaturePairProtobuf(signature), + ) + } + + return transaction +} + +// Execute executes the Transaction with the provided client +func (transaction *TokenUpdateTransaction) Execute( + client *Client, +) (TransactionResponse, error) { + if !transaction.IsFrozen() { + transaction.FreezeWith(client) + } + + transactionID := transaction.id + + if !client.GetOperatorID().isZero() && client.GetOperatorID().equals(transactionID.TokenID) { + transaction.SignWith( + client.GetOperatorKey(), + client.operator.signer, + ) + } + + resp, err := execute( + client, + request{ + transaction: &transaction.Transaction, + }, + transaction_shouldRetry, + transaction_makeRequest, + transaction_advanceRequest, + transaction_getNodeId, + tokenUpdateTransaction_getMethod, + transaction_mapResponseStatus, + transaction_mapResponse, + ) + + if err != nil { + return TransactionResponse{}, err + } + + return TransactionResponse{ + TransactionID: transaction.id, + NodeID: resp.transaction.NodeID, + }, nil +} + +func (transaction *TokenUpdateTransaction) onFreeze( + pbBody *proto.TransactionBody, +) bool { + pbBody.Data = &proto.TransactionBody_TokenUpdate{ + TokenUpdate: transaction.pb, + } + + return true +} + +func (transaction *TokenUpdateTransaction) Freeze() (*TokenUpdateTransaction, error) { + return transaction.FreezeWith(nil) +} + +func (transaction *TokenUpdateTransaction) FreezeWith(client *Client) (*TokenUpdateTransaction, error) { + transaction.initFee(client) + if err := transaction.initTransactionID(client); err != nil { + return transaction, err + } + + if !transaction.onFreeze(transaction.pbBody) { + return transaction, nil + } + + return transaction, transaction_freezeWith(&transaction.Transaction, client) +} + +func (transaction *TokenUpdateTransaction) GetMaxTransactionFee() Hbar { + return transaction.Transaction.GetMaxTransactionFee() +} + +// SetMaxTransactionFee sets the max transaction fee for this TokenUpdateTransaction. +func (transaction *TokenUpdateTransaction) SetMaxTransactionFee(fee Hbar) *TokenUpdateTransaction { + transaction.Transaction.SetMaxTransactionFee(fee) + return transaction +} + +func (transaction *TokenUpdateTransaction) GetTransactionMemo() string { + return transaction.Transaction.GetTransactionMemo() +} + +// SetTransactionMemo sets the memo for this TokenUpdateTransaction. +func (transaction *TokenUpdateTransaction) SetTransactionMemo(memo string) *TokenUpdateTransaction { + transaction.Transaction.SetTransactionMemo(memo) + return transaction +} + +func (transaction *TokenUpdateTransaction) GetTransactionValidDuration() time.Duration { + return transaction.Transaction.GetTransactionValidDuration() +} + +// SetTransactionValidDuration sets the valid duration for this TokenUpdateTransaction. +func (transaction *TokenUpdateTransaction) SetTransactionValidDuration(duration time.Duration) *TokenUpdateTransaction { + transaction.Transaction.SetTransactionValidDuration(duration) + return transaction +} + +func (transaction *TokenUpdateTransaction) GetTransactionID() TransactionID { + return transaction.Transaction.GetTransactionID() +} + +// SetTransactionID sets the TransactionID for this TokenUpdateTransaction. +func (transaction *TokenUpdateTransaction) SetTransactionID(transactionID TransactionID) *TokenUpdateTransaction { + transaction.id = transactionID + transaction.Transaction.SetTransactionID(transactionID) + return transaction +} + +func (transaction *TokenUpdateTransaction) GetNodeID() AccountID { + return transaction.Transaction.GetNodeID() +} + +// SetNodeTokenID sets the node TokenID for this TokenUpdateTransaction. +func (transaction *TokenUpdateTransaction) SetNodeAccountID(nodeID AccountID) *TokenUpdateTransaction { + transaction.Transaction.SetNodeAccountID(nodeID) + return transaction +} diff --git a/token_wipe_transaction.go b/token_wipe_transaction.go new file mode 100644 index 000000000..0a036fa6e --- /dev/null +++ b/token_wipe_transaction.go @@ -0,0 +1,245 @@ +package hedera + +import ( + "time" + + "github.com/hashgraph/hedera-sdk-go/proto" +) + +// Wipes the provided amount of tokens from the specified Account. Must be signed by the Token's +// Wipe 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 Wipe Key is not present in the Token, transaction results in TOKEN_HAS_NO_WIPE_KEY. +// If the provided account is the Token's Treasury Account, transaction results in +// CANNOT_WIPE_TOKEN_TREASURY_ACCOUNT +// On success, tokens are removed from the account and the total supply of the token is decreased +// by the wiped amount. +// +// The amount provided is in the lowest denomination possible. Example: +// Token A has 2 decimals. In order to wipe 100 tokens from account, one must provide amount of +// 10000. In order to wipe 100.55 tokens, one must provide amount of 10055. +type TokenWipeTransaction struct { + Transaction + pb *proto.TokenWipeAccountTransactionBody +} + +func NewTokenWipeTransaction() *TokenWipeTransaction { + pb := &proto.TokenWipeAccountTransactionBody{} + + transaction := TokenWipeTransaction{ + pb: pb, + Transaction: newTransaction(), + } + + return &transaction +} + +// The token for which the account will be wiped. If token does not exist, transaction results in +// INVALID_TOKEN_ID +func (transaction *TokenWipeTransaction) SetTokenID(tokenID TokenID) *TokenWipeTransaction { + transaction.pb.Token = tokenID.toProtobuf() + return transaction +} + +// The account to be wiped +func (transaction *TokenWipeTransaction) SetAccountID(accountID AccountID) *TokenWipeTransaction { + transaction.pb.Account = accountID.toProtobuf() + return transaction +} + +// The amount of tokens to wipe from the specified account. Amount must be a positive non-zero +// number in the lowest denomination possible, not bigger than the token balance of the account +// (0; balance] +func (transaction *TokenWipeTransaction) SetAmount(amount uint64) *TokenWipeTransaction { + transaction.pb.Amount = amount + return transaction +} + +// +// The following methods must be copy-pasted/overriden at the bottom of **every** _transaction.go file +// We override the embedded fluent setter methods to return the outer type +// + +func tokenWipeTransaction_getMethod(request request, channel *channel) method { + return method{ + transaction: channel.getToken().WipeTokenAccount, + } +} + +func (transaction *TokenWipeTransaction) IsFrozen() bool { + return transaction.isFrozen() +} + +// Sign uses the provided privateKey to sign the transaction. +func (transaction *TokenWipeTransaction) Sign( + privateKey PrivateKey, +) *TokenWipeTransaction { + return transaction.SignWith(privateKey.PublicKey(), privateKey.Sign) +} + +func (transaction *TokenWipeTransaction) SignWithOperator( + client *Client, +) (*TokenWipeTransaction, error) { + // If the transaction is not signed by the operator, we need + // to sign the transaction with the operator + + if client.operator == nil { + return nil, errClientOperatorSigning + } + + if !transaction.IsFrozen() { + transaction.FreezeWith(client) + } + + return transaction.SignWith(client.operator.publicKey, client.operator.signer), nil +} + +// SignWith executes the TransactionSigner and adds the resulting signature data to the Transaction's signature map +// with the publicKey as the map key. +func (transaction *TokenWipeTransaction) SignWith( + publicKey PublicKey, + signer TransactionSigner, +) *TokenWipeTransaction { + if !transaction.IsFrozen() { + transaction.Freeze() + } + + if transaction.keyAlreadySigned(publicKey) { + return transaction + } + + for index := 0; index < len(transaction.transactions); index++ { + signature := signer(transaction.transactions[index].GetBodyBytes()) + + transaction.signatures[index].SigPair = append( + transaction.signatures[index].SigPair, + publicKey.toSignaturePairProtobuf(signature), + ) + } + + return transaction +} + +// Execute executes the Transaction with the provided client +func (transaction *TokenWipeTransaction) Execute( + client *Client, +) (TransactionResponse, error) { + if !transaction.IsFrozen() { + transaction.FreezeWith(client) + } + + transactionID := transaction.id + + if !client.GetOperatorID().isZero() && client.GetOperatorID().equals(transactionID.TokenID) { + transaction.SignWith( + client.GetOperatorKey(), + client.operator.signer, + ) + } + + resp, err := execute( + client, + request{ + transaction: &transaction.Transaction, + }, + transaction_shouldRetry, + transaction_makeRequest, + transaction_advanceRequest, + transaction_getNodeId, + tokenWipeTransaction_getMethod, + transaction_mapResponseStatus, + transaction_mapResponse, + ) + + if err != nil { + return TransactionResponse{}, err + } + + return TransactionResponse{ + TransactionID: transaction.id, + NodeID: resp.transaction.NodeID, + }, nil +} + +func (transaction *TokenWipeTransaction) onFreeze( + pbBody *proto.TransactionBody, +) bool { + pbBody.Data = &proto.TransactionBody_TokenWipe{ + TokenWipe: transaction.pb, + } + + return true +} + +func (transaction *TokenWipeTransaction) Freeze() (*TokenWipeTransaction, error) { + return transaction.FreezeWith(nil) +} + +func (transaction *TokenWipeTransaction) FreezeWith(client *Client) (*TokenWipeTransaction, error) { + transaction.initFee(client) + if err := transaction.initTransactionID(client); err != nil { + return transaction, err + } + + if !transaction.onFreeze(transaction.pbBody) { + return transaction, nil + } + + return transaction, transaction_freezeWith(&transaction.Transaction, client) +} + +func (transaction *TokenWipeTransaction) GetMaxTransactionFee() Hbar { + return transaction.Transaction.GetMaxTransactionFee() +} + +// SetMaxTransactionFee sets the max transaction fee for this TokenWipeTransaction. +func (transaction *TokenWipeTransaction) SetMaxTransactionFee(fee Hbar) *TokenWipeTransaction { + transaction.Transaction.SetMaxTransactionFee(fee) + return transaction +} + +func (transaction *TokenWipeTransaction) GetTransactionMemo() string { + return transaction.Transaction.GetTransactionMemo() +} + +// SetTransactionMemo sets the memo for this TokenWipeTransaction. +func (transaction *TokenWipeTransaction) SetTransactionMemo(memo string) *TokenWipeTransaction { + transaction.Transaction.SetTransactionMemo(memo) + return transaction +} + +func (transaction *TokenWipeTransaction) GetTransactionValidDuration() time.Duration { + return transaction.Transaction.GetTransactionValidDuration() +} + +// SetTransactionValidDuration sets the valid duration for this TokenWipeTransaction. +func (transaction *TokenWipeTransaction) SetTransactionValidDuration(duration time.Duration) *TokenWipeTransaction { + transaction.Transaction.SetTransactionValidDuration(duration) + return transaction +} + +func (transaction *TokenWipeTransaction) GetTransactionID() TransactionID { + return transaction.Transaction.GetTransactionID() +} + +// SetTransactionID sets the TransactionID for this TokenWipeTransaction. +func (transaction *TokenWipeTransaction) SetTransactionID(transactionID TransactionID) *TokenWipeTransaction { + transaction.id = transactionID + transaction.Transaction.SetTransactionID(transactionID) + return transaction +} + +func (transaction *TokenWipeTransaction) GetNodeID() AccountID { + return transaction.Transaction.GetNodeID() +} + +// SetNodeTokenID sets the node TokenID for this TokenWipeTransaction. +func (transaction *TokenWipeTransaction) SetNodeAccountID(nodeID AccountID) *TokenWipeTransaction { + transaction.Transaction.SetNodeAccountID(nodeID) + return transaction +}