github.com/algorand/go-algorand-sdk@v1.24.0/templates/split.go (about) 1 package templates 2 3 import ( 4 "encoding/base64" 5 "fmt" 6 "math" 7 8 "golang.org/x/crypto/ed25519" 9 10 "github.com/algorand/go-algorand-sdk/crypto" 11 "github.com/algorand/go-algorand-sdk/future" 12 "github.com/algorand/go-algorand-sdk/logic" 13 "github.com/algorand/go-algorand-sdk/types" 14 ) 15 16 // Split template representation 17 // 18 // Deprecated: Use TealCompile source compilation instead. 19 type Split struct { 20 ContractTemplate 21 rat1 uint64 22 rat2 uint64 23 receiverOne types.Address 24 receiverTwo types.Address 25 } 26 27 //GetSplitFundsTransaction returns a group transaction array which transfer funds according to the contract's ratio 28 // the returned byte array is suitable for passing to SendRawTransaction 29 // contract: the bytecode of the contract to be used 30 // amount: uint64 total number of algos to be transferred (payment1_amount + payment2_amount) 31 // params: is typically received from algod, it defines common-to-all-txns arguments like fee and validity period 32 // 33 // Deprecated: Use TealCompile source compilation instead. 34 func GetSplitFundsTransaction(contract []byte, amount uint64, params types.SuggestedParams) ([]byte, error) { 35 ints, byteArrays, err := logic.ReadProgram(contract, nil) 36 if err != nil { 37 return nil, err 38 } 39 rat1 := ints[6] 40 rat2 := ints[5] 41 // Convert the byteArrays[0] to receiver 42 var receiverOne types.Address //byteArrays[0] 43 n := copy(receiverOne[:], byteArrays[1]) 44 if n != ed25519.PublicKeySize { 45 err = fmt.Errorf("address generated from receiver bytes is the wrong size") 46 return nil, err 47 } 48 // Convert the byteArrays[2] to receiverTwo 49 var receiverTwo types.Address 50 n = copy(receiverTwo[:], byteArrays[2]) 51 if n != ed25519.PublicKeySize { 52 err = fmt.Errorf("address generated from closeRemainderTo bytes is the wrong size") 53 return nil, err 54 } 55 56 ratio := float64(rat2) / float64(rat1) 57 amountForReceiverOneFloat := float64(amount) / (1 + ratio) 58 amountForReceiverOne := uint64(math.Round(amountForReceiverOneFloat)) 59 amountForReceiverTwo := amount - amountForReceiverOne 60 if rat2*amountForReceiverOne != rat1*amountForReceiverTwo { 61 err = fmt.Errorf("could not split funds in a way that satisfied the contract ratio (%d * %d != %d * %d)", rat2, amountForReceiverOne, rat1, amountForReceiverTwo) 62 return nil, err 63 } 64 65 from := crypto.AddressFromProgram(contract) 66 tx1, err := future.MakePaymentTxn(from.String(), receiverOne.String(), amountForReceiverOne, nil, "", params) 67 if err != nil { 68 return nil, err 69 } 70 tx2, err := future.MakePaymentTxn(from.String(), receiverTwo.String(), amountForReceiverTwo, nil, "", params) 71 if err != nil { 72 return nil, err 73 } 74 gid, err := crypto.ComputeGroupID([]types.Transaction{tx1, tx2}) 75 if err != nil { 76 return nil, err 77 } 78 tx1.Group = gid 79 tx2.Group = gid 80 81 logicSig, err := crypto.MakeLogicSig(contract, nil, nil, crypto.MultisigAccount{}) 82 if err != nil { 83 return nil, err 84 } 85 _, stx1, err := crypto.SignLogicsigTransaction(logicSig, tx1) 86 if err != nil { 87 return nil, err 88 } 89 _, stx2, err := crypto.SignLogicsigTransaction(logicSig, tx2) 90 if err != nil { 91 return nil, err 92 } 93 94 var signedGroup []byte 95 signedGroup = append(signedGroup, stx1...) 96 signedGroup = append(signedGroup, stx2...) 97 98 return signedGroup, err 99 } 100 101 // MakeSplit splits money sent to some account to two recipients at some ratio. 102 // This is a contract account. 103 // 104 // This allows either a two-transaction group, for executing a 105 // split, or single transaction, for closing the account. 106 // 107 // Withdrawals from this account are allowed as a group transaction which 108 // sends receiverOne and receiverTwo amounts with exactly the ratio of 109 // rat1/rat2. At least minPay must be sent to receiverOne. 110 // (CloseRemainderTo must be zero.) 111 // 112 // After expiryRound passes, all funds can be refunded to owner. 113 // 114 // Split ratio: 115 // firstRecipient_amount * rat2 == secondRecipient_amount * rat1 116 // or phrased another way 117 // firstRecipient_amount == secondRecipient_amount * (rat1/rat2) 118 // 119 // Parameters: 120 // - owner: the address to refund funds to on timeout 121 // - receiverOne: the first recipient in the split account 122 // - receiverTwo: the second recipient in the split account 123 // - rat1: fraction determines resource split ratio 124 // - rat2: fraction determines resource split ratio 125 // - expiryRound: the round at which the account expires 126 // - minPay: minimum amount to be paid out of the account to receiverOne 127 // - maxFee: half of the maximum fee used by each split forwarding group transaction 128 // 129 // Deprecated: Use TealCompile source compilation instead. 130 func MakeSplit(owner, receiverOne, receiverTwo string, rat1, rat2, expiryRound, minPay, maxFee uint64) (Split, error) { 131 const referenceProgram = "ASAIAQUCAAYHCAkmAyCztwQn0+DycN+vsk+vJWcsoz/b7NDS6i33HOkvTpf+YiC3qUpIgHGWE8/1LPh9SGCalSN7IaITeeWSXbfsS5wsXyC4kBQ38Z8zcwWVAym4S8vpFB/c0XC6R4mnPi9EBADsPDEQIhIxASMMEDIEJBJAABkxCSgSMQcyAxIQMQglEhAxAiEEDRAiQAAuMwAAMwEAEjEJMgMSEDMABykSEDMBByoSEDMACCEFCzMBCCEGCxIQMwAIIQcPEBA=" 132 referenceAsBytes, err := base64.StdEncoding.DecodeString(referenceProgram) 133 if err != nil { 134 return Split{}, err 135 } 136 var referenceOffsets = []uint64{ /*fee*/ 4 /*timeout*/, 7 /*rat2*/, 8 /*rat1*/, 9 /*minPay*/, 10 /*owner*/, 14 /*receiver1*/, 47 /*receiver2*/, 80} 137 ownerAddr, err := types.DecodeAddress(owner) 138 if err != nil { 139 return Split{}, err 140 } 141 receiverOneAddr, err := types.DecodeAddress(receiverOne) 142 if err != nil { 143 return Split{}, err 144 } 145 receiverTwoAddr, err := types.DecodeAddress(receiverTwo) 146 if err != nil { 147 return Split{}, err 148 } 149 injectionVector := []interface{}{maxFee, expiryRound, rat2, rat1, minPay, ownerAddr, receiverOneAddr, receiverTwoAddr} 150 injectedBytes, err := inject(referenceAsBytes, referenceOffsets, injectionVector) 151 if err != nil { 152 return Split{}, err 153 } 154 155 address := crypto.AddressFromProgram(injectedBytes) 156 split := Split{ 157 ContractTemplate: ContractTemplate{ 158 address: address.String(), 159 program: injectedBytes, 160 }, 161 rat1: rat1, 162 rat2: rat2, 163 receiverOne: receiverOneAddr, 164 receiverTwo: receiverTwoAddr, 165 } 166 return split, err 167 }