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{}