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

     1  package templates
     2  
     3  import (
     4  	"encoding/base64"
     5  	"fmt"
     6  
     7  	"github.com/algorand/go-algorand-sdk/crypto"
     8  	"github.com/algorand/go-algorand-sdk/future"
     9  	"github.com/algorand/go-algorand-sdk/logic"
    10  	"github.com/algorand/go-algorand-sdk/transaction"
    11  	"github.com/algorand/go-algorand-sdk/types"
    12  	"golang.org/x/crypto/ed25519"
    13  )
    14  
    15  // DynamicFee template representation
    16  type DynamicFee struct {
    17  	ContractTemplate
    18  }
    19  
    20  // MakeDynamicFee contract allows you to create a transaction without
    21  // specifying the fee. The fee will be determined at the moment of
    22  // transfer.
    23  //
    24  // Parameters:
    25  //  - receiver: address which is authorized to receive withdrawals
    26  //  - closeRemainder: address which will receive the balance of funds
    27  //  - amount: the maximum number of funds allowed for a single withdrawal
    28  //  - withdrawWindow: the duration of a withdrawal period
    29  //  - period: the time between a pair of withdrawal periods
    30  //  - expiryRound: the round at which the account expires
    31  //  - maxFee: maximum fee used by the withdrawal transaction
    32  //
    33  // Deprecated: Use TealCompile source compilation instead.
    34  func MakeDynamicFee(receiver, closeRemainder string, amount, firstValid, lastValid uint64) (DynamicFee, error) {
    35  	leaseBytes := make([]byte, 32)
    36  	crypto.RandomBytes(leaseBytes)
    37  	leaseString := base64.StdEncoding.EncodeToString(leaseBytes)
    38  	return makeDynamicFeeWithLease(receiver, closeRemainder, leaseString, amount, firstValid, lastValid)
    39  }
    40  
    41  // makeDynamicFeeWithLease is as MakeDynamicFee, but the caller can specify the lease (using b64 string)
    42  func makeDynamicFeeWithLease(receiver, closeRemainder, lease string, amount, firstValid, lastValid uint64) (DynamicFee, error) {
    43  	const referenceProgram = "ASAFAgEHBgUmAyD+vKC7FEpaTqe0OKRoGsgObKEFvLYH/FZTJclWlfaiEyDmmpYeby1feshmB5JlUr6YI17TM2PKiJGLuck4qRW2+SB/g7Flf/H8U7ktwYFIodZd/C1LH6PWdyhK3dIAEm2QaTIEIhIzABAjEhAzAAcxABIQMwAIMQESEDEWIxIQMRAjEhAxBygSEDEJKRIQMQgkEhAxAiUSEDEEIQQSEDEGKhIQ"
    44  	referenceAsBytes, err := base64.StdEncoding.DecodeString(referenceProgram)
    45  	if err != nil {
    46  		return DynamicFee{}, err
    47  	}
    48  	receiverAddr, err := types.DecodeAddress(receiver)
    49  	if err != nil {
    50  		return DynamicFee{}, err
    51  	}
    52  	var closeRemainderAddr types.Address
    53  	if closeRemainder != "" {
    54  		closeRemainderAddr, err = types.DecodeAddress(closeRemainder)
    55  		if err != nil {
    56  			return DynamicFee{}, err
    57  		}
    58  	}
    59  
    60  	var referenceOffsets = []uint64{ /*amount*/ 5 /*firstValid*/, 6 /*lastValid*/, 7 /*receiver*/, 11 /*closeRemainder*/, 44 /*lease*/, 76}
    61  	injectionVector := []interface{}{amount, firstValid, lastValid, receiverAddr, closeRemainderAddr, lease}
    62  	injectedBytes, err := inject(referenceAsBytes, referenceOffsets, injectionVector)
    63  	if err != nil {
    64  		return DynamicFee{}, err
    65  	}
    66  
    67  	address := crypto.AddressFromProgram(injectedBytes)
    68  	dynamicFee := DynamicFee{
    69  		ContractTemplate: ContractTemplate{
    70  			address: address.String(),
    71  			program: injectedBytes,
    72  		},
    73  	}
    74  	return dynamicFee, err
    75  }
    76  
    77  // GetDynamicFeeTransactions creates and signs the secondary dynamic fee transaction, updates
    78  // transaction fields, and signs as the fee payer; it returns both
    79  // transactions as bytes suitable for sendRaw.
    80  // Parameters:
    81  // txn - main transaction from payer
    82  // lsig - the signed logic received from the payer
    83  // privateKey - the private key for the account that pays the fee
    84  // fee - fee per byte for both transactions
    85  // firstValid - first protocol round on which both transactions will be valid
    86  // lastValid - last protocol round on which both transactions will be valid
    87  //
    88  // Deprecated: Use TealCompile source compilation instead.
    89  func GetDynamicFeeTransactions(txn types.Transaction, lsig types.LogicSig, privateKey ed25519.PrivateKey, fee uint64) ([]byte, error) {
    90  	txn.Fee = types.MicroAlgos(fee)
    91  	eSize, err := transaction.EstimateSize(txn)
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  	txn.Fee = types.MicroAlgos(eSize * fee)
    96  
    97  	if txn.Fee < transaction.MinTxnFee {
    98  		txn.Fee = transaction.MinTxnFee
    99  	}
   100  
   101  	address := types.Address{}
   102  	copy(address[:], privateKey[ed25519.PublicKeySize:])
   103  	genesisHash := make([]byte, 32)
   104  	copy(genesisHash[:], txn.GenesisHash[:])
   105  
   106  	params := types.SuggestedParams{
   107  		Fee:             types.MicroAlgos(fee),
   108  		GenesisID:       txn.GenesisID,
   109  		GenesisHash:     genesisHash,
   110  		FirstRoundValid: txn.FirstValid,
   111  		LastRoundValid:  txn.LastValid,
   112  		FlatFee:         false,
   113  	}
   114  
   115  	feePayTxn, err := future.MakePaymentTxn(address.String(), txn.Sender.String(), uint64(txn.Fee), nil, "", params)
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  	feePayTxn.AddLease(txn.Lease, fee)
   120  
   121  	txnGroup := []types.Transaction{feePayTxn, txn}
   122  
   123  	updatedTxns, err := transaction.AssignGroupID(txnGroup, "")
   124  
   125  	_, stx1Bytes, err := crypto.SignTransaction(privateKey, updatedTxns[0])
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  	_, stx2Bytes, err := crypto.SignLogicsigTransaction(lsig, updatedTxns[1])
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	return append(stx1Bytes, stx2Bytes...), nil
   134  }
   135  
   136  // SignDynamicFee takes in the contract bytes and returns the main transaction and signed logic needed to complete the
   137  // transfer. These should be sent to the fee payer, who can use
   138  // GetDynamicFeeTransactions() to update fields and create the auxiliary
   139  // transaction.
   140  // Parameters:
   141  // contract - the bytearray representing the contract in question
   142  // genesisHash - the bytearray representing the network for the txns
   143  //
   144  // Deprecated: Use TealCompile source compilation instead.
   145  func SignDynamicFee(contract []byte, privateKey ed25519.PrivateKey, genesisHash []byte) (txn types.Transaction, lsig types.LogicSig, err error) {
   146  	ints, byteArrays, err := logic.ReadProgram(contract, nil)
   147  	if err != nil {
   148  		return
   149  	}
   150  
   151  	// Convert the byteArrays[0] to receiver
   152  	var receiver types.Address //byteArrays[0]
   153  	n := copy(receiver[:], byteArrays[0])
   154  	if n != ed25519.PublicKeySize {
   155  		err = fmt.Errorf("address generated from receiver bytes is the wrong size")
   156  		return
   157  	}
   158  	// Convert the byteArrays[1] to closeRemainderTo
   159  	var closeRemainderTo types.Address
   160  	n = copy(closeRemainderTo[:], byteArrays[1])
   161  	if n != ed25519.PublicKeySize {
   162  		err = fmt.Errorf("address generated from closeRemainderTo bytes is the wrong size")
   163  		return
   164  	}
   165  	contractLease := byteArrays[2]
   166  	amount, firstValid, lastValid := ints[2], ints[3], ints[4]
   167  	address := types.Address{}
   168  	copy(address[:], privateKey[ed25519.PublicKeySize:])
   169  
   170  	fee := uint64(0)
   171  	params := types.SuggestedParams{
   172  		Fee:             types.MicroAlgos(fee),
   173  		GenesisID:       "",
   174  		GenesisHash:     genesisHash,
   175  		FirstRoundValid: types.Round(firstValid),
   176  		LastRoundValid:  types.Round(lastValid),
   177  		FlatFee:         false,
   178  	}
   179  
   180  	txn, err = future.MakePaymentTxn(address.String(), receiver.String(), amount, nil, closeRemainderTo.String(), params)
   181  	if err != nil {
   182  		return
   183  	}
   184  	lease := [32]byte{}
   185  	copy(lease[:], contractLease) // convert from []byte to [32]byte
   186  	txn.AddLease(lease, fee)
   187  	lsig, err = crypto.MakeLogicSig(contract, nil, privateKey, crypto.MultisigAccount{})
   188  
   189  	return
   190  }