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 }