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

     1  package future
     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/transaction"
    11  	"github.com/algorand/go-algorand-sdk/types"
    12  )
    13  
    14  // MinTxnFee is v5 consensus params, in microAlgos
    15  const MinTxnFee = transaction.MinTxnFee
    16  
    17  // NumOfAdditionalBytesAfterSigning is the number of bytes added to a txn after signing it
    18  const NumOfAdditionalBytesAfterSigning = 75
    19  
    20  func setFee(tx types.Transaction, params types.SuggestedParams) (types.Transaction, error) {
    21  	if !params.FlatFee {
    22  		eSize, err := EstimateSize(tx)
    23  		if err != nil {
    24  			return types.Transaction{}, err
    25  		}
    26  		tx.Fee = types.MicroAlgos(eSize * uint64(params.Fee))
    27  		if tx.Fee < MinTxnFee {
    28  			tx.Fee = MinTxnFee
    29  		}
    30  	} else {
    31  		tx.Fee = params.Fee
    32  	}
    33  
    34  	return tx, nil
    35  }
    36  
    37  // MakePaymentTxn constructs a payment transaction using the passed parameters.
    38  // `from` and `to` addresses should be checksummed, human-readable addresses
    39  // fee is fee per byte as received from algod SuggestedFee API call
    40  func MakePaymentTxn(from, to string, amount uint64, note []byte, closeRemainderTo string, params types.SuggestedParams) (types.Transaction, error) {
    41  	// Decode from address
    42  	fromAddr, err := types.DecodeAddress(from)
    43  	if err != nil {
    44  		return types.Transaction{}, err
    45  	}
    46  
    47  	// Decode to address
    48  	toAddr, err := types.DecodeAddress(to)
    49  	if err != nil {
    50  		return types.Transaction{}, err
    51  	}
    52  
    53  	// Decode the CloseRemainderTo address, if present
    54  	var closeRemainderToAddr types.Address
    55  	if closeRemainderTo != "" {
    56  		closeRemainderToAddr, err = types.DecodeAddress(closeRemainderTo)
    57  		if err != nil {
    58  			return types.Transaction{}, err
    59  		}
    60  	}
    61  
    62  	if len(params.GenesisHash) == 0 {
    63  		return types.Transaction{}, fmt.Errorf("payment transaction must contain a genesisHash")
    64  	}
    65  
    66  	var gh types.Digest
    67  	copy(gh[:], params.GenesisHash)
    68  
    69  	// Build the transaction
    70  	tx := types.Transaction{
    71  		Type: types.PaymentTx,
    72  		Header: types.Header{
    73  			Sender:      fromAddr,
    74  			Fee:         params.Fee,
    75  			FirstValid:  params.FirstRoundValid,
    76  			LastValid:   params.LastRoundValid,
    77  			Note:        note,
    78  			GenesisID:   params.GenesisID,
    79  			GenesisHash: gh,
    80  		},
    81  		PaymentTxnFields: types.PaymentTxnFields{
    82  			Receiver:         toAddr,
    83  			Amount:           types.MicroAlgos(amount),
    84  			CloseRemainderTo: closeRemainderToAddr,
    85  		},
    86  	}
    87  
    88  	return setFee(tx, params)
    89  }
    90  
    91  // MakeKeyRegTxn constructs a keyreg transaction using the passed parameters.
    92  // - account is a checksummed, human-readable address for which we register the given participation key.
    93  // - note is a byte array
    94  // - params is typically received from algod, it defines common-to-all-txns arguments like fee and validity period
    95  // KeyReg parameters:
    96  // - votePK is a base64-encoded string corresponding to the root participation public key
    97  // - selectionKey is a base64-encoded string corresponding to the vrf public key
    98  // - voteFirst is the first round this participation key is valid
    99  // - voteLast is the last round this participation key is valid
   100  // - voteKeyDilution is the dilution for the 2-level participation key
   101  func MakeKeyRegTxn(account string, note []byte, params types.SuggestedParams, voteKey, selectionKey string, voteFirst, voteLast, voteKeyDilution uint64) (types.Transaction, error) {
   102  	// Decode account address
   103  	accountAddr, err := types.DecodeAddress(account)
   104  	if err != nil {
   105  		return types.Transaction{}, err
   106  	}
   107  
   108  	if len(params.GenesisHash) == 0 {
   109  		return types.Transaction{}, fmt.Errorf("key registration transaction must contain a genesisHash")
   110  	}
   111  
   112  	var gh types.Digest
   113  	copy(gh[:], params.GenesisHash)
   114  
   115  	votePKBytes, err := byte32FromBase64(voteKey)
   116  	if err != nil {
   117  		return types.Transaction{}, err
   118  	}
   119  
   120  	selectionPKBytes, err := byte32FromBase64(selectionKey)
   121  	if err != nil {
   122  		return types.Transaction{}, err
   123  	}
   124  
   125  	tx := types.Transaction{
   126  		Type: types.KeyRegistrationTx,
   127  		Header: types.Header{
   128  			Sender:      accountAddr,
   129  			Fee:         params.Fee,
   130  			FirstValid:  params.FirstRoundValid,
   131  			LastValid:   params.LastRoundValid,
   132  			Note:        note,
   133  			GenesisHash: gh,
   134  			GenesisID:   params.GenesisID,
   135  		},
   136  		KeyregTxnFields: types.KeyregTxnFields{
   137  			VotePK:          types.VotePK(votePKBytes),
   138  			SelectionPK:     types.VRFPK(selectionPKBytes),
   139  			VoteFirst:       types.Round(voteFirst),
   140  			VoteLast:        types.Round(voteLast),
   141  			VoteKeyDilution: voteKeyDilution,
   142  		},
   143  	}
   144  
   145  	return setFee(tx, params)
   146  }
   147  
   148  // MakeKeyRegTxnWithStateProofKey constructs a keyreg transaction using the passed parameters.
   149  // - account is a checksummed, human-readable address for which we register the given participation key.
   150  // - note is a byte array
   151  // - params is typically received from algod, it defines common-to-all-txns arguments like fee and validity period
   152  // KeyReg parameters:
   153  // - votePK is a base64-encoded string corresponding to the root participation public key
   154  // - selectionKey is a base64-encoded string corresponding to the vrf public key
   155  // - stateProofPK is a base64-encoded string corresponding to the block proof public key
   156  // - voteFirst is the first round this participation key is valid
   157  // - voteLast is the last round this participation key is valid
   158  // - voteKeyDilution is the dilution for the 2-level participation key
   159  // - nonpart is an indicator marking a key registration participating or nonparticipating
   160  func MakeKeyRegTxnWithStateProofKey(account string, note []byte, params types.SuggestedParams, voteKey, selectionKey, stateProofPK string, voteFirst, voteLast, voteKeyDilution uint64, nonpart bool) (types.Transaction, error) {
   161  	// Decode account address
   162  	accountAddr, err := types.DecodeAddress(account)
   163  	if err != nil {
   164  		return types.Transaction{}, err
   165  	}
   166  
   167  	if len(params.GenesisHash) == 0 {
   168  		return types.Transaction{}, fmt.Errorf("key registration transaction must contain a genesisHash")
   169  	}
   170  
   171  	var gh types.Digest
   172  	copy(gh[:], params.GenesisHash)
   173  	var votePKBytes [32]byte
   174  	var selectionPKBytes [32]byte
   175  	var statePKBytes [64]byte
   176  
   177  	if len(voteKey) > 0 {
   178  		votePKBytes, err = byte32FromBase64(voteKey)
   179  		if err != nil {
   180  			return types.Transaction{}, err
   181  		}
   182  	}
   183  
   184  	if len(selectionKey) > 0 {
   185  		selectionPKBytes, err = byte32FromBase64(selectionKey)
   186  		if err != nil {
   187  			return types.Transaction{}, err
   188  		}
   189  	}
   190  
   191  	if len(stateProofPK) > 0 {
   192  		statePKBytes, err = byte64FromBase64(stateProofPK)
   193  		if err != nil {
   194  			return types.Transaction{}, err
   195  		}
   196  	}
   197  
   198  	tx := types.Transaction{
   199  		Type: types.KeyRegistrationTx,
   200  		Header: types.Header{
   201  			Sender:      accountAddr,
   202  			Fee:         params.Fee,
   203  			FirstValid:  params.FirstRoundValid,
   204  			LastValid:   params.LastRoundValid,
   205  			Note:        note,
   206  			GenesisHash: gh,
   207  			GenesisID:   params.GenesisID,
   208  		},
   209  		KeyregTxnFields: types.KeyregTxnFields{
   210  			VotePK:           types.VotePK(votePKBytes),
   211  			SelectionPK:      types.VRFPK(selectionPKBytes),
   212  			VoteFirst:        types.Round(voteFirst),
   213  			VoteLast:         types.Round(voteLast),
   214  			VoteKeyDilution:  voteKeyDilution,
   215  			Nonparticipation: nonpart,
   216  			StateProofPK:     types.MerkleVerifier(statePKBytes),
   217  		},
   218  	}
   219  
   220  	return setFee(tx, params)
   221  }
   222  
   223  // MakeAssetCreateTxn constructs an asset creation transaction using the passed parameters.
   224  // - account is a checksummed, human-readable address which will send the transaction.
   225  // - note is a byte array
   226  // - params is typically received from algod, it defines common-to-all-txns arguments like fee and validity period
   227  // Asset creation parameters:
   228  // - see asset.go
   229  func MakeAssetCreateTxn(account string, note []byte, params types.SuggestedParams, total uint64, decimals uint32, defaultFrozen bool, manager, reserve, freeze, clawback string, unitName, assetName, url, metadataHash string) (types.Transaction, error) {
   230  	var tx types.Transaction
   231  	var err error
   232  
   233  	if decimals > types.AssetMaxNumberOfDecimals {
   234  		return tx, fmt.Errorf("cannot create an asset with number of decimals %d (more than maximum %d)", decimals, types.AssetMaxNumberOfDecimals)
   235  	}
   236  
   237  	tx.Type = types.AssetConfigTx
   238  	tx.AssetParams = types.AssetParams{
   239  		Total:         total,
   240  		Decimals:      decimals,
   241  		DefaultFrozen: defaultFrozen,
   242  		UnitName:      unitName,
   243  		AssetName:     assetName,
   244  		URL:           url,
   245  	}
   246  
   247  	if manager != "" {
   248  		tx.AssetParams.Manager, err = types.DecodeAddress(manager)
   249  		if err != nil {
   250  			return tx, err
   251  		}
   252  	}
   253  	if reserve != "" {
   254  		tx.AssetParams.Reserve, err = types.DecodeAddress(reserve)
   255  		if err != nil {
   256  			return tx, err
   257  		}
   258  	}
   259  	if freeze != "" {
   260  		tx.AssetParams.Freeze, err = types.DecodeAddress(freeze)
   261  		if err != nil {
   262  			return tx, err
   263  		}
   264  	}
   265  	if clawback != "" {
   266  		tx.AssetParams.Clawback, err = types.DecodeAddress(clawback)
   267  		if err != nil {
   268  			return tx, err
   269  		}
   270  	}
   271  
   272  	if len(assetName) > types.AssetNameMaxLen {
   273  		return tx, fmt.Errorf("asset name too long: %d > %d", len(assetName), types.AssetNameMaxLen)
   274  	}
   275  	tx.AssetParams.AssetName = assetName
   276  
   277  	if len(url) > types.AssetURLMaxLen {
   278  		return tx, fmt.Errorf("asset url too long: %d > %d", len(url), types.AssetURLMaxLen)
   279  	}
   280  	tx.AssetParams.URL = url
   281  
   282  	if len(unitName) > types.AssetUnitNameMaxLen {
   283  		return tx, fmt.Errorf("asset unit name too long: %d > %d", len(unitName), types.AssetUnitNameMaxLen)
   284  	}
   285  	tx.AssetParams.UnitName = unitName
   286  
   287  	if len(metadataHash) > types.AssetMetadataHashLen {
   288  		return tx, fmt.Errorf("asset metadata hash '%s' too long: %d > %d)", metadataHash, len(metadataHash), types.AssetMetadataHashLen)
   289  	}
   290  	copy(tx.AssetParams.MetadataHash[:], []byte(metadataHash))
   291  
   292  	if len(params.GenesisHash) == 0 {
   293  		return types.Transaction{}, fmt.Errorf("asset transaction must contain a genesisHash")
   294  	}
   295  	var gh types.Digest
   296  	copy(gh[:], params.GenesisHash)
   297  
   298  	// Fill in header
   299  	accountAddr, err := types.DecodeAddress(account)
   300  	if err != nil {
   301  		return types.Transaction{}, err
   302  	}
   303  
   304  	tx.Header = types.Header{
   305  		Sender:      accountAddr,
   306  		Fee:         params.Fee,
   307  		FirstValid:  params.FirstRoundValid,
   308  		LastValid:   params.LastRoundValid,
   309  		GenesisHash: gh,
   310  		GenesisID:   params.GenesisID,
   311  		Note:        note,
   312  	}
   313  
   314  	// Update fee
   315  	return setFee(tx, params)
   316  }
   317  
   318  // MakeAssetConfigTxn creates a tx template for changing the
   319  // key configuration of an existing asset.
   320  // Important notes -
   321  // 	* Every asset config transaction is a fresh one. No parameters will be inherited from the current config.
   322  // 	* Once an address is set to to the empty string, IT CAN NEVER BE CHANGED AGAIN. For example, if you want to keep
   323  //    The current manager, you must specify its address again.
   324  //	Parameters -
   325  // - account is a checksummed, human-readable address that will send the transaction
   326  // - note is an arbitrary byte array
   327  // - params is typically received from algod, it defines common-to-all-txns arguments like fee and validity period
   328  // - index is the asset index id
   329  // - for newManager, newReserve, newFreeze, newClawback see asset.go
   330  // - strictEmptyAddressChecking: if true, disallow empty admin accounts from being set (preventing accidental disable of admin features)
   331  func MakeAssetConfigTxn(account string, note []byte, params types.SuggestedParams, index uint64, newManager, newReserve, newFreeze, newClawback string, strictEmptyAddressChecking bool) (types.Transaction, error) {
   332  	var tx types.Transaction
   333  
   334  	if strictEmptyAddressChecking && (newManager == "" || newReserve == "" || newFreeze == "" || newClawback == "") {
   335  		return tx, fmt.Errorf("strict empty address checking requested but empty address supplied to one or more manager addresses")
   336  	}
   337  
   338  	tx.Type = types.AssetConfigTx
   339  
   340  	accountAddr, err := types.DecodeAddress(account)
   341  	if err != nil {
   342  		return tx, err
   343  	}
   344  
   345  	if len(params.GenesisHash) == 0 {
   346  		return types.Transaction{}, fmt.Errorf("asset transaction must contain a genesisHash")
   347  	}
   348  	var gh types.Digest
   349  	copy(gh[:], params.GenesisHash)
   350  
   351  	tx.Header = types.Header{
   352  		Sender:      accountAddr,
   353  		Fee:         params.Fee,
   354  		FirstValid:  params.FirstRoundValid,
   355  		LastValid:   params.LastRoundValid,
   356  		GenesisHash: gh,
   357  		GenesisID:   params.GenesisID,
   358  		Note:        note,
   359  	}
   360  
   361  	tx.ConfigAsset = types.AssetIndex(index)
   362  
   363  	if newManager != "" {
   364  		tx.Type = types.AssetConfigTx
   365  		tx.AssetParams.Manager, err = types.DecodeAddress(newManager)
   366  		if err != nil {
   367  			return tx, err
   368  		}
   369  	}
   370  
   371  	if newReserve != "" {
   372  		tx.AssetParams.Reserve, err = types.DecodeAddress(newReserve)
   373  		if err != nil {
   374  			return tx, err
   375  		}
   376  	}
   377  
   378  	if newFreeze != "" {
   379  		tx.AssetParams.Freeze, err = types.DecodeAddress(newFreeze)
   380  		if err != nil {
   381  			return tx, err
   382  		}
   383  	}
   384  
   385  	if newClawback != "" {
   386  		tx.AssetParams.Clawback, err = types.DecodeAddress(newClawback)
   387  		if err != nil {
   388  			return tx, err
   389  		}
   390  	}
   391  
   392  	// Update fee
   393  	return setFee(tx, params)
   394  }
   395  
   396  // transferAssetBuilder is a helper that builds asset transfer transactions:
   397  // either a normal asset transfer, or an asset revocation
   398  func transferAssetBuilder(account, recipient string, amount uint64, note []byte, params types.SuggestedParams, index uint64, closeAssetsTo, revocationTarget string) (types.Transaction, error) {
   399  	var tx types.Transaction
   400  	tx.Type = types.AssetTransferTx
   401  
   402  	accountAddr, err := types.DecodeAddress(account)
   403  	if err != nil {
   404  		return tx, err
   405  	}
   406  
   407  	if len(params.GenesisHash) == 0 {
   408  		return types.Transaction{}, fmt.Errorf("asset transaction must contain a genesisHash")
   409  	}
   410  	var gh types.Digest
   411  	copy(gh[:], params.GenesisHash)
   412  
   413  	tx.Header = types.Header{
   414  		Sender:      accountAddr,
   415  		Fee:         params.Fee,
   416  		FirstValid:  params.FirstRoundValid,
   417  		LastValid:   params.LastRoundValid,
   418  		GenesisHash: gh,
   419  		GenesisID:   params.GenesisID,
   420  		Note:        note,
   421  	}
   422  
   423  	tx.XferAsset = types.AssetIndex(index)
   424  
   425  	recipientAddr, err := types.DecodeAddress(recipient)
   426  	if err != nil {
   427  		return tx, err
   428  	}
   429  	tx.AssetReceiver = recipientAddr
   430  
   431  	if closeAssetsTo != "" {
   432  		closeToAddr, err := types.DecodeAddress(closeAssetsTo)
   433  		if err != nil {
   434  			return tx, err
   435  		}
   436  		tx.AssetCloseTo = closeToAddr
   437  	}
   438  
   439  	if revocationTarget != "" {
   440  		revokedAddr, err := types.DecodeAddress(revocationTarget)
   441  		if err != nil {
   442  			return tx, err
   443  		}
   444  		tx.AssetSender = revokedAddr
   445  	}
   446  
   447  	tx.AssetAmount = amount
   448  
   449  	// Update fee
   450  	return setFee(tx, params)
   451  }
   452  
   453  // MakeAssetTransferTxn creates a tx for sending some asset from an asset holder to another user
   454  // the recipient address must have previously issued an asset acceptance transaction for this asset
   455  // - account is a checksummed, human-readable address that will send the transaction and assets
   456  // - recipient is a checksummed, human-readable address what will receive the assets
   457  // - amount is the number of assets to send
   458  // - note is an arbitrary byte array
   459  // - params is typically received from algod, it defines common-to-all-txns arguments like fee and validity period
   460  // - 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.
   461  // - index is the asset index
   462  func MakeAssetTransferTxn(account, recipient string, amount uint64, note []byte, params types.SuggestedParams, closeAssetsTo string, index uint64) (types.Transaction, error) {
   463  	revocationTarget := "" // no asset revocation, this is normal asset transfer
   464  	return transferAssetBuilder(account, recipient, amount, note, params, index, closeAssetsTo, revocationTarget)
   465  }
   466  
   467  // MakeAssetAcceptanceTxn creates a tx for marking an account as willing to accept the given asset
   468  // - account is a checksummed, human-readable address that will send the transaction and begin accepting the asset
   469  // - note is an arbitrary byte array
   470  // - params is typically received from algod, it defines common-to-all-txns arguments like fee and validity period
   471  // - index is the asset index
   472  func MakeAssetAcceptanceTxn(account string, note []byte, params types.SuggestedParams, index uint64) (types.Transaction, error) {
   473  	return MakeAssetTransferTxn(account, account, 0, note, params, "", index)
   474  }
   475  
   476  // MakeAssetRevocationTxn creates a tx for revoking an asset from an account and sending it to another
   477  // - account is a checksummed, human-readable address; it must be the revocation manager / clawback address from the asset's parameters
   478  // - target is a checksummed, human-readable address; it is the account whose assets will be revoked
   479  // - recipient is a checksummed, human-readable address; it will receive the revoked assets
   480  // - amount defines the number of assets to clawback
   481  // - params is typically received from algod, it defines common-to-all-txns arguments like fee and validity period
   482  // - index is the asset index
   483  func MakeAssetRevocationTxn(account, target string, amount uint64, recipient string, note []byte, params types.SuggestedParams, index uint64) (types.Transaction, error) {
   484  	closeAssetsTo := "" // no close-out, this is an asset revocation
   485  	return transferAssetBuilder(account, recipient, amount, note, params, index, closeAssetsTo, target)
   486  }
   487  
   488  // MakeAssetDestroyTxn creates a tx template for destroying an asset, removing it from the record.
   489  // All outstanding asset amount must be held by the creator, and this transaction must be issued by the asset manager.
   490  // - account is a checksummed, human-readable address that will send the transaction; it also must be the asset manager
   491  // - params is typically received from algod, it defines common-to-all-txns arguments like fee and validity period
   492  // - index is the asset index
   493  func MakeAssetDestroyTxn(account string, note []byte, params types.SuggestedParams, index uint64) (types.Transaction, error) {
   494  	// an asset destroy transaction is just a configuration transaction with AssetParams zeroed
   495  	return MakeAssetConfigTxn(account, note, params, index, "", "", "", "", false)
   496  }
   497  
   498  // MakeAssetFreezeTxn constructs a transaction that freezes or unfreezes an account's asset holdings
   499  // It must be issued by the freeze address for the asset
   500  // - account is a checksummed, human-readable address which will send the transaction.
   501  // - note is an optional arbitrary byte array
   502  // - params is typically received from algod, it defines common-to-all-txns arguments like fee and validity period
   503  // - assetIndex is the index for tracking the asset
   504  // - target is the account to be frozen or unfrozen
   505  // - newFreezeSetting is the new state of the target account
   506  func MakeAssetFreezeTxn(account string, note []byte, params types.SuggestedParams, assetIndex uint64, target string, newFreezeSetting bool) (types.Transaction, error) {
   507  	var tx types.Transaction
   508  
   509  	tx.Type = types.AssetFreezeTx
   510  
   511  	accountAddr, err := types.DecodeAddress(account)
   512  	if err != nil {
   513  		return tx, err
   514  	}
   515  
   516  	if len(params.GenesisHash) == 0 {
   517  		return types.Transaction{}, fmt.Errorf("asset transaction must contain a genesisHash")
   518  	}
   519  	var gh types.Digest
   520  	copy(gh[:], params.GenesisHash)
   521  
   522  	tx.Header = types.Header{
   523  		Sender:      accountAddr,
   524  		Fee:         params.Fee,
   525  		FirstValid:  params.FirstRoundValid,
   526  		LastValid:   params.LastRoundValid,
   527  		GenesisHash: gh,
   528  		GenesisID:   params.GenesisID,
   529  		Note:        note,
   530  	}
   531  
   532  	tx.FreezeAsset = types.AssetIndex(assetIndex)
   533  
   534  	tx.FreezeAccount, err = types.DecodeAddress(target)
   535  	if err != nil {
   536  		return tx, err
   537  	}
   538  
   539  	tx.AssetFrozen = newFreezeSetting
   540  
   541  	// Update fee
   542  	return setFee(tx, params)
   543  }
   544  
   545  // byte32FromBase64 decodes the input base64 string and outputs a
   546  // 32 byte array, erroring if the input is the wrong length.
   547  func byte32FromBase64(in string) (out [32]byte, err error) {
   548  	slice, err := base64.StdEncoding.DecodeString(in)
   549  	if err != nil {
   550  		return
   551  	}
   552  	if len(slice) != 32 {
   553  		return out, fmt.Errorf("Input is not 32 bytes")
   554  	}
   555  	copy(out[:], slice)
   556  	return
   557  }
   558  
   559  // byte32FromBase64 decodes the input base64 string and outputs a
   560  // 64 byte array, erroring if the input is the wrong length.
   561  func byte64FromBase64(in string) (out [64]byte, err error) {
   562  	slice, err := base64.StdEncoding.DecodeString(in)
   563  	if err != nil {
   564  		return
   565  	}
   566  	if len(slice) != 64 {
   567  		return out, fmt.Errorf("input is not 64 bytes")
   568  	}
   569  	copy(out[:], slice)
   570  	return
   571  }
   572  
   573  // - accounts      lists the accounts (in addition to the sender) that may be accessed
   574  //                 from the application logic.
   575  //
   576  // - appArgs       ApplicationArgs lists some transaction-specific arguments accessible
   577  //                 from application logic.
   578  //
   579  // - appIdx        ApplicationID is the application being interacted with, or 0 if
   580  //                 creating a new application.
   581  //
   582  // - approvalProg  ApprovalProgram determines whether or not this ApplicationCall
   583  //                 transaction will be approved or not.
   584  //
   585  // - clearProg     ClearStateProgram executes when a clear state ApplicationCall
   586  //                 transaction is executed. This program may not reject the
   587  //                 transaction, only update state.
   588  //
   589  // - foreignApps   lists the applications (in addition to txn.ApplicationID) whose global
   590  //                 states may be accessed by this application. The access is read-only.
   591  //
   592  // - foreignAssets lists the assets whose global state may be accessed by this application. The access is read-only.
   593  //
   594  // - globalSchema  GlobalStateSchema sets limits on the number of strings and
   595  //                 integers that may be stored in the GlobalState. The larger these
   596  //                 limits are, the larger minimum balance must be maintained inside
   597  //                 the creator's account (in order to 'pay' for the state that can
   598  //                 be used). The GlobalStateSchema is immutable.
   599  //
   600  // - localSchema   LocalStateSchema sets limits on the number of strings and integers
   601  //                 that may be stored in an account's LocalState for this application.
   602  //                 The larger these limits are, the larger minimum balance must be
   603  //                 maintained inside the account of any users who opt into this
   604  //                 application. The LocalStateSchema is immutable.
   605  //
   606  // - extraPages    ExtraProgramPages specifies the additional app program size requested in pages.
   607  //                 A page is 1024 bytes. This field enables execution of app programs
   608  //                 larger than the default maximum program size.
   609  //
   610  // - onComplete    This is the faux application type used to distinguish different
   611  //                 application actions. Specifically, OnCompletion specifies what
   612  //                 side effects this transaction will have if it successfully makes
   613  //                 it into a block.
   614  //
   615  // - boxes         lists the boxes to be accessed during evaluation of the application
   616  //                 call. This also must include the boxes accessed by inner app calls.
   617  
   618  // MakeApplicationCreateTx makes a transaction for creating an application (see above for args desc.)
   619  // - optIn: true for opting in on complete, false for no-op.
   620  //
   621  // NOTE: if you need to use extra pages or boxes, use MakeApplicationCreateTxWithBoxes instead.
   622  func MakeApplicationCreateTx(
   623  	optIn bool,
   624  	approvalProg []byte,
   625  	clearProg []byte,
   626  	globalSchema types.StateSchema,
   627  	localSchema types.StateSchema,
   628  	appArgs [][]byte,
   629  	accounts []string,
   630  	foreignApps []uint64,
   631  	foreignAssets []uint64,
   632  	sp types.SuggestedParams,
   633  	sender types.Address,
   634  	note []byte,
   635  	group types.Digest,
   636  	lease [32]byte,
   637  	rekeyTo types.Address) (tx types.Transaction, err error) {
   638  	return MakeApplicationCreateTxWithBoxes(
   639  		optIn,
   640  		approvalProg,
   641  		clearProg,
   642  		globalSchema,
   643  		localSchema,
   644  		0,
   645  		appArgs,
   646  		accounts,
   647  		foreignApps,
   648  		foreignAssets,
   649  		nil,
   650  		sp,
   651  		sender,
   652  		note,
   653  		group,
   654  		lease,
   655  		rekeyTo,
   656  	)
   657  }
   658  
   659  // MakeApplicationCreateTxWithExtraPages makes a transaction for creating an application (see above for args desc.)
   660  // - optIn: true for opting in on complete, false for no-op.
   661  //
   662  // NOTE: if you need to use boxes, use MakeApplicationCreateTxWithBoxes instead.
   663  func MakeApplicationCreateTxWithExtraPages(
   664  	optIn bool,
   665  	approvalProg []byte,
   666  	clearProg []byte,
   667  	globalSchema types.StateSchema,
   668  	localSchema types.StateSchema,
   669  	appArgs [][]byte,
   670  	accounts []string,
   671  	foreignApps []uint64,
   672  	foreignAssets []uint64,
   673  	sp types.SuggestedParams,
   674  	sender types.Address,
   675  	note []byte,
   676  	group types.Digest,
   677  	lease [32]byte,
   678  	rekeyTo types.Address,
   679  	extraPages uint32) (tx types.Transaction, err error) {
   680  	return MakeApplicationCreateTxWithBoxes(
   681  		optIn,
   682  		approvalProg,
   683  		clearProg,
   684  		globalSchema,
   685  		localSchema,
   686  		extraPages,
   687  		appArgs,
   688  		accounts,
   689  		foreignApps,
   690  		foreignAssets,
   691  		nil,
   692  		sp,
   693  		sender,
   694  		note,
   695  		group,
   696  		lease,
   697  		rekeyTo,
   698  	)
   699  }
   700  
   701  // MakeApplicationCreateTxWithBoxes makes a transaction for creating an application (see above for args desc.)
   702  // - optIn: true for opting in on complete, false for no-op.
   703  func MakeApplicationCreateTxWithBoxes(
   704  	optIn bool,
   705  	approvalProg []byte,
   706  	clearProg []byte,
   707  	globalSchema types.StateSchema,
   708  	localSchema types.StateSchema,
   709  	extraPages uint32,
   710  	appArgs [][]byte,
   711  	accounts []string,
   712  	foreignApps []uint64,
   713  	foreignAssets []uint64,
   714  	appBoxReferences []types.AppBoxReference,
   715  	sp types.SuggestedParams,
   716  	sender types.Address,
   717  	note []byte,
   718  	group types.Digest,
   719  	lease [32]byte,
   720  	rekeyTo types.Address) (tx types.Transaction, err error) {
   721  
   722  	oncomp := types.NoOpOC
   723  	if optIn {
   724  		oncomp = types.OptInOC
   725  	}
   726  
   727  	return MakeApplicationCallTxWithBoxes(
   728  		0,
   729  		appArgs,
   730  		accounts,
   731  		foreignApps,
   732  		foreignAssets,
   733  		appBoxReferences,
   734  		oncomp,
   735  		approvalProg,
   736  		clearProg,
   737  		globalSchema,
   738  		localSchema,
   739  		extraPages,
   740  		sp,
   741  		sender,
   742  		note,
   743  		group,
   744  		lease,
   745  		rekeyTo,
   746  	)
   747  }
   748  
   749  // MakeApplicationUpdateTx makes a transaction for updating an application's programs (see above for args desc.)
   750  //
   751  // NOTE: if you need to use boxes, use MakeApplicationUpdateTxWithBoxes instead.
   752  func MakeApplicationUpdateTx(
   753  	appIdx uint64,
   754  	appArgs [][]byte,
   755  	accounts []string,
   756  	foreignApps []uint64,
   757  	foreignAssets []uint64,
   758  	approvalProg []byte,
   759  	clearProg []byte,
   760  	sp types.SuggestedParams,
   761  	sender types.Address,
   762  	note []byte,
   763  	group types.Digest,
   764  	lease [32]byte,
   765  	rekeyTo types.Address) (tx types.Transaction, err error) {
   766  	return MakeApplicationUpdateTxWithBoxes(appIdx,
   767  		appArgs,
   768  		accounts,
   769  		foreignApps,
   770  		foreignAssets,
   771  		nil,
   772  		approvalProg,
   773  		clearProg,
   774  		sp,
   775  		sender,
   776  		note,
   777  		group,
   778  		lease,
   779  		rekeyTo,
   780  	)
   781  }
   782  
   783  // MakeApplicationUpdateTxWithBoxes makes a transaction for updating an application's programs (see above for args desc.)
   784  func MakeApplicationUpdateTxWithBoxes(
   785  	appIdx uint64,
   786  	appArgs [][]byte,
   787  	accounts []string,
   788  	foreignApps []uint64,
   789  	foreignAssets []uint64,
   790  	appBoxReferences []types.AppBoxReference,
   791  	approvalProg []byte,
   792  	clearProg []byte,
   793  	sp types.SuggestedParams,
   794  	sender types.Address,
   795  	note []byte,
   796  	group types.Digest,
   797  	lease [32]byte,
   798  	rekeyTo types.Address) (tx types.Transaction, err error) {
   799  	return MakeApplicationCallTxWithBoxes(appIdx,
   800  		appArgs,
   801  		accounts,
   802  		foreignApps,
   803  		foreignAssets,
   804  		appBoxReferences,
   805  		types.UpdateApplicationOC,
   806  		approvalProg,
   807  		clearProg,
   808  		emptySchema,
   809  		emptySchema,
   810  		0,
   811  		sp,
   812  		sender,
   813  		note,
   814  		group,
   815  		lease,
   816  		rekeyTo,
   817  	)
   818  }
   819  
   820  // MakeApplicationDeleteTx makes a transaction for deleting an application (see above for args desc.)
   821  //
   822  // NOTE: if you need to use boxes, use MakeApplicationDeleteTxWithBoxes instead.
   823  func MakeApplicationDeleteTx(
   824  	appIdx uint64,
   825  	appArgs [][]byte,
   826  	accounts []string,
   827  	foreignApps []uint64,
   828  	foreignAssets []uint64,
   829  	sp types.SuggestedParams,
   830  	sender types.Address,
   831  	note []byte,
   832  	group types.Digest,
   833  	lease [32]byte,
   834  	rekeyTo types.Address) (tx types.Transaction, err error) {
   835  	return MakeApplicationDeleteTxWithBoxes(appIdx,
   836  		appArgs,
   837  		accounts,
   838  		foreignApps,
   839  		foreignAssets,
   840  		nil,
   841  		sp,
   842  		sender,
   843  		note,
   844  		group,
   845  		lease,
   846  		rekeyTo,
   847  	)
   848  }
   849  
   850  // MakeApplicationDeleteTxWithBoxes makes a transaction for deleting an application (see above for args desc.)
   851  func MakeApplicationDeleteTxWithBoxes(
   852  	appIdx uint64,
   853  	appArgs [][]byte,
   854  	accounts []string,
   855  	foreignApps []uint64,
   856  	foreignAssets []uint64,
   857  	appBoxReferences []types.AppBoxReference,
   858  	sp types.SuggestedParams,
   859  	sender types.Address,
   860  	note []byte,
   861  	group types.Digest,
   862  	lease [32]byte,
   863  	rekeyTo types.Address) (tx types.Transaction, err error) {
   864  	return MakeApplicationCallTxWithBoxes(appIdx,
   865  		appArgs,
   866  		accounts,
   867  		foreignApps,
   868  		foreignAssets,
   869  		appBoxReferences,
   870  		types.DeleteApplicationOC,
   871  		nil,
   872  		nil,
   873  		emptySchema,
   874  		emptySchema,
   875  		0,
   876  		sp,
   877  		sender,
   878  		note,
   879  		group,
   880  		lease,
   881  		rekeyTo,
   882  	)
   883  }
   884  
   885  // MakeApplicationOptInTx makes a transaction for opting in to (allocating
   886  // some account-specific state for) an application (see above for args desc.)
   887  //
   888  // NOTE: if you need to use boxes, use MakeApplicationOptInTxWithBoxes instead.
   889  func MakeApplicationOptInTx(
   890  	appIdx uint64,
   891  	appArgs [][]byte,
   892  	accounts []string,
   893  	foreignApps []uint64,
   894  	foreignAssets []uint64,
   895  	sp types.SuggestedParams,
   896  	sender types.Address,
   897  	note []byte,
   898  	group types.Digest,
   899  	lease [32]byte,
   900  	rekeyTo types.Address) (tx types.Transaction, err error) {
   901  	return MakeApplicationOptInTxWithBoxes(appIdx,
   902  		appArgs,
   903  		accounts,
   904  		foreignApps,
   905  		foreignAssets,
   906  		nil,
   907  		sp,
   908  		sender,
   909  		note,
   910  		group,
   911  		lease,
   912  		rekeyTo,
   913  	)
   914  }
   915  
   916  // MakeApplicationOptInTxWithBoxes makes a transaction for opting in to (allocating
   917  // some account-specific state for) an application (see above for args desc.)
   918  func MakeApplicationOptInTxWithBoxes(
   919  	appIdx uint64,
   920  	appArgs [][]byte,
   921  	accounts []string,
   922  	foreignApps []uint64,
   923  	foreignAssets []uint64,
   924  	appBoxReferences []types.AppBoxReference,
   925  	sp types.SuggestedParams,
   926  	sender types.Address,
   927  	note []byte,
   928  	group types.Digest,
   929  	lease [32]byte,
   930  	rekeyTo types.Address) (tx types.Transaction, err error) {
   931  	return MakeApplicationCallTxWithBoxes(appIdx,
   932  		appArgs,
   933  		accounts,
   934  		foreignApps,
   935  		foreignAssets,
   936  		appBoxReferences,
   937  		types.OptInOC,
   938  		nil,
   939  		nil,
   940  		emptySchema,
   941  		emptySchema,
   942  		0,
   943  		sp,
   944  		sender,
   945  		note,
   946  		group,
   947  		lease,
   948  		rekeyTo,
   949  	)
   950  }
   951  
   952  // MakeApplicationCloseOutTx makes a transaction for closing out of
   953  // (deallocating all account-specific state for) an application (see above for args desc.)
   954  //
   955  // NOTE: if you need to use boxes, use MakeApplicationCloseOutTxWithBoxes
   956  // instead.
   957  func MakeApplicationCloseOutTx(
   958  	appIdx uint64,
   959  	appArgs [][]byte,
   960  	accounts []string,
   961  	foreignApps []uint64,
   962  	foreignAssets []uint64,
   963  	sp types.SuggestedParams,
   964  	sender types.Address,
   965  	note []byte,
   966  	group types.Digest,
   967  	lease [32]byte,
   968  	rekeyTo types.Address) (tx types.Transaction, err error) {
   969  	return MakeApplicationCloseOutTxWithBoxes(appIdx,
   970  		appArgs,
   971  		accounts,
   972  		foreignApps,
   973  		foreignAssets,
   974  		nil,
   975  		sp,
   976  		sender,
   977  		note,
   978  		group,
   979  		lease,
   980  		rekeyTo,
   981  	)
   982  }
   983  
   984  // MakeApplicationCloseOutTxWithBoxes makes a transaction for closing out of
   985  // (deallocating all account-specific state for) an application (see above for args desc.)
   986  func MakeApplicationCloseOutTxWithBoxes(
   987  	appIdx uint64,
   988  	appArgs [][]byte,
   989  	accounts []string,
   990  	foreignApps []uint64,
   991  	foreignAssets []uint64,
   992  	appBoxReferences []types.AppBoxReference,
   993  	sp types.SuggestedParams,
   994  	sender types.Address,
   995  	note []byte,
   996  	group types.Digest,
   997  	lease [32]byte,
   998  	rekeyTo types.Address) (tx types.Transaction, err error) {
   999  	return MakeApplicationCallTxWithBoxes(appIdx,
  1000  		appArgs,
  1001  		accounts,
  1002  		foreignApps,
  1003  		foreignAssets,
  1004  		appBoxReferences,
  1005  		types.CloseOutOC,
  1006  		nil,
  1007  		nil,
  1008  		emptySchema,
  1009  		emptySchema,
  1010  		0,
  1011  		sp,
  1012  		sender,
  1013  		note,
  1014  		group,
  1015  		lease,
  1016  		rekeyTo,
  1017  	)
  1018  }
  1019  
  1020  // MakeApplicationClearStateTx makes a transaction for clearing out all
  1021  // account-specific state for an application. It may not be rejected by the
  1022  // application's logic. (see above for args desc.)
  1023  //
  1024  // NOTE: if you need to use boxes, use MakeApplicationClearStateTxWithBoxes
  1025  // instead.
  1026  func MakeApplicationClearStateTx(
  1027  	appIdx uint64,
  1028  	appArgs [][]byte,
  1029  	accounts []string,
  1030  	foreignApps []uint64,
  1031  	foreignAssets []uint64,
  1032  	sp types.SuggestedParams,
  1033  	sender types.Address,
  1034  	note []byte,
  1035  	group types.Digest,
  1036  	lease [32]byte,
  1037  	rekeyTo types.Address) (tx types.Transaction, err error) {
  1038  	return MakeApplicationClearStateTxWithBoxes(appIdx,
  1039  		appArgs,
  1040  		accounts,
  1041  		foreignApps,
  1042  		foreignAssets,
  1043  		nil,
  1044  		sp,
  1045  		sender,
  1046  		note,
  1047  		group,
  1048  		lease,
  1049  		rekeyTo,
  1050  	)
  1051  }
  1052  
  1053  // MakeApplicationClearStateTxWithBoxes makes a transaction for clearing out all
  1054  // account-specific state for an application. It may not be rejected by the
  1055  // application's logic. (see above for args desc.)
  1056  func MakeApplicationClearStateTxWithBoxes(
  1057  	appIdx uint64,
  1058  	appArgs [][]byte,
  1059  	accounts []string,
  1060  	foreignApps []uint64,
  1061  	foreignAssets []uint64,
  1062  	appBoxReferences []types.AppBoxReference,
  1063  	sp types.SuggestedParams,
  1064  	sender types.Address,
  1065  	note []byte,
  1066  	group types.Digest,
  1067  	lease [32]byte,
  1068  	rekeyTo types.Address) (tx types.Transaction, err error) {
  1069  	return MakeApplicationCallTxWithBoxes(appIdx,
  1070  		appArgs,
  1071  		accounts,
  1072  		foreignApps,
  1073  		foreignAssets,
  1074  		appBoxReferences,
  1075  		types.ClearStateOC,
  1076  		nil,
  1077  		nil,
  1078  		emptySchema,
  1079  		emptySchema,
  1080  		0,
  1081  		sp,
  1082  		sender,
  1083  		note,
  1084  		group,
  1085  		lease,
  1086  		rekeyTo,
  1087  	)
  1088  }
  1089  
  1090  // MakeApplicationNoOpTx makes a transaction for interacting with an existing
  1091  // application, potentially updating any account-specific local state and
  1092  // global state associated with it. (see above for args desc.)
  1093  //
  1094  // NOTE: if you need to use boxes, use MakeApplicationNoOpTxWithBoxes instead.
  1095  func MakeApplicationNoOpTx(
  1096  	appIdx uint64,
  1097  	appArgs [][]byte,
  1098  	accounts []string,
  1099  	foreignApps []uint64,
  1100  	foreignAssets []uint64,
  1101  	sp types.SuggestedParams,
  1102  	sender types.Address,
  1103  	note []byte,
  1104  	group types.Digest,
  1105  	lease [32]byte,
  1106  	rekeyTo types.Address) (tx types.Transaction, err error) {
  1107  	return MakeApplicationNoOpTxWithBoxes(
  1108  		appIdx,
  1109  		appArgs,
  1110  		accounts,
  1111  		foreignApps,
  1112  		foreignAssets,
  1113  		nil,
  1114  		sp,
  1115  		sender,
  1116  		note,
  1117  		group,
  1118  		lease,
  1119  		rekeyTo,
  1120  	)
  1121  }
  1122  
  1123  // MakeApplicationNoOpTxWithBoxes makes a transaction for interacting with an
  1124  // existing application, potentially updating any account-specific local state
  1125  // and global state associated with it. (see above for args desc.)
  1126  func MakeApplicationNoOpTxWithBoxes(
  1127  	appIdx uint64,
  1128  	appArgs [][]byte,
  1129  	accounts []string,
  1130  	foreignApps []uint64,
  1131  	foreignAssets []uint64,
  1132  	appBoxReferences []types.AppBoxReference,
  1133  	sp types.SuggestedParams,
  1134  	sender types.Address,
  1135  	note []byte,
  1136  	group types.Digest,
  1137  	lease [32]byte,
  1138  	rekeyTo types.Address) (tx types.Transaction, err error) {
  1139  	return MakeApplicationCallTxWithBoxes(
  1140  		appIdx,
  1141  		appArgs,
  1142  		accounts,
  1143  		foreignApps,
  1144  		foreignAssets,
  1145  		appBoxReferences,
  1146  		types.NoOpOC,
  1147  		nil,
  1148  		nil,
  1149  		emptySchema,
  1150  		emptySchema,
  1151  		0,
  1152  		sp,
  1153  		sender,
  1154  		note,
  1155  		group,
  1156  		lease,
  1157  		rekeyTo,
  1158  	)
  1159  }
  1160  
  1161  // MakeApplicationCallTx is a helper for the above ApplicationCall
  1162  // transaction constructors. A fully custom ApplicationCall transaction may
  1163  // be constructed using this method. (see above for args desc.)
  1164  //
  1165  // NOTE: if you need to use boxes or extra program pages, use
  1166  // MakeApplicationCallTxWithBoxes instead.
  1167  func MakeApplicationCallTx(
  1168  	appIdx uint64,
  1169  	appArgs [][]byte,
  1170  	accounts []string,
  1171  	foreignApps []uint64,
  1172  	foreignAssets []uint64,
  1173  	onCompletion types.OnCompletion,
  1174  	approvalProg []byte,
  1175  	clearProg []byte,
  1176  	globalSchema types.StateSchema,
  1177  	localSchema types.StateSchema,
  1178  	sp types.SuggestedParams,
  1179  	sender types.Address,
  1180  	note []byte,
  1181  	group types.Digest,
  1182  	lease [32]byte,
  1183  	rekeyTo types.Address) (tx types.Transaction, err error) {
  1184  	return MakeApplicationCallTxWithBoxes(
  1185  		appIdx,
  1186  		appArgs,
  1187  		accounts,
  1188  		foreignApps,
  1189  		foreignAssets,
  1190  		nil,
  1191  		onCompletion,
  1192  		approvalProg,
  1193  		clearProg,
  1194  		globalSchema,
  1195  		localSchema,
  1196  		0,
  1197  		sp,
  1198  		sender,
  1199  		note,
  1200  		group,
  1201  		lease,
  1202  		rekeyTo,
  1203  	)
  1204  }
  1205  
  1206  // MakeApplicationCallTxWithExtraPages sets the ExtraProgramPages on an existing
  1207  // application call transaction.
  1208  //
  1209  // Consider using MakeApplicationCallTxWithBoxes instead if you wish to assign
  1210  // the extra pages value at creation.
  1211  func MakeApplicationCallTxWithExtraPages(
  1212  	txn types.Transaction, extraPages uint32) (types.Transaction, error) {
  1213  	txn.ExtraProgramPages = extraPages
  1214  	return txn, nil
  1215  }
  1216  
  1217  // MakeApplicationCallTxWithBoxes is a helper for the above ApplicationCall
  1218  // transaction constructors. A fully custom ApplicationCall transaction may
  1219  // be constructed using this method. (see above for args desc.)
  1220  func MakeApplicationCallTxWithBoxes(
  1221  	appIdx uint64,
  1222  	appArgs [][]byte,
  1223  	accounts []string,
  1224  	foreignApps []uint64,
  1225  	foreignAssets []uint64,
  1226  	appBoxReferences []types.AppBoxReference,
  1227  	onCompletion types.OnCompletion,
  1228  	approvalProg []byte,
  1229  	clearProg []byte,
  1230  	globalSchema types.StateSchema,
  1231  	localSchema types.StateSchema,
  1232  	extraPages uint32,
  1233  	sp types.SuggestedParams,
  1234  	sender types.Address,
  1235  	note []byte,
  1236  	group types.Digest,
  1237  	lease [32]byte,
  1238  	rekeyTo types.Address) (tx types.Transaction, err error) {
  1239  	tx.Type = types.ApplicationCallTx
  1240  	tx.ApplicationID = types.AppIndex(appIdx)
  1241  	tx.OnCompletion = onCompletion
  1242  
  1243  	tx.ApplicationArgs = appArgs
  1244  	tx.Accounts, err = parseTxnAccounts(accounts)
  1245  	if err != nil {
  1246  		return tx, err
  1247  	}
  1248  
  1249  	tx.ForeignApps = parseTxnForeignApps(foreignApps)
  1250  	tx.ForeignAssets = parseTxnForeignAssets(foreignAssets)
  1251  	tx.BoxReferences, err = parseBoxReferences(appBoxReferences, foreignApps, appIdx)
  1252  	if err != nil {
  1253  		return tx, err
  1254  	}
  1255  
  1256  	tx.ApprovalProgram = approvalProg
  1257  	tx.ClearStateProgram = clearProg
  1258  	tx.LocalStateSchema = localSchema
  1259  	tx.GlobalStateSchema = globalSchema
  1260  	tx.ExtraProgramPages = extraPages
  1261  
  1262  	var gh types.Digest
  1263  	copy(gh[:], sp.GenesisHash)
  1264  
  1265  	tx.Header = types.Header{
  1266  		Sender:      sender,
  1267  		Fee:         sp.Fee,
  1268  		FirstValid:  sp.FirstRoundValid,
  1269  		LastValid:   sp.LastRoundValid,
  1270  		Note:        note,
  1271  		GenesisID:   sp.GenesisID,
  1272  		GenesisHash: gh,
  1273  		Group:       group,
  1274  		Lease:       lease,
  1275  		RekeyTo:     rekeyTo,
  1276  	}
  1277  
  1278  	// Update fee
  1279  	return setFee(tx, sp)
  1280  }
  1281  
  1282  // AssignGroupID computes and return list of transactions with Group field set.
  1283  // - txns is a list of transactions to process
  1284  // - account specifies a sender field of transaction to return. Set to empty string to return all of them
  1285  func AssignGroupID(txns []types.Transaction, account string) (result []types.Transaction, err error) {
  1286  	gid, err := crypto.ComputeGroupID(txns)
  1287  	if err != nil {
  1288  		return
  1289  	}
  1290  	var decoded types.Address
  1291  	if account != "" {
  1292  		decoded, err = types.DecodeAddress(account)
  1293  		if err != nil {
  1294  			return
  1295  		}
  1296  	}
  1297  	for _, tx := range txns {
  1298  		if account == "" || bytes.Compare(tx.Sender[:], decoded[:]) == 0 {
  1299  			tx.Group = gid
  1300  			result = append(result, tx)
  1301  		}
  1302  	}
  1303  	return result, nil
  1304  }
  1305  
  1306  // EstimateSize returns the estimated length of the encoded transaction
  1307  func EstimateSize(txn types.Transaction) (uint64, error) {
  1308  	return uint64(len(msgpack.Encode(txn))) + NumOfAdditionalBytesAfterSigning, nil
  1309  }
  1310  
  1311  func parseTxnAccounts(accounts []string) (parsed []types.Address, err error) {
  1312  	for _, acct := range accounts {
  1313  		addr, err := types.DecodeAddress(acct)
  1314  		if err != nil {
  1315  			return nil, err
  1316  		}
  1317  		parsed = append(parsed, addr)
  1318  	}
  1319  	return
  1320  }
  1321  
  1322  func parseTxnForeignApps(foreignApps []uint64) (parsed []types.AppIndex) {
  1323  	for _, aidx := range foreignApps {
  1324  		parsed = append(parsed, types.AppIndex(aidx))
  1325  	}
  1326  	return
  1327  }
  1328  
  1329  func parseTxnForeignAssets(foreignAssets []uint64) (parsed []types.AssetIndex) {
  1330  	for _, aidx := range foreignAssets {
  1331  		parsed = append(parsed, types.AssetIndex(aidx))
  1332  	}
  1333  	return
  1334  }
  1335  
  1336  func parseBoxReferences(abrs []types.AppBoxReference, foreignApps []uint64, curAppID uint64) (parsed []types.BoxReference, err error) {
  1337  	for _, abr := range abrs {
  1338  		// there are a few unintuitive details to the parsing:
  1339  		//     1. the AppID of the box must either be in the foreign apps array or
  1340  		//        equal to 0, which references the current app.
  1341  		//     2. if the box references the current app by its appID rather than 0 AND
  1342  		//        the current appID is explicitly provided in the foreign apps array
  1343  		//        then ForeignAppIdx should be set to its index in the array.
  1344  		br := types.BoxReference{Name: abr.Name}
  1345  		found := false
  1346  
  1347  		if abr.AppID == 0 {
  1348  			found = true
  1349  			br.ForeignAppIdx = 0
  1350  		} else {
  1351  			for idx, appID := range foreignApps {
  1352  				if appID == abr.AppID {
  1353  					found = true
  1354  					br.ForeignAppIdx = uint64(idx + 1)
  1355  					break
  1356  				}
  1357  			}
  1358  		}
  1359  
  1360  		if !found && abr.AppID == curAppID {
  1361  			found = true
  1362  			br.ForeignAppIdx = 0
  1363  		}
  1364  
  1365  		if !found {
  1366  			return nil, fmt.Errorf("the app id %d provided for this box is not in the foreignApps array", abr.AppID)
  1367  		}
  1368  
  1369  		parsed = append(parsed, br)
  1370  	}
  1371  
  1372  	return
  1373  }
  1374  
  1375  var emptySchema = types.StateSchema{}