github.com/algorand/go-algorand-sdk@v1.24.0/transaction/transaction.go (about)

     1  package transaction
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/base64"
     6  	"fmt"
     7  
     8  	"github.com/algorand/go-algorand-sdk/crypto"
     9  	"github.com/algorand/go-algorand-sdk/encoding/msgpack"
    10  	"github.com/algorand/go-algorand-sdk/types"
    11  )
    12  
    13  // MinTxnFee is v5 consensus params, in microAlgos
    14  const MinTxnFee = 1000
    15  
    16  // NumOfAdditionalBytesAfterSigning is the number of bytes added to a txn after signing it
    17  const NumOfAdditionalBytesAfterSigning = 75
    18  
    19  // MakePaymentTxn constructs a payment transaction using the passed parameters.
    20  // `from` and `to` addresses should be checksummed, human-readable addresses
    21  // fee is fee per byte as received from algod SuggestedFee API call
    22  // Deprecated: next major version will use a Params object, see package future
    23  func MakePaymentTxn(from, to string, fee, amount, firstRound, lastRound uint64, note []byte, closeRemainderTo, genesisID string, genesisHash []byte) (types.Transaction, error) {
    24  	// Decode from address
    25  	fromAddr, err := types.DecodeAddress(from)
    26  	if err != nil {
    27  		return types.Transaction{}, err
    28  	}
    29  
    30  	// Decode to address
    31  	toAddr, err := types.DecodeAddress(to)
    32  	if err != nil {
    33  		return types.Transaction{}, err
    34  	}
    35  
    36  	// Decode the CloseRemainderTo address, if present
    37  	var closeRemainderToAddr types.Address
    38  	if closeRemainderTo != "" {
    39  		closeRemainderToAddr, err = types.DecodeAddress(closeRemainderTo)
    40  		if err != nil {
    41  			return types.Transaction{}, err
    42  		}
    43  	}
    44  
    45  	// Decode GenesisHash
    46  	if len(genesisHash) == 0 {
    47  		return types.Transaction{}, fmt.Errorf("payment transaction must contain a genesisHash")
    48  	}
    49  
    50  	var gh types.Digest
    51  	copy(gh[:], genesisHash)
    52  
    53  	// Build the transaction
    54  	tx := types.Transaction{
    55  		Type: types.PaymentTx,
    56  		Header: types.Header{
    57  			Sender:      fromAddr,
    58  			Fee:         types.MicroAlgos(fee),
    59  			FirstValid:  types.Round(firstRound),
    60  			LastValid:   types.Round(lastRound),
    61  			Note:        note,
    62  			GenesisID:   genesisID,
    63  			GenesisHash: gh,
    64  		},
    65  		PaymentTxnFields: types.PaymentTxnFields{
    66  			Receiver:         toAddr,
    67  			Amount:           types.MicroAlgos(amount),
    68  			CloseRemainderTo: closeRemainderToAddr,
    69  		},
    70  	}
    71  
    72  	// Update fee
    73  	eSize, err := EstimateSize(tx)
    74  	if err != nil {
    75  		return types.Transaction{}, err
    76  	}
    77  	tx.Fee = types.MicroAlgos(eSize * fee)
    78  
    79  	if tx.Fee < MinTxnFee {
    80  		tx.Fee = MinTxnFee
    81  	}
    82  
    83  	return tx, nil
    84  }
    85  
    86  // MakePaymentTxnWithFlatFee constructs a payment transaction using the passed parameters.
    87  // `from` and `to` addresses should be checksummed, human-readable addresses
    88  // fee is a flat fee
    89  // Deprecated: next major version will use a Params object, see package future
    90  func MakePaymentTxnWithFlatFee(from, to string, fee, amount, firstRound, lastRound uint64, note []byte, closeRemainderTo, genesisID string, genesisHash []byte) (types.Transaction, error) {
    91  	tx, err := MakePaymentTxn(from, to, fee, amount, firstRound, lastRound, note, closeRemainderTo, genesisID, genesisHash)
    92  	if err != nil {
    93  		return types.Transaction{}, err
    94  	}
    95  	tx.Fee = types.MicroAlgos(fee)
    96  
    97  	if tx.Fee < MinTxnFee {
    98  		tx.Fee = MinTxnFee
    99  	}
   100  
   101  	return tx, nil
   102  }
   103  
   104  // MakeKeyRegTxn constructs a keyreg transaction using the passed parameters.
   105  // - account is a checksummed, human-readable address for which we register the given participation key.
   106  // - fee is fee per byte as received from algod SuggestedFee API call.
   107  // - firstRound is the first round this txn is valid (txn semantics unrelated to key registration)
   108  // - lastRound is the last round this txn is valid
   109  // - note is a byte array
   110  // - genesis id corresponds to the id of the network
   111  // - genesis hash corresponds to the base64-encoded hash of the genesis of the network
   112  // KeyReg parameters:
   113  // - votePK is a base64-encoded string corresponding to the root participation public key
   114  // - selectionKey is a base64-encoded string corresponding to the vrf public key
   115  // - voteFirst is the first round this participation key is valid
   116  // - voteLast is the last round this participation key is valid
   117  // - voteKeyDilution is the dilution for the 2-level participation key
   118  // Deprecated: next major version will use a Params object, see package future
   119  func MakeKeyRegTxn(account string, feePerByte, firstRound, lastRound uint64, note []byte, genesisID string, genesisHash string,
   120  	voteKey, selectionKey string, voteFirst, voteLast, voteKeyDilution uint64) (types.Transaction, error) {
   121  	// Decode account address
   122  	accountAddr, err := types.DecodeAddress(account)
   123  	if err != nil {
   124  		return types.Transaction{}, err
   125  	}
   126  
   127  	ghBytes, err := byte32FromBase64(genesisHash)
   128  	if err != nil {
   129  		return types.Transaction{}, err
   130  	}
   131  
   132  	votePKBytes, err := byte32FromBase64(voteKey)
   133  	if err != nil {
   134  		return types.Transaction{}, err
   135  	}
   136  
   137  	selectionPKBytes, err := byte32FromBase64(selectionKey)
   138  	if err != nil {
   139  		return types.Transaction{}, err
   140  	}
   141  
   142  	tx := types.Transaction{
   143  		Type: types.KeyRegistrationTx,
   144  		Header: types.Header{
   145  			Sender:      accountAddr,
   146  			Fee:         types.MicroAlgos(feePerByte),
   147  			FirstValid:  types.Round(firstRound),
   148  			LastValid:   types.Round(lastRound),
   149  			Note:        note,
   150  			GenesisHash: types.Digest(ghBytes),
   151  			GenesisID:   genesisID,
   152  		},
   153  		KeyregTxnFields: types.KeyregTxnFields{
   154  			VotePK:          types.VotePK(votePKBytes),
   155  			SelectionPK:     types.VRFPK(selectionPKBytes),
   156  			VoteFirst:       types.Round(voteFirst),
   157  			VoteLast:        types.Round(voteLast),
   158  			VoteKeyDilution: voteKeyDilution,
   159  		},
   160  	}
   161  
   162  	// Update fee
   163  	eSize, err := EstimateSize(tx)
   164  	if err != nil {
   165  		return types.Transaction{}, err
   166  	}
   167  	tx.Fee = types.MicroAlgos(eSize * feePerByte)
   168  
   169  	if tx.Fee < MinTxnFee {
   170  		tx.Fee = MinTxnFee
   171  	}
   172  
   173  	return tx, nil
   174  }
   175  
   176  // MakeKeyRegTxnWithFlatFee constructs a keyreg transaction using the passed parameters.
   177  // - account is a checksummed, human-readable address for which we register the given participation key.
   178  // - fee is a flat fee
   179  // - firstRound is the first round this txn is valid (txn semantics unrelated to key registration)
   180  // - lastRound is the last round this txn is valid
   181  // - note is a byte array
   182  // - genesis id corresponds to the id of the network
   183  // - genesis hash corresponds to the base64-encoded hash of the genesis of the network
   184  // KeyReg parameters:
   185  // - votePK is a base64-encoded string corresponding to the root participation public key
   186  // - selectionKey is a base64-encoded string corresponding to the vrf public key
   187  // - voteFirst is the first round this participation key is valid
   188  // - voteLast is the last round this participation key is valid
   189  // - voteKeyDilution is the dilution for the 2-level participation key
   190  // Deprecated: next major version will use a Params object, see package future
   191  func MakeKeyRegTxnWithFlatFee(account string, fee, firstRound, lastRound uint64, note []byte, genesisID string, genesisHash string,
   192  	voteKey, selectionKey string, voteFirst, voteLast, voteKeyDilution uint64) (types.Transaction, error) {
   193  	tx, err := MakeKeyRegTxn(account, fee, firstRound, lastRound, note, genesisID, genesisHash, voteKey, selectionKey, voteFirst, voteLast, voteKeyDilution)
   194  	if err != nil {
   195  		return types.Transaction{}, err
   196  	}
   197  
   198  	tx.Fee = types.MicroAlgos(fee)
   199  
   200  	if tx.Fee < MinTxnFee {
   201  		tx.Fee = MinTxnFee
   202  	}
   203  
   204  	return tx, nil
   205  }
   206  
   207  // MakeAssetCreateTxn constructs an asset creation transaction using the passed parameters.
   208  // - account is a checksummed, human-readable address which will send the transaction.
   209  // - fee is fee per byte as received from algod SuggestedFee API call.
   210  // - firstRound is the first round this txn is valid (txn semantics unrelated to the asset)
   211  // - lastRound is the last round this txn is valid
   212  // - note is a byte array
   213  // - genesis id corresponds to the id of the network
   214  // - genesis hash corresponds to the base64-encoded hash of the genesis of the network
   215  // Asset creation parameters:
   216  // - see asset.go
   217  // Deprecated: next major version will use a Params object, see package future
   218  func MakeAssetCreateTxn(account string, feePerByte, firstRound, lastRound uint64, note []byte, genesisID, genesisHash string,
   219  	total uint64, decimals uint32, defaultFrozen bool, manager, reserve, freeze, clawback string,
   220  	unitName, assetName, url, metadataHash string) (types.Transaction, error) {
   221  	var tx types.Transaction
   222  	var err error
   223  
   224  	if decimals > types.AssetMaxNumberOfDecimals {
   225  		return tx, fmt.Errorf("cannot create an asset with number of decimals %d (more than maximum %d)", decimals, types.AssetMaxNumberOfDecimals)
   226  	}
   227  
   228  	tx.Type = types.AssetConfigTx
   229  	tx.AssetParams = types.AssetParams{
   230  		Total:         total,
   231  		Decimals:      decimals,
   232  		DefaultFrozen: defaultFrozen,
   233  		UnitName:      unitName,
   234  		AssetName:     assetName,
   235  		URL:           url,
   236  	}
   237  
   238  	if manager != "" {
   239  		tx.AssetParams.Manager, err = types.DecodeAddress(manager)
   240  		if err != nil {
   241  			return tx, err
   242  		}
   243  	}
   244  	if reserve != "" {
   245  		tx.AssetParams.Reserve, err = types.DecodeAddress(reserve)
   246  		if err != nil {
   247  			return tx, err
   248  		}
   249  	}
   250  	if freeze != "" {
   251  		tx.AssetParams.Freeze, err = types.DecodeAddress(freeze)
   252  		if err != nil {
   253  			return tx, err
   254  		}
   255  	}
   256  	if clawback != "" {
   257  		tx.AssetParams.Clawback, err = types.DecodeAddress(clawback)
   258  		if err != nil {
   259  			return tx, err
   260  		}
   261  	}
   262  
   263  	if len(assetName) > types.AssetNameMaxLen {
   264  		return tx, fmt.Errorf("asset name too long: %d > %d", len(assetName), types.AssetNameMaxLen)
   265  	}
   266  	tx.AssetParams.AssetName = assetName
   267  
   268  	if len(url) > types.AssetURLMaxLen {
   269  		return tx, fmt.Errorf("asset url too long: %d > %d", len(url), types.AssetURLMaxLen)
   270  	}
   271  	tx.AssetParams.URL = url
   272  
   273  	if len(unitName) > types.AssetUnitNameMaxLen {
   274  		return tx, fmt.Errorf("asset unit name too long: %d > %d", len(unitName), types.AssetUnitNameMaxLen)
   275  	}
   276  	tx.AssetParams.UnitName = unitName
   277  
   278  	if len(metadataHash) > types.AssetMetadataHashLen {
   279  		return tx, fmt.Errorf("asset metadata hash '%s' too long: %d > %d)", metadataHash, len(metadataHash), types.AssetMetadataHashLen)
   280  	}
   281  	copy(tx.AssetParams.MetadataHash[:], []byte(metadataHash))
   282  
   283  	// Fill in header
   284  	accountAddr, err := types.DecodeAddress(account)
   285  	if err != nil {
   286  		return types.Transaction{}, err
   287  	}
   288  	ghBytes, err := byte32FromBase64(genesisHash)
   289  	if err != nil {
   290  		return types.Transaction{}, err
   291  	}
   292  	tx.Header = types.Header{
   293  		Sender:      accountAddr,
   294  		Fee:         types.MicroAlgos(feePerByte),
   295  		FirstValid:  types.Round(firstRound),
   296  		LastValid:   types.Round(lastRound),
   297  		GenesisHash: types.Digest(ghBytes),
   298  		GenesisID:   genesisID,
   299  		Note:        note,
   300  	}
   301  
   302  	// Update fee
   303  	eSize, err := EstimateSize(tx)
   304  	if err != nil {
   305  		return types.Transaction{}, err
   306  	}
   307  	tx.Fee = types.MicroAlgos(eSize * feePerByte)
   308  
   309  	if tx.Fee < MinTxnFee {
   310  		tx.Fee = MinTxnFee
   311  	}
   312  
   313  	return tx, nil
   314  }
   315  
   316  // MakeAssetConfigTxn creates a tx template for changing the
   317  // key configuration of an existing asset.
   318  // Important notes -
   319  // 	* Every asset config transaction is a fresh one. No parameters will be inherited from the current config.
   320  // 	* Once an address is set to to the empty string, IT CAN NEVER BE CHANGED AGAIN. For example, if you want to keep
   321  //    The current manager, you must specify its address again.
   322  //	Parameters -
   323  // - account is a checksummed, human-readable address that will send the transaction
   324  // - feePerByte  is a fee per byte
   325  // - firstRound is the first round this txn is valid (txn semantics unrelated to asset config)
   326  // - lastRound is the last round this txn is valid
   327  // - note is an arbitrary byte array
   328  // - genesis id corresponds to the id of the network
   329  // - genesis hash corresponds to the base64-encoded hash of the genesis of the network
   330  // - index is the asset index id
   331  // - for newManager, newReserve, newFreeze, newClawback see asset.go
   332  // - strictEmptyAddressChecking: if true, disallow empty admin accounts from being set (preventing accidental disable of admin features)
   333  // Deprecated: next major version will use a Params object, see package future
   334  func MakeAssetConfigTxn(account string, feePerByte, firstRound, lastRound uint64, note []byte, genesisID, genesisHash string,
   335  	index uint64, newManager, newReserve, newFreeze, newClawback string, strictEmptyAddressChecking bool) (types.Transaction, error) {
   336  	var tx types.Transaction
   337  
   338  	if strictEmptyAddressChecking && (newManager == "" || newReserve == "" || newFreeze == "" || newClawback == "") {
   339  		return tx, fmt.Errorf("strict empty address checking requested but empty address supplied to one or more manager addresses")
   340  	}
   341  
   342  	tx.Type = types.AssetConfigTx
   343  
   344  	accountAddr, err := types.DecodeAddress(account)
   345  	if err != nil {
   346  		return tx, err
   347  	}
   348  
   349  	ghBytes, err := byte32FromBase64(genesisHash)
   350  	if err != nil {
   351  		return types.Transaction{}, err
   352  	}
   353  
   354  	tx.Header = types.Header{
   355  		Sender:      accountAddr,
   356  		Fee:         types.MicroAlgos(feePerByte),
   357  		FirstValid:  types.Round(firstRound),
   358  		LastValid:   types.Round(lastRound),
   359  		GenesisHash: ghBytes,
   360  		GenesisID:   genesisID,
   361  		Note:        note,
   362  	}
   363  
   364  	tx.ConfigAsset = types.AssetIndex(index)
   365  
   366  	if newManager != "" {
   367  		tx.Type = types.AssetConfigTx
   368  		tx.AssetParams.Manager, err = types.DecodeAddress(newManager)
   369  		if err != nil {
   370  			return tx, err
   371  		}
   372  	}
   373  
   374  	if newReserve != "" {
   375  		tx.AssetParams.Reserve, err = types.DecodeAddress(newReserve)
   376  		if err != nil {
   377  			return tx, err
   378  		}
   379  	}
   380  
   381  	if newFreeze != "" {
   382  		tx.AssetParams.Freeze, err = types.DecodeAddress(newFreeze)
   383  		if err != nil {
   384  			return tx, err
   385  		}
   386  	}
   387  
   388  	if newClawback != "" {
   389  		tx.AssetParams.Clawback, err = types.DecodeAddress(newClawback)
   390  		if err != nil {
   391  			return tx, err
   392  		}
   393  	}
   394  
   395  	// Update fee
   396  	eSize, err := EstimateSize(tx)
   397  	if err != nil {
   398  		return types.Transaction{}, err
   399  	}
   400  	tx.Fee = types.MicroAlgos(eSize * feePerByte)
   401  
   402  	if tx.Fee < MinTxnFee {
   403  		tx.Fee = MinTxnFee
   404  	}
   405  
   406  	return tx, nil
   407  }
   408  
   409  // transferAssetBuilder is a helper that builds asset transfer transactions:
   410  // either a normal asset transfer, or an asset revocation
   411  // Deprecated: next major version will use a Params object, see package future
   412  func transferAssetBuilder(account, recipient, closeAssetsTo, revocationTarget string, amount, feePerByte,
   413  	firstRound, lastRound uint64, note []byte, genesisID, genesisHash string, index uint64) (types.Transaction, error) {
   414  	var tx types.Transaction
   415  	tx.Type = types.AssetTransferTx
   416  
   417  	accountAddr, err := types.DecodeAddress(account)
   418  	if err != nil {
   419  		return tx, err
   420  	}
   421  
   422  	ghBytes, err := byte32FromBase64(genesisHash)
   423  	if err != nil {
   424  		return types.Transaction{}, err
   425  	}
   426  
   427  	tx.Header = types.Header{
   428  		Sender:      accountAddr,
   429  		Fee:         types.MicroAlgos(feePerByte),
   430  		FirstValid:  types.Round(firstRound),
   431  		LastValid:   types.Round(lastRound),
   432  		GenesisHash: types.Digest(ghBytes),
   433  		GenesisID:   genesisID,
   434  		Note:        note,
   435  	}
   436  
   437  	tx.XferAsset = types.AssetIndex(index)
   438  
   439  	recipientAddr, err := types.DecodeAddress(recipient)
   440  	if err != nil {
   441  		return tx, err
   442  	}
   443  	tx.AssetReceiver = recipientAddr
   444  
   445  	if closeAssetsTo != "" {
   446  		closeToAddr, err := types.DecodeAddress(closeAssetsTo)
   447  		if err != nil {
   448  			return tx, err
   449  		}
   450  		tx.AssetCloseTo = closeToAddr
   451  	}
   452  
   453  	if revocationTarget != "" {
   454  		revokedAddr, err := types.DecodeAddress(revocationTarget)
   455  		if err != nil {
   456  			return tx, err
   457  		}
   458  		tx.AssetSender = revokedAddr
   459  	}
   460  
   461  	tx.AssetAmount = amount
   462  
   463  	// Update fee
   464  	eSize, err := EstimateSize(tx)
   465  	if err != nil {
   466  		return types.Transaction{}, err
   467  	}
   468  	tx.Fee = types.MicroAlgos(eSize * feePerByte)
   469  
   470  	if tx.Fee < MinTxnFee {
   471  		tx.Fee = MinTxnFee
   472  	}
   473  
   474  	return tx, nil
   475  }
   476  
   477  // MakeAssetTransferTxn creates a tx for sending some asset from an asset holder to another user
   478  // the recipient address must have previously issued an asset acceptance transaction for this asset
   479  // - account is a checksummed, human-readable address that will send the transaction and assets
   480  // - recipient is a checksummed, human-readable address what will receive the assets
   481  // - closeAssetsTo is a checksummed, human-readable address that behaves as a close-to address for the asset transaction; the remaining assets not sent to recipient will be sent to closeAssetsTo. Leave blank for no close-to behavior.
   482  // - amount is the number of assets to send
   483  // - feePerByte is a fee per byte
   484  // - firstRound is the first round this txn is valid (txn semantics unrelated to asset management)
   485  // - lastRound is the last round this txn is valid
   486  // - note is an arbitrary byte array
   487  // - genesis id corresponds to the id of the network
   488  // - genesis hash corresponds to the base64-encoded hash of the genesis of the network
   489  // - index is the asset index
   490  // Deprecated: next major version will use a Params object, see package future
   491  func MakeAssetTransferTxn(account, recipient, closeAssetsTo string, amount, feePerByte, firstRound, lastRound uint64, note []byte,
   492  	genesisID, genesisHash string, index uint64) (types.Transaction, error) {
   493  	revocationTarget := "" // no asset revocation, this is normal asset transfer
   494  	return transferAssetBuilder(account, recipient, closeAssetsTo, revocationTarget, amount, feePerByte, firstRound, lastRound,
   495  		note, genesisID, genesisHash, index)
   496  }
   497  
   498  // MakeAssetAcceptanceTxn creates a tx for marking an account as willing to accept the given asset
   499  // - account is a checksummed, human-readable address that will send the transaction and begin accepting the asset
   500  // - feePerByte is a fee per byte
   501  // - firstRound is the first round this txn is valid (txn semantics unrelated to asset management)
   502  // - lastRound is the last round this txn is valid
   503  // - note is an arbitrary byte array
   504  // - genesis id corresponds to the id of the network
   505  // - genesis hash corresponds to the base64-encoded hash of the genesis of the network
   506  // - index is the asset index
   507  // Deprecated: next major version will use a Params object, see package future
   508  func MakeAssetAcceptanceTxn(account string, feePerByte, firstRound, lastRound uint64, note []byte,
   509  	genesisID, genesisHash string, index uint64) (types.Transaction, error) {
   510  	return MakeAssetTransferTxn(account, account, "", 0,
   511  		feePerByte, firstRound, lastRound, note, genesisID, genesisHash, index)
   512  }
   513  
   514  // MakeAssetRevocationTxn creates a tx for revoking an asset from an account and sending it to another
   515  // - account is a checksummed, human-readable address; it must be the revocation manager / clawback address from the asset's parameters
   516  // - target is a checksummed, human-readable address; it is the account whose assets will be revoked
   517  // - recipient is a checksummed, human-readable address; it will receive the revoked assets
   518  // - feePerByte is a fee per byte
   519  // - firstRound is the first round this txn is valid (txn semantics unrelated to asset management)
   520  // - lastRound is the last round this txn is valid
   521  // - note is an arbitrary byte array
   522  // - genesis id corresponds to the id of the network
   523  // - genesis hash corresponds to the base64-encoded hash of the genesis of the network
   524  // - index is the asset index
   525  // Deprecated: next major version will use a Params object, see package future
   526  func MakeAssetRevocationTxn(account, target, recipient string, amount, feePerByte, firstRound, lastRound uint64, note []byte,
   527  	genesisID, genesisHash string, index uint64) (types.Transaction, error) {
   528  	closeAssetsTo := "" // no close-out, this is an asset revocation
   529  	return transferAssetBuilder(account, recipient, closeAssetsTo, target, amount, feePerByte, firstRound, lastRound,
   530  		note, genesisID, genesisHash, index)
   531  }
   532  
   533  // MakeAssetDestroyTxn creates a tx template for destroying an asset, removing it from the record.
   534  // All outstanding asset amount must be held by the creator, and this transaction must be issued by the asset manager.
   535  // - account is a checksummed, human-readable address that will send the transaction; it also must be the asset manager
   536  // - fee is a fee per byte
   537  // - firstRound is the first round this txn is valid (txn semantics unrelated to asset management)
   538  // - lastRound is the last round this txn is valid
   539  // - genesis id corresponds to the id of the network
   540  // - genesis hash corresponds to the base64-encoded hash of the genesis of the network
   541  // - index is the asset index
   542  // Deprecated: next major version will use a Params object, see package future
   543  func MakeAssetDestroyTxn(account string, feePerByte, firstRound, lastRound uint64, note []byte, genesisID, genesisHash string,
   544  	index uint64) (types.Transaction, error) {
   545  	// an asset destroy transaction is just a configuration transaction with AssetParams zeroed
   546  	tx, err := MakeAssetConfigTxn(account, feePerByte, firstRound, lastRound, note, genesisID, genesisHash,
   547  		index, "", "", "", "", false)
   548  
   549  	return tx, err
   550  }
   551  
   552  // MakeAssetFreezeTxn constructs a transaction that freezes or unfreezes an account's asset holdings
   553  // It must be issued by the freeze address for the asset
   554  // - account is a checksummed, human-readable address which will send the transaction.
   555  // - fee is fee per byte as received from algod SuggestedFee API call.
   556  // - firstRound is the first round this txn is valid (txn semantics unrelated to the asset)
   557  // - lastRound is the last round this txn is valid
   558  // - note is an optional arbitrary byte array
   559  // - genesis id corresponds to the id of the network
   560  // - genesis hash corresponds to the base64-encoded hash of the genesis of the network
   561  // - assetIndex is the index for tracking the asset
   562  // - target is the account to be frozen or unfrozen
   563  // - newFreezeSetting is the new state of the target account
   564  // Deprecated: next major version will use a Params object, see package future
   565  func MakeAssetFreezeTxn(account string, fee, firstRound, lastRound uint64, note []byte, genesisID, genesisHash string,
   566  	assetIndex uint64, target string, newFreezeSetting bool) (types.Transaction, error) {
   567  	var tx types.Transaction
   568  
   569  	tx.Type = types.AssetFreezeTx
   570  
   571  	accountAddr, err := types.DecodeAddress(account)
   572  	if err != nil {
   573  		return tx, err
   574  	}
   575  
   576  	ghBytes, err := byte32FromBase64(genesisHash)
   577  	if err != nil {
   578  		return types.Transaction{}, err
   579  	}
   580  
   581  	tx.Header = types.Header{
   582  		Sender:      accountAddr,
   583  		Fee:         types.MicroAlgos(fee),
   584  		FirstValid:  types.Round(firstRound),
   585  		LastValid:   types.Round(lastRound),
   586  		GenesisHash: types.Digest(ghBytes),
   587  		GenesisID:   genesisID,
   588  		Note:        note,
   589  	}
   590  
   591  	tx.FreezeAsset = types.AssetIndex(assetIndex)
   592  
   593  	tx.FreezeAccount, err = types.DecodeAddress(target)
   594  	if err != nil {
   595  		return tx, err
   596  	}
   597  
   598  	tx.AssetFrozen = newFreezeSetting
   599  	// Update fee
   600  	eSize, err := EstimateSize(tx)
   601  	if err != nil {
   602  		return types.Transaction{}, err
   603  	}
   604  	tx.Fee = types.MicroAlgos(eSize * fee)
   605  
   606  	if tx.Fee < MinTxnFee {
   607  		tx.Fee = MinTxnFee
   608  	}
   609  
   610  	return tx, nil
   611  }
   612  
   613  // MakeAssetCreateTxnWithFlatFee constructs an asset creation transaction using the passed parameters.
   614  // - account is a checksummed, human-readable address which will send the transaction.
   615  // - fee is fee per byte as received from algod SuggestedFee API call.
   616  // - firstRound is the first round this txn is valid (txn semantics unrelated to the asset)
   617  // - lastRound is the last round this txn is valid
   618  // - genesis id corresponds to the id of the network
   619  // - genesis hash corresponds to the base64-encoded hash of the genesis of the network
   620  // Asset creation parameters:
   621  // - see asset.go
   622  // Deprecated: next major version will use a Params object, see package future
   623  func MakeAssetCreateTxnWithFlatFee(account string, fee, firstRound, lastRound uint64, note []byte, genesisID, genesisHash string,
   624  	total uint64, decimals uint32, defaultFrozen bool, manager, reserve, freeze, clawback, unitName, assetName, url, metadataHash string) (types.Transaction, error) {
   625  	tx, err := MakeAssetCreateTxn(account, fee, firstRound, lastRound, note, genesisID, genesisHash, total, decimals, defaultFrozen, manager, reserve, freeze, clawback, unitName, assetName, url, metadataHash)
   626  	if err != nil {
   627  		return types.Transaction{}, err
   628  	}
   629  
   630  	tx.Fee = types.MicroAlgos(fee)
   631  
   632  	if tx.Fee < MinTxnFee {
   633  		tx.Fee = MinTxnFee
   634  	}
   635  
   636  	return tx, nil
   637  }
   638  
   639  // MakeAssetConfigTxnWithFlatFee creates a tx template for changing the
   640  // keys for an asset. An empty string means a zero key (which
   641  // cannot be changed after becoming zero); to keep a key
   642  // unchanged, you must specify that key.
   643  // Deprecated: next major version will use a Params object, see package future
   644  func MakeAssetConfigTxnWithFlatFee(account string, fee, firstRound, lastRound uint64, note []byte, genesisID, genesisHash string,
   645  	index uint64, newManager, newReserve, newFreeze, newClawback string, strictEmptyAddressChecking bool) (types.Transaction, error) {
   646  	tx, err := MakeAssetConfigTxn(account, fee, firstRound, lastRound, note, genesisID, genesisHash,
   647  		index, newManager, newReserve, newFreeze, newClawback, strictEmptyAddressChecking)
   648  	if err != nil {
   649  		return types.Transaction{}, err
   650  	}
   651  
   652  	tx.Fee = types.MicroAlgos(fee)
   653  
   654  	if tx.Fee < MinTxnFee {
   655  		tx.Fee = MinTxnFee
   656  	}
   657  	return tx, nil
   658  }
   659  
   660  // MakeAssetTransferTxnWithFlatFee creates a tx for sending some asset from an asset holder to another user
   661  // the recipient address must have previously issued an asset acceptance transaction for this asset
   662  // - account is a checksummed, human-readable address that will send the transaction and assets
   663  // - recipient is a checksummed, human-readable address what will receive the assets
   664  // - closeAssetsTo is a checksummed, human-readable address that behaves as a close-to address for the asset transaction; the remaining assets not sent to recipient will be sent to closeAssetsTo. Leave blank for no close-to behavior.
   665  // - amount is the number of assets to send
   666  // - fee is a flat fee
   667  // - firstRound is the first round this txn is valid (txn semantics unrelated to asset management)
   668  // - lastRound is the last round this txn is valid
   669  // - genesis id corresponds to the id of the network
   670  // - genesis hash corresponds to the base64-encoded hash of the genesis of the network
   671  // - index is the asset index
   672  func MakeAssetTransferTxnWithFlatFee(account, recipient, closeAssetsTo string, amount, fee, firstRound, lastRound uint64, note []byte,
   673  	genesisID, genesisHash string, index uint64) (types.Transaction, error) {
   674  	tx, err := MakeAssetTransferTxn(account, recipient, closeAssetsTo, amount,
   675  		fee, firstRound, lastRound, note, genesisID, genesisHash, index)
   676  	if err != nil {
   677  		return types.Transaction{}, err
   678  	}
   679  
   680  	tx.Fee = types.MicroAlgos(fee)
   681  
   682  	if tx.Fee < MinTxnFee {
   683  		tx.Fee = MinTxnFee
   684  	}
   685  	return tx, nil
   686  }
   687  
   688  // MakeAssetAcceptanceTxnWithFlatFee creates a tx for marking an account as willing to accept an asset
   689  // - account is a checksummed, human-readable address that will send the transaction and begin accepting the asset
   690  // - fee is a flat fee
   691  // - firstRound is the first round this txn is valid (txn semantics unrelated to asset management)
   692  // - lastRound is the last round this txn is valid
   693  // - genesis id corresponds to the id of the network
   694  // - genesis hash corresponds to the base64-encoded hash of the genesis of the network
   695  // - index is the asset index
   696  // Deprecated: next major version will use a Params object, see package future
   697  func MakeAssetAcceptanceTxnWithFlatFee(account string, fee, firstRound, lastRound uint64, note []byte,
   698  	genesisID, genesisHash string, index uint64) (types.Transaction, error) {
   699  	tx, err := MakeAssetTransferTxnWithFlatFee(account, account, "", 0,
   700  		fee, firstRound, lastRound, note, genesisID, genesisHash, index)
   701  	return tx, err
   702  }
   703  
   704  // MakeAssetRevocationTxnWithFlatFee creates a tx for revoking an asset from an account and sending it to another
   705  // - account is a checksummed, human-readable address; it must be the revocation manager / clawback address from the asset's parameters
   706  // - target is a checksummed, human-readable address; it is the account whose assets will be revoked
   707  // - recipient is a checksummed, human-readable address; it will receive the revoked assets
   708  // - fee is a flat fee
   709  // - firstRound is the first round this txn is valid (txn semantics unrelated to asset management)
   710  // - lastRound is the last round this txn is valid
   711  // - note is an arbitrary byte array
   712  // - genesis id corresponds to the id of the network
   713  // - genesis hash corresponds to the base64-encoded hash of the genesis of the network
   714  // - index is the asset index
   715  // Deprecated: next major version will use a Params object, see package future
   716  func MakeAssetRevocationTxnWithFlatFee(account, target, recipient string, amount, fee, firstRound, lastRound uint64, note []byte,
   717  	genesisID, genesisHash, creator string, index uint64) (types.Transaction, error) {
   718  	tx, err := MakeAssetRevocationTxn(account, target, recipient, amount, fee, firstRound, lastRound,
   719  		note, genesisID, genesisHash, index)
   720  
   721  	if err != nil {
   722  		return types.Transaction{}, err
   723  	}
   724  
   725  	tx.Fee = types.MicroAlgos(fee)
   726  
   727  	if tx.Fee < MinTxnFee {
   728  		tx.Fee = MinTxnFee
   729  	}
   730  	return tx, nil
   731  }
   732  
   733  // MakeAssetDestroyTxnWithFlatFee creates a tx template for destroying an asset, removing it from the record.
   734  // All outstanding asset amount must be held by the creator, and this transaction must be issued by the asset manager.
   735  // - account is a checksummed, human-readable address that will send the transaction; it also must be the asset manager
   736  // - fee is a flat fee
   737  // - firstRound is the first round this txn is valid (txn semantics unrelated to asset management)
   738  // - lastRound is the last round this txn is valid
   739  // - genesis id corresponds to the id of the network
   740  // - genesis hash corresponds to the base64-encoded hash of the genesis of the network
   741  // - index is the asset index
   742  // Deprecated: next major version will use a Params object, see package future
   743  func MakeAssetDestroyTxnWithFlatFee(account string, fee, firstRound, lastRound uint64, note []byte, genesisID, genesisHash string,
   744  	creator string, index uint64) (types.Transaction, error) {
   745  	tx, err := MakeAssetConfigTxnWithFlatFee(account, fee, firstRound, lastRound, note, genesisID, genesisHash,
   746  		index, "", "", "", "", false)
   747  	return tx, err
   748  }
   749  
   750  // MakeAssetFreezeTxnWithFlatFee is as MakeAssetFreezeTxn, but taking a flat fee rather than a fee per byte.
   751  // Deprecated: next major version will use a Params object, see package future
   752  func MakeAssetFreezeTxnWithFlatFee(account string, fee, firstRound, lastRound uint64, note []byte, genesisID, genesisHash string,
   753  	creator string, assetIndex uint64, target string, newFreezeSetting bool) (types.Transaction, error) {
   754  	tx, err := MakeAssetFreezeTxn(account, fee, firstRound, lastRound, note, genesisID, genesisHash,
   755  		assetIndex, target, newFreezeSetting)
   756  	if err != nil {
   757  		return types.Transaction{}, err
   758  	}
   759  
   760  	tx.Fee = types.MicroAlgos(fee)
   761  
   762  	if tx.Fee < MinTxnFee {
   763  		tx.Fee = MinTxnFee
   764  	}
   765  	return tx, nil
   766  }
   767  
   768  // AssignGroupID computes and return list of transactions with Group field set.
   769  // - txns is a list of transactions to process
   770  // - account specifies a sender field of transaction to return. Set to empty string to return all of them
   771  func AssignGroupID(txns []types.Transaction, account string) (result []types.Transaction, err error) {
   772  	gid, err := crypto.ComputeGroupID(txns)
   773  	if err != nil {
   774  		return
   775  	}
   776  	var decoded types.Address
   777  	if account != "" {
   778  		decoded, err = types.DecodeAddress(account)
   779  		if err != nil {
   780  			return
   781  		}
   782  	}
   783  	for _, tx := range txns {
   784  		if account == "" || bytes.Compare(tx.Sender[:], decoded[:]) == 0 {
   785  			tx.Group = gid
   786  			result = append(result, tx)
   787  		}
   788  	}
   789  	return result, nil
   790  }
   791  
   792  // EstimateSize returns the estimated length of the encoded transaction
   793  func EstimateSize(txn types.Transaction) (uint64, error) {
   794  	return uint64(len(msgpack.Encode(txn))) + NumOfAdditionalBytesAfterSigning, nil
   795  }
   796  
   797  // byte32FromBase64 decodes the input base64 string and outputs a
   798  // 32 byte array, erroring if the input is the wrong length.
   799  func byte32FromBase64(in string) (out [32]byte, err error) {
   800  	slice, err := base64.StdEncoding.DecodeString(in)
   801  	if err != nil {
   802  		return
   803  	}
   804  	if len(slice) != 32 {
   805  		return out, fmt.Errorf("Input is not 32 bytes")
   806  	}
   807  	copy(out[:], slice)
   808  	return
   809  }