github.com/hashgraph/hedera-sdk-go/v2@v2.48.0/examples/custom_fees/main.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  
     7  	"github.com/hashgraph/hedera-sdk-go/v2"
     8  )
     9  
    10  func main() {
    11  	var client *hedera.Client
    12  	var err error
    13  
    14  	// Retrieving network type from environment variable HEDERA_NETWORK
    15  	client, err = hedera.ClientForName(os.Getenv("HEDERA_NETWORK"))
    16  	if err != nil {
    17  		panic(fmt.Sprintf("%v : error creating client", err))
    18  	}
    19  
    20  	// Retrieving operator ID from environment variable OPERATOR_ID
    21  	operatorAccountID, err := hedera.AccountIDFromString(os.Getenv("OPERATOR_ID"))
    22  	if err != nil {
    23  		panic(fmt.Sprintf("%v : error converting string to AccountID", err))
    24  	}
    25  
    26  	// Retrieving operator key from environment variable OPERATOR_KEY
    27  	operatorKey, err := hedera.PrivateKeyFromString(os.Getenv("OPERATOR_KEY"))
    28  	if err != nil {
    29  		panic(fmt.Sprintf("%v : error converting string to PrivateKey", err))
    30  	}
    31  
    32  	// Setting the client operator ID and key
    33  	client.SetOperator(operatorAccountID, operatorKey)
    34  
    35  	// Generate new key to be used with new account
    36  	aliceKey, err := hedera.GeneratePrivateKey()
    37  	if err != nil {
    38  		panic(fmt.Sprintf("%v : error generating PrivateKey", err))
    39  	}
    40  
    41  	// Create three accounts, Alice, Bob, and Charlie.  Alice will be the treasury for our example token.
    42  	// Fees only apply in transactions not involving the treasury, so we need two other accounts.
    43  
    44  	aliceAccountCreate, err := hedera.NewAccountCreateTransaction().
    45  		SetInitialBalance(hedera.NewHbar(10)).
    46  		SetKey(aliceKey).
    47  		FreezeWith(client)
    48  	if err != nil {
    49  		panic(fmt.Sprintf("%v : error freezing account create for alice", err))
    50  	}
    51  
    52  	aliceAccountCreate.Sign(aliceKey)
    53  	resp, err := aliceAccountCreate.Execute(client)
    54  	if err != nil {
    55  		panic(fmt.Sprintf("%v : error executing account create for alice", err))
    56  	}
    57  
    58  	receipt, err := resp.GetReceipt(client)
    59  	if err != nil {
    60  		panic(fmt.Sprintf("%v : error getting receipt for alice account create", err))
    61  	}
    62  
    63  	var aliceId hedera.AccountID
    64  	if receipt.AccountID != nil {
    65  		aliceId = *receipt.AccountID
    66  	} else {
    67  		panic("Receipt didn't return alice's ID")
    68  	}
    69  
    70  	bobKey, err := hedera.GeneratePrivateKey()
    71  	if err != nil {
    72  		panic(fmt.Sprintf("%v : error generating PrivateKey", err))
    73  	}
    74  
    75  	bobAccountCreate, err := hedera.NewAccountCreateTransaction().
    76  		SetInitialBalance(hedera.NewHbar(10)).
    77  		SetKey(bobKey).
    78  		FreezeWith(client)
    79  	if err != nil {
    80  		panic(fmt.Sprintf("%v : error freezing account create for bob", err))
    81  	}
    82  
    83  	bobAccountCreate.Sign(bobKey)
    84  	resp, err = bobAccountCreate.Execute(client)
    85  	if err != nil {
    86  		panic(fmt.Sprintf("%v : error executing account create for bob", err))
    87  	}
    88  
    89  	receipt, err = resp.GetReceipt(client)
    90  	if err != nil {
    91  		panic(fmt.Sprintf("%v : error getting receipt for bob account create", err))
    92  	}
    93  
    94  	var bobId hedera.AccountID
    95  	if receipt.AccountID != nil {
    96  		bobId = *receipt.AccountID
    97  	} else {
    98  		panic("Receipt didn't return bob's ID")
    99  	}
   100  
   101  	charlieKey, err := hedera.GeneratePrivateKey()
   102  	if err != nil {
   103  		panic(fmt.Sprintf("%v : error generating PrivateKey", err))
   104  	}
   105  
   106  	charlieAccountCreate, err := hedera.NewAccountCreateTransaction().
   107  		SetInitialBalance(hedera.NewHbar(10)).
   108  		SetKey(charlieKey).
   109  		FreezeWith(client)
   110  	if err != nil {
   111  		panic(fmt.Sprintf("%v : error freezing account create for charlie", err))
   112  	}
   113  
   114  	charlieAccountCreate.Sign(aliceKey)
   115  	resp, err = charlieAccountCreate.Execute(client)
   116  	if err != nil {
   117  		panic(fmt.Sprintf("%v : error executing account create for charlie", err))
   118  	}
   119  
   120  	receipt, err = resp.GetReceipt(client)
   121  	if err != nil {
   122  		panic(fmt.Sprintf("%v : error getting receipt for charlie account create", err))
   123  	}
   124  
   125  	var charlieId hedera.AccountID
   126  	if receipt.AccountID != nil {
   127  		charlieId = *receipt.AccountID
   128  	} else {
   129  		panic("Receipt didn't return charlie's ID")
   130  	}
   131  
   132  	println("Alice:", aliceId.String())
   133  	println("Bob:", bobId.String())
   134  	println("Charlie:", charlieId.String())
   135  
   136  	// Let's start with a custom fee list of 1 fixed fee.  A custom fee list can be a list of up to
   137  	// 10 custom fees, where each fee is a fixed fee or a fractional fee.
   138  	// This fixed fee will mean that every time Bob transfers any number of tokens to Charlie,
   139  	// Alice will collect 1 Hbar from each account involved in the transaction who is SENDING
   140  	// the Token (in this case, Bob).
   141  
   142  	customHbarFee := hedera.NewCustomFixedFee().
   143  		SetHbarAmount(hedera.NewHbar(1)).
   144  		SetFeeCollectorAccountID(aliceId)
   145  
   146  	// In this example the fee is in Hbar, but you can charge a fixed fee in a token if you'd like.
   147  	// EG, you can make it so that each time an account transfers Foo tokens,
   148  	// they must pay a fee in Bar tokens to the fee collecting account.
   149  	// To charge a fixed fee in tokens, instead of calling setHbarAmount(), call
   150  	// setDenominatingTokenId(tokenForFee) and setAmount(tokenFeeAmount).
   151  
   152  	// Setting the feeScheduleKey to Alice's key will enable Alice to change the custom
   153  	// fees list on this token later using the TokenFeeScheduleUpdateTransaction.
   154  	// We will create an initial supply of 100 of these tokens.
   155  
   156  	tokenCreate, err := hedera.NewTokenCreateTransaction().
   157  		// Token name and symbol are only things required to create a token
   158  		SetTokenName("Example Token").
   159  		SetTokenSymbol("EX").
   160  		// The key which can perform update/delete operations on the token. If empty, the token can be
   161  		// perceived as immutable (not being able to be updated/deleted)
   162  		SetAdminKey(aliceKey).
   163  		// The key which can change the supply of a token. The key is used to sign Token Mint/Burn
   164  		// operations
   165  		SetSupplyKey(aliceKey).
   166  		// The key which can change the token's custom fee schedule; must sign a TokenFeeScheduleUpdate
   167  		// transaction
   168  		SetFeeScheduleKey(aliceKey).
   169  		// The account which will act as a treasury for the token. This account
   170  		// will receive the specified initial supply or the newly minted NFTs
   171  		SetTreasuryAccountID(aliceId).
   172  		// The custom fees to be assessed during a CryptoTransfer that transfers units of this token
   173  		SetCustomFees([]hedera.Fee{*customHbarFee}).
   174  		// Specifies the initial supply of tokens to be put in circulation. The
   175  		// initial supply is sent to the Treasury Account.
   176  		SetInitialSupply(100).
   177  		FreezeWith(client)
   178  	if err != nil {
   179  		panic(fmt.Sprintf("%v : error freezing token create transaction", err))
   180  	}
   181  
   182  	// Sign with alice's key before executing
   183  	tokenCreate.Sign(aliceKey)
   184  	resp, err = tokenCreate.Execute(client)
   185  	if err != nil {
   186  		panic(fmt.Sprintf("%v : error executing token create transaction", err))
   187  	}
   188  
   189  	// Get receipt to make sure the transaction passed through
   190  	receipt, err = resp.GetReceipt(client)
   191  	if err != nil {
   192  		panic(fmt.Sprintf("%v : error getting receipt for token create transaction", err))
   193  	}
   194  
   195  	// Get the token out of the receipt
   196  	var tokenId hedera.TokenID
   197  	if receipt.TokenID != nil {
   198  		tokenId = *receipt.TokenID
   199  	} else {
   200  		println("Token ID missing in the receipt")
   201  	}
   202  
   203  	println("TokenID:", tokenId.String())
   204  
   205  	tokenInfo1, err := hedera.NewTokenInfoQuery().
   206  		SetTokenID(tokenId).
   207  		Execute(client)
   208  
   209  	println("Custom Fees according to TokenInfoQuery:")
   210  	for _, i := range tokenInfo1.CustomFees {
   211  		switch t := i.(type) {
   212  		case hedera.CustomFixedFee:
   213  			println(t.String())
   214  		}
   215  	}
   216  
   217  	// We must associate the token with Bob and Charlie before they can trade in it.
   218  
   219  	tokenAssociate, err := hedera.NewTokenAssociateTransaction().
   220  		// Account to associate token with
   221  		SetAccountID(bobId).
   222  		// The token to associate with
   223  		SetTokenIDs(tokenId).
   224  		FreezeWith(client)
   225  	if err != nil {
   226  		panic(fmt.Sprintf("%v : error freezing token associate transaction for bob", err))
   227  	}
   228  
   229  	// Signing with bob's key
   230  	tokenAssociate.Sign(bobKey)
   231  	resp, err = tokenAssociate.Execute(client)
   232  	if err != nil {
   233  		panic(fmt.Sprintf("%v : error executing token associate transaction for bob", err))
   234  	}
   235  
   236  	_, err = resp.GetReceipt(client)
   237  	if err != nil {
   238  		panic(fmt.Sprintf("%v : error getting receipt for token associate transaction for bob", err))
   239  	}
   240  
   241  	// Associating charlie's account with the token
   242  	tokenAssociate, err = hedera.NewTokenAssociateTransaction().
   243  		// Account to associate token with
   244  		SetAccountID(charlieId).
   245  		// The token to associate with
   246  		SetTokenIDs(tokenId).
   247  		FreezeWith(client)
   248  	if err != nil {
   249  		panic(fmt.Sprintf("%v : error freezing token associate transaction for charlie", err))
   250  	}
   251  
   252  	// Signing with charlie's key
   253  	tokenAssociate.Sign(charlieKey)
   254  	resp, err = tokenAssociate.Execute(client)
   255  	if err != nil {
   256  		panic(fmt.Sprintf("%v : error executing token associate transaction for charlie", err))
   257  	}
   258  
   259  	_, err = resp.GetReceipt(client)
   260  	if err != nil {
   261  		panic(fmt.Sprintf("%v : error getting receipt for token associate transaction for charlie", err))
   262  	}
   263  
   264  	// Give all 100 tokens to Bob
   265  	transferTransaction, err := hedera.NewTransferTransaction().
   266  		// The 100 tokens being given to bob
   267  		AddTokenTransfer(tokenId, bobId, 100).
   268  		// Have to take the 100 tokens from alice by negating the 100
   269  		AddTokenTransfer(tokenId, aliceId, -100).
   270  		FreezeWith(client)
   271  	if err != nil {
   272  		panic(fmt.Sprintf("%v : error freezing token transfer transaction for alice", err))
   273  	}
   274  
   275  	// Have to sign with alice's key as we are taking alice's tokens
   276  	transferTransaction.Sign(aliceKey)
   277  	resp, err = transferTransaction.Execute(client)
   278  	if err != nil {
   279  		panic(fmt.Sprintf("%v : error executing token transfer transaction for alice", err))
   280  	}
   281  
   282  	// Make sure the transaction passed through
   283  	_, err = resp.GetReceipt(client)
   284  	if err != nil {
   285  		panic(fmt.Sprintf("%v : error getting receipt for token transfer transaction for alice", err))
   286  	}
   287  
   288  	// Check alice's balance before Bob transfers 20 tokens to Charlie
   289  	// This is a free query
   290  	aliceBalance1, err := hedera.NewAccountBalanceQuery().
   291  		SetAccountID(aliceId).
   292  		Execute(client)
   293  	if err != nil {
   294  		panic(fmt.Sprintf("%v : error getting account balance 1 for alice", err))
   295  	}
   296  
   297  	println("Alice's Hbar balance before Bob transfers 20 tokens to Charlie:", aliceBalance1.Hbars.String())
   298  
   299  	// Transfer 20 tokens from bob to charlie
   300  	transferTransaction, err = hedera.NewTransferTransaction().
   301  		// Taking away 20 tokens from bob
   302  		AddTokenTransfer(tokenId, bobId, -20).
   303  		// Giving 20 to charlie
   304  		AddTokenTransfer(tokenId, charlieId, 20).
   305  		FreezeWith(client)
   306  	if err != nil {
   307  		panic(fmt.Sprintf("%v : error freezing token transfer transaction for bob", err))
   308  	}
   309  
   310  	// As we are taking from bob, bob has to sign this.
   311  	transferTransaction.Sign(bobKey)
   312  	resp, err = transferTransaction.Execute(client)
   313  	if err != nil {
   314  		panic(fmt.Sprintf("%v : error executing token transfer transaction for bob", err))
   315  	}
   316  
   317  	// Getting the record to show the assessed custom fees
   318  	record1, err := resp.GetRecord(client)
   319  	if err != nil {
   320  		panic(fmt.Sprintf("%v : error getting record for token transfer transaction for bob", err))
   321  	}
   322  
   323  	// Query to check alice's balance
   324  	aliceBalance2, err := hedera.NewAccountBalanceQuery().
   325  		SetAccountID(aliceId).
   326  		Execute(client)
   327  	if err != nil {
   328  		panic(fmt.Sprintf("%v : error getting account balance 2 for alice", err))
   329  	}
   330  
   331  	println("Alice's Hbar balance after Bob transfers 20 tokens to Charlie:", aliceBalance2.Hbars.String())
   332  	println("Assessed fees according to transaction record:")
   333  	for _, k := range record1.AssessedCustomFees {
   334  		println(k.String())
   335  	}
   336  
   337  	// Let's use the TokenUpdateFeeScheduleTransaction with Alice's key to change the custom fees on our token.
   338  	// TokenUpdateFeeScheduleTransaction will replace the list of fees that apply to the token with
   339  	// an entirely new list.  Let's charge a 10% fractional fee.  This means that when Bob attempts to transfer
   340  	// 20 tokens to Charlie, 10% of the tokens he attempts to transfer (2 in this case) will be transferred to
   341  	// Alice instead.
   342  
   343  	// Fractional fees default to FeeAssessmentMethod.INCLUSIVE, which is the behavior described above.
   344  	// If you set the assessment method to EXCLUSIVE, then when Bob attempts to transfer 20 tokens to Charlie,
   345  	// Charlie will receive all 20 tokens, and Bob will be charged an _additional_ 10% fee which
   346  	// will be transferred to Alice.
   347  
   348  	customFractionalFee := hedera.NewCustomFractionalFee().
   349  		SetNumerator(1).
   350  		SetDenominator(10).
   351  		// The minimum amount to assess
   352  		SetMin(1).
   353  		// The maximum amount to assess (zero implies no maximum)
   354  		SetMax(10).
   355  		// The account to receive the custom fee
   356  		SetFeeCollectorAccountID(aliceId)
   357  
   358  	tokenFeeUpdate, err := hedera.NewTokenFeeScheduleUpdateTransaction().
   359  		// The token for which the custom fee will be updated
   360  		SetTokenID(tokenId).
   361  		// The updated custom fee
   362  		SetCustomFees([]hedera.Fee{*customFractionalFee}).
   363  		FreezeWith(client)
   364  	if err != nil {
   365  		panic(fmt.Sprintf("%v : error freezing token fee update", err))
   366  	}
   367  
   368  	// As the token is owned by alice and all keys are set to alice's key we have to sign with that
   369  	tokenFeeUpdate.Sign(aliceKey)
   370  	resp, err = tokenFeeUpdate.Execute(client)
   371  	if err != nil {
   372  		panic(fmt.Sprintf("%v : error executing token fee update", err))
   373  	}
   374  
   375  	_, err = resp.GetReceipt(client)
   376  	if err != nil {
   377  		panic(fmt.Sprintf("%v : error getting receipt for token fee update", err))
   378  	}
   379  
   380  	// Get token info, we can check if the custom fee is updated
   381  	tokenInfo2, err := hedera.NewTokenInfoQuery().
   382  		SetTokenID(tokenId).
   383  		Execute(client)
   384  	if err != nil {
   385  		panic(fmt.Sprintf("%v : error getting token info 2", err))
   386  	}
   387  
   388  	println("Custom Fees according to TokenInfoQuery:")
   389  	for _, i := range tokenInfo2.CustomFees {
   390  		switch t := i.(type) {
   391  		case hedera.CustomFractionalFee:
   392  			println(t.String())
   393  		}
   394  	}
   395  
   396  	// Another account balance query to check alice's token balance before Bob transfers 20 tokens to Charlie
   397  	aliceBalance3, err := hedera.NewAccountBalanceQuery().
   398  		SetAccountID(aliceId).
   399  		Execute(client)
   400  	if err != nil {
   401  		panic(fmt.Sprintf("%v : error getting account balance 3 for alice", err))
   402  	}
   403  
   404  	println("Alice's token balance before Bob transfers 20 tokens to Charlie:", aliceBalance3.Tokens.Get(tokenId))
   405  
   406  	// Once again transfer 20 tokens from bob to charlie
   407  	transferTransaction, err = hedera.NewTransferTransaction().
   408  		AddTokenTransfer(tokenId, bobId, -20).
   409  		AddTokenTransfer(tokenId, charlieId, 20).
   410  		FreezeWith(client)
   411  	if err != nil {
   412  		panic(fmt.Sprintf("%v : error freezing token transfer transaction for bob", err))
   413  	}
   414  
   415  	// Bob's is losing 20 tokens again. so he has to sign this transfer
   416  	transferTransaction.Sign(bobKey)
   417  	resp, err = transferTransaction.Execute(client)
   418  	if err != nil {
   419  		panic(fmt.Sprintf("%v : error executing token transfer transaction for bob", err))
   420  	}
   421  
   422  	record2, err := resp.GetRecord(client)
   423  	if err != nil {
   424  		panic(fmt.Sprintf("%v : error getting record for token transfer transaction for bob", err))
   425  	}
   426  
   427  	// Checking alice's token balance again
   428  	aliceBalance4, err := hedera.NewAccountBalanceQuery().
   429  		SetAccountID(aliceId).
   430  		Execute(client)
   431  	if err != nil {
   432  		panic(fmt.Sprintf("%v : error getting account balance 2 for alice", err))
   433  	}
   434  
   435  	println("Alice's token balance after Bob transfers 20 tokens to Charlie:", aliceBalance4.Tokens.Get(tokenId))
   436  	println("Token transfers according to transaction record:")
   437  	for token, transfer := range record2.TokenTransfers {
   438  		tokenT := ""
   439  		for _, t := range transfer {
   440  			tokenT = tokenT + " " + t.String()
   441  		}
   442  		println("for token", token.String()+":", tokenT)
   443  	}
   444  	println("Assessed fees according to transaction record:")
   445  	for _, k := range record2.AssessedCustomFees {
   446  		println(k.String())
   447  	}
   448  
   449  	//Clean up
   450  
   451  	tokenDelete, _ := hedera.NewTokenDeleteTransaction().
   452  		SetTokenID(tokenId).
   453  		FreezeWith(client)
   454  
   455  	tokenDelete.Sign(aliceKey)
   456  	resp, _ = tokenDelete.Execute(client)
   457  	_, _ = resp.GetReceipt(client)
   458  
   459  	accDelete, _ := hedera.NewAccountDeleteTransaction().
   460  		SetTransactionID(hedera.TransactionIDGenerate(charlieId)).
   461  		SetTransferAccountID(client.GetOperatorAccountID()).
   462  		SetAccountID(charlieId).
   463  		FreezeWith(client)
   464  
   465  	accDelete.Sign(charlieKey)
   466  	resp, err = accDelete.Execute(client)
   467  	_, _ = resp.GetReceipt(client)
   468  
   469  	accDelete, _ = hedera.NewAccountDeleteTransaction().
   470  		SetTransactionID(hedera.TransactionIDGenerate(bobId)).
   471  		SetTransferAccountID(client.GetOperatorAccountID()).
   472  		SetAccountID(bobId).
   473  		FreezeWith(client)
   474  
   475  	accDelete.Sign(bobKey)
   476  	resp, _ = accDelete.Execute(client)
   477  	_, _ = resp.GetReceipt(client)
   478  
   479  	accDelete, _ = hedera.NewAccountDeleteTransaction().
   480  		SetTransactionID(hedera.TransactionIDGenerate(aliceId)).
   481  		SetTransferAccountID(client.GetOperatorAccountID()).
   482  		SetAccountID(aliceId).
   483  		FreezeWith(client)
   484  
   485  	accDelete.Sign(aliceKey)
   486  	resp, _ = accDelete.Execute(client)
   487  	_, _ = resp.GetReceipt(client)
   488  
   489  	_ = client.Close()
   490  }