mirror of
https://github.com/XRPLF/xrpl-dev-portal.git
synced 2026-06-04 09:16:40 +00:00
Use Tickets tutorial improvements and additions
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# Tickets
|
||||
# Use Tickets
|
||||
|
||||
Create a Ticket and use it to send a transaction out of the usual Sequence order.
|
||||
Create a batch of Tickets and use one to send a transaction outside the normal Sequence order.
|
||||
|
||||
For more context, see the interactive [Use Tickets tutorial](https://xrpl.org/use-tickets.html).
|
||||
For more context, see the [Use Tickets tutorial](https://xrpl.org/docs/tutorials/best-practices/transaction-sending/use-tickets).
|
||||
|
||||
17
_code-samples/use-tickets/go/README.md
Normal file
17
_code-samples/use-tickets/go/README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Use Tickets (Go)
|
||||
|
||||
Demonstrates how to use Tickets for out-of-order transaction submission on the XRP Ledger.
|
||||
|
||||
For more context, see the [Use Tickets tutorial](https://xrpl.org/docs/tutorials/best-practices/transaction-sending/use-tickets).
|
||||
|
||||
## Setup
|
||||
|
||||
```sh
|
||||
go mod download
|
||||
```
|
||||
|
||||
## Basic Ticket Usage
|
||||
|
||||
```sh
|
||||
go run main.go
|
||||
```
|
||||
@@ -1,21 +1,18 @@
|
||||
module github.com/XRPLF
|
||||
|
||||
go 1.24.0
|
||||
go 1.24.3
|
||||
|
||||
require github.com/Peersyst/xrpl-go v0.1.11
|
||||
require github.com/Peersyst/xrpl-go v0.1.18
|
||||
|
||||
require (
|
||||
github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e // indirect
|
||||
github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec // indirect
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
|
||||
github.com/bsv-blockchain/go-sdk v1.2.9 // indirect
|
||||
github.com/decred/dcrd/crypto/ripemd160 v1.0.2 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/tyler-smith/go-bip32 v1.0.0 // indirect
|
||||
github.com/tyler-smith/go-bip39 v1.1.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
)
|
||||
|
||||
@@ -1,22 +1,16 @@
|
||||
github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e h1:ahyvB3q25YnZWly5Gq1ekg6jcmWaGj/vG/MhF4aisoc=
|
||||
github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:kGUqhHd//musdITWjFvNTHn90WG9bMLBEPQZ17Cmlpw=
|
||||
github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec h1:1Qb69mGp/UtRPn422BH4/Y4Q3SLUrD9KHuDkm8iodFc=
|
||||
github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec/go.mod h1:CD8UlnlLDiqb36L110uqiP2iSflVjx9g/3U9hCI4q2U=
|
||||
github.com/Peersyst/xrpl-go v0.1.11 h1:P6r/gHxRnbAtAdPmzNHz/7zpsdfvwh0SS+QI2JNT44w=
|
||||
github.com/Peersyst/xrpl-go v0.1.11/go.mod h1:CBRM3/soqNeeL2Jx6USVUtECqulZVUoq3UxZKMz9hdw=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
|
||||
github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e h1:0XBUw73chJ1VYSsfvcPvVT7auykAJce9FpRr10L6Qhw=
|
||||
github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:P13beTBKr5Q18lJe1rIoLUqjM+CB1zYrRg44ZqGuQSA=
|
||||
github.com/Peersyst/xrpl-go v0.1.18 h1:PUIEGvnkO9oQ4yZRr6EHjglay1xmrjFhujQNVkXynqs=
|
||||
github.com/Peersyst/xrpl-go v0.1.18/go.mod h1:38j60Mr65poIHdhmjvNXnwbcUFNo8J7CBDot7ZWgrb8=
|
||||
github.com/bsv-blockchain/go-sdk v1.2.9 h1:LwFzuts+J5X7A+ECx0LNowtUgIahCkNNlXckdiEMSDk=
|
||||
github.com/bsv-blockchain/go-sdk v1.2.9/go.mod h1:KiHWa/hblo3Bzr+IsX11v0sn1E6elGbNX0VXl5mOq6E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
||||
github.com/decred/dcrd/crypto/ripemd160 v1.0.2 h1:TvGTmUBHDU75OHro9ojPLK+Yv7gDl2hnUvRocRCjsys=
|
||||
github.com/decred/dcrd/crypto/ripemd160 v1.0.2/go.mod h1:uGfjDyePSpa75cSQLzNdVmWlbQMBuiJkvXw/MNKRY4M=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 h1:5RVFMOWjMyRy8cARdy79nAmgYw3hK/4HUq48LQ6Wwqo=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
@@ -28,29 +22,17 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OH
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.1.5-0.20170601210322-f6abca593680/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/tyler-smith/go-bip32 v1.0.0 h1:sDR9juArbUgX+bO/iblgZnMPeWY1KZMUC2AFUJdv5KE=
|
||||
github.com/tyler-smith/go-bip32 v1.0.0/go.mod h1:onot+eHknzV4BVPwrzqY5OoVpyCvnwD7lMawL5aQupE=
|
||||
github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8=
|
||||
github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
golang.org/x/crypto v0.0.0-20170613210332-850760c427c5/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
launchpad.net/gocheck v0.0.0-20140225173054-000000000087 h1:Izowp2XBH6Ya6rv+hqbceQyw/gSGoXfH/UPoTGduL54=
|
||||
launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM=
|
||||
|
||||
@@ -8,116 +8,83 @@ import (
|
||||
"github.com/Peersyst/xrpl-go/xrpl/faucet"
|
||||
"github.com/Peersyst/xrpl-go/xrpl/queries/account"
|
||||
"github.com/Peersyst/xrpl-go/xrpl/rpc"
|
||||
rpctypes "github.com/Peersyst/xrpl-go/xrpl/rpc/types"
|
||||
"github.com/Peersyst/xrpl-go/xrpl/transaction"
|
||||
"github.com/Peersyst/xrpl-go/xrpl/wallet"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cfg, err := rpc.NewClientConfig(
|
||||
"https://s.altnet.rippletest.net:51234/",
|
||||
rpc.WithFaucetProvider(faucet.NewTestnetFaucetProvider()),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Set up client and account
|
||||
cfg, err := rpc.NewClientConfig(
|
||||
"https://s.altnet.rippletest.net:51234/",
|
||||
rpc.WithFaucetProvider(faucet.NewTestnetFaucetProvider()),
|
||||
rpc.WithMaxRetries(30),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
client := rpc.NewClient(cfg)
|
||||
|
||||
client := rpc.NewClient(cfg)
|
||||
// Fund wallet
|
||||
w, err := wallet.New(crypto.ED25519())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("Funding wallet...")
|
||||
if err := client.FundWallet(&w); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("Wallet funded")
|
||||
|
||||
w, err := wallet.New(crypto.ED25519())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Create Tickets
|
||||
ticketCreate := &transaction.TicketCreate{
|
||||
BaseTx: transaction.BaseTx{
|
||||
Account: w.GetAddress(),
|
||||
},
|
||||
TicketCount: 10,
|
||||
}
|
||||
fmt.Println("Submitting TicketCreate transaction...")
|
||||
tcResult, err := client.SubmitTxAndWait(ticketCreate.Flatten(), &rpctypes.SubmitOptions{
|
||||
Autofill: true,
|
||||
Wallet: &w,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("TicketCreate hash: %s, validated: %t\n", tcResult.Hash, tcResult.Validated)
|
||||
|
||||
fmt.Println("Funding wallet...")
|
||||
if err := client.FundWallet(&w); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Check Available Tickets
|
||||
objects, err := client.GetAccountObjects(&account.ObjectsRequest{
|
||||
Account: w.GetAddress(),
|
||||
Type: account.TicketObject,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("Found %d Tickets\n", len(objects.AccountObjects))
|
||||
|
||||
fmt.Println("Wallet funded")
|
||||
fmt.Println()
|
||||
// Choose an arbitrary Ticket to use
|
||||
useTicket, err := objects.AccountObjects[0]["TicketSequence"].(json.Number).Int64()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("Using Ticket Sequence: %d\n", useTicket)
|
||||
|
||||
info, err := client.GetAccountInfo(&account.InfoRequest{
|
||||
Account: w.GetAddress(),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println("Current wallet sequence:", info.AccountData.Sequence)
|
||||
fmt.Println()
|
||||
|
||||
fmt.Println("Submitting TicketCreate transaction...")
|
||||
tc := &transaction.TicketCreate{
|
||||
BaseTx: transaction.BaseTx{
|
||||
Account: w.GetAddress(),
|
||||
Sequence: info.AccountData.Sequence,
|
||||
},
|
||||
TicketCount: 10,
|
||||
}
|
||||
|
||||
flatTc := tc.Flatten()
|
||||
|
||||
if err := client.Autofill(&flatTc); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
blob, _, err := w.Sign(flatTc)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
res, err := client.SubmitTxBlobAndWait(blob, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println("TicketCreate transaction submitted")
|
||||
fmt.Printf("Hash: %s\n", res.Hash)
|
||||
fmt.Printf("Validated: %t\n", res.Validated)
|
||||
fmt.Println()
|
||||
|
||||
objects, err := client.GetAccountObjects(&account.ObjectsRequest{
|
||||
Account: w.GetAddress(),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println("Account objects:", objects.AccountObjects[0]["TicketSequence"])
|
||||
|
||||
seq, err := objects.AccountObjects[0]["TicketSequence"].(json.Number).Int64()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println("Submitting AccountSet transaction...")
|
||||
as := &transaction.AccountSet{
|
||||
BaseTx: transaction.BaseTx{
|
||||
Account: w.GetAddress(),
|
||||
Sequence: 0,
|
||||
TicketSequence: uint32(seq),
|
||||
},
|
||||
}
|
||||
|
||||
flatAs := as.Flatten()
|
||||
|
||||
if err := client.Autofill(&flatAs); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
flatAs["Sequence"] = uint32(0)
|
||||
|
||||
blob, _, err = w.Sign(flatAs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
res, err = client.SubmitTxBlobAndWait(blob, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println("AccountSet transaction submitted")
|
||||
fmt.Printf("Hash: %s\n", res.Hash)
|
||||
fmt.Printf("Validated: %t\n", res.Validated)
|
||||
// Use a Ticket
|
||||
ticketedTx := &transaction.AccountSet{
|
||||
BaseTx: transaction.BaseTx{
|
||||
Account: w.GetAddress(),
|
||||
Sequence: 0,
|
||||
TicketSequence: uint32(useTicket),
|
||||
},
|
||||
}
|
||||
fmt.Println("Submitting ticketed AccountSet transaction...")
|
||||
ticketedResult, err := client.SubmitTxAndWait(ticketedTx.Flatten(), &rpctypes.SubmitOptions{
|
||||
Autofill: true,
|
||||
Wallet: &w,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("Ticketed AccountSet hash: %s, validated: %t\n", ticketedResult.Hash, ticketedResult.Validated)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
Demonstrates how to use Tickets for out-of-order transaction submission on the XRP Ledger.
|
||||
|
||||
For more context, see the [Use Tickets tutorial](https://xrpl.org/docs/tutorials/best-practices/transaction-sending/use-tickets).
|
||||
|
||||
## Setup
|
||||
|
||||
```sh
|
||||
@@ -10,12 +12,6 @@ npm install
|
||||
|
||||
## Basic Ticket Usage
|
||||
|
||||
### Browser
|
||||
|
||||
Open `demo.html` in a web browser and check the browser console to see the logs.
|
||||
|
||||
Alternatively, you can run the code from the command line:
|
||||
|
||||
```sh
|
||||
node use-tickets.js
|
||||
```
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Code Sample - Use Tickets</title>
|
||||
<script src="https://unpkg.com/xrpl@4.0.0/build/xrpl-latest.js"></script>
|
||||
<script type="application/javascript" src="use-tickets.js"></script>
|
||||
</head>
|
||||
<body>Open your browser's console (F12) to see the logs.</body>
|
||||
</html>
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"xrpl": "^4.0.0"
|
||||
}
|
||||
|
||||
@@ -1,129 +1,74 @@
|
||||
if (typeof module !== "undefined") {
|
||||
// Use var here because const/let are block-scoped to the if statement.
|
||||
var xrpl = require('xrpl')
|
||||
}
|
||||
// List which Tickets are outstanding against one’s own account and use Tickets to collect signatures for multisign transactions
|
||||
// https://xrpl.org/use-tickets.html
|
||||
// https://xrpl.org/signerlistset.html#signerlistset
|
||||
// https://xrpl.org/multi-signing.html#multi-signing
|
||||
import xrpl from 'xrpl'
|
||||
|
||||
async function main() {
|
||||
// Connect to a testnet node
|
||||
console.log("Connecting to Testnet...")
|
||||
const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233')
|
||||
await client.connect()
|
||||
|
||||
// Get account credentials from the Testnet Faucet
|
||||
console.log("Requesting account credentials from the Testnet faucet, this may take awhile...")
|
||||
const { wallet: main_wallet } = await client.fundWallet()
|
||||
// Use Tickets with multi-signing: each Ticket holds a Sequence slot for a
|
||||
// transaction while you collect signatures from multiple signers.
|
||||
|
||||
// Signer keys don't need to be funded on the ledger, it only needs to be cryptographically valid
|
||||
// Thus, we could generate keys and set them as signers without the need to fund their accounts
|
||||
// But we'll still fund them for testing purposes...
|
||||
const { wallet: wallet_1 } = await client.fundWallet()
|
||||
const { wallet: wallet_2 } = await client.fundWallet()
|
||||
const { wallet: wallet_3 } = await client.fundWallet()
|
||||
const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233')
|
||||
await client.connect()
|
||||
|
||||
console.log(" Main Account: ", main_wallet.address)
|
||||
console.log(" Seed: ", main_wallet.seed)
|
||||
// Set up the main account and three signers ------------------------------
|
||||
// Signer accounts only need cryptographically valid keys; the addresses
|
||||
// don't need to be funded or exist on the ledger.
|
||||
console.log('Funding main account from the faucet...')
|
||||
const { wallet: mainWallet } = await client.fundWallet()
|
||||
const signer1 = xrpl.Wallet.generate()
|
||||
const signer2 = xrpl.Wallet.generate()
|
||||
const signer3 = xrpl.Wallet.generate()
|
||||
console.log(`Main account: ${mainWallet.address}`)
|
||||
|
||||
console.log("\n Signer 1: ", wallet_1.address)
|
||||
console.log(" Signer 2: ", wallet_2.address)
|
||||
console.log(" Signer 3: ", wallet_3.address)
|
||||
// Configure the signer list (quorum 2 of 3) -------------------------------
|
||||
console.log('Submitting SignerListSet...')
|
||||
const signerListResult = await client.submitAndWait({
|
||||
TransactionType: 'SignerListSet',
|
||||
Account: mainWallet.address,
|
||||
SignerQuorum: 2,
|
||||
SignerEntries: [
|
||||
{ SignerEntry: { Account: signer1.address, SignerWeight: 1 } },
|
||||
{ SignerEntry: { Account: signer2.address, SignerWeight: 1 } },
|
||||
{ SignerEntry: { Account: signer3.address, SignerWeight: 1 } }
|
||||
]
|
||||
}, { wallet: mainWallet, autofill: true })
|
||||
console.log(`SignerListSet hash: ${signerListResult.result.hash}`)
|
||||
|
||||
// Send SignerListSet transaction
|
||||
// Since each signer is given a signer weight of 1 and there are 3 signers, the maximum quorom would be 3
|
||||
// SignerQuorom is a target number for the signer weights
|
||||
// A multisig from this list is valid only if the sum weights of the signatures provided is greater than or equal to the SignerQuorom
|
||||
const signerLiSetSignerList_tx = {
|
||||
TransactionType: "SignerListSet",
|
||||
Account: main_wallet.classicAddress,
|
||||
SignerEntries: [
|
||||
{
|
||||
SignerEntry: {
|
||||
Account: wallet_1.classicAddress,
|
||||
SignerWeight: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
SignerEntry: {
|
||||
Account: wallet_2.classicAddress,
|
||||
SignerWeight: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
SignerEntry: {
|
||||
Account: wallet_3.classicAddress,
|
||||
SignerWeight: 1,
|
||||
},
|
||||
}
|
||||
],
|
||||
SignerQuorum: 2,
|
||||
}
|
||||
// Create Tickets ----------------------------------------------------------
|
||||
console.log('Submitting TicketCreate (3 Tickets)...')
|
||||
const ticketCreateResult = await client.submitAndWait({
|
||||
TransactionType: 'TicketCreate',
|
||||
Account: mainWallet.address,
|
||||
TicketCount: 3
|
||||
}, { wallet: mainWallet, autofill: true })
|
||||
console.log(`TicketCreate hash: ${ticketCreateResult.result.hash}`)
|
||||
|
||||
const signerLiSetSignerList_tx_prepared = await client.autofill(signerLiSetSignerList_tx)
|
||||
const SetSignerList_tx_signed = main_wallet.sign(signerLiSetSignerList_tx_prepared)
|
||||
console.log(`\n SignerListSet Tx hash: ${SetSignerList_tx_signed.hash}`)
|
||||
|
||||
const setsignerlist_submit = await client.submitAndWait(SetSignerList_tx_signed.tx_blob)
|
||||
console.log(`\t Submit result: ${setsignerlist_submit.result.meta.TransactionResult}`)
|
||||
|
||||
const CreateTicket_tx = await client.autofill({
|
||||
TransactionType: "TicketCreate",
|
||||
Account: main_wallet.address,
|
||||
TicketCount: 3
|
||||
})
|
||||
|
||||
const CreateTicket_tx_signed = main_wallet.sign(CreateTicket_tx)
|
||||
console.log("\n CreateTicket Tx hash:", CreateTicket_tx_signed.hash)
|
||||
|
||||
const ticket_submit = await client.submitAndWait(CreateTicket_tx_signed.tx_blob)
|
||||
console.log(" Submit result:", ticket_submit.result.meta.TransactionResult)
|
||||
|
||||
const ticket_response = await client.request({
|
||||
command: "account_objects",
|
||||
account: main_wallet.address,
|
||||
ledger_index: "validated",
|
||||
type: "ticket"
|
||||
})
|
||||
// Pick a Ticket -----------------------------------------------------------
|
||||
const ticketsResponse = await client.request({
|
||||
command: 'account_objects',
|
||||
account: mainWallet.address,
|
||||
type: 'ticket'
|
||||
})
|
||||
const useTicket = ticketsResponse.result.account_objects[0].TicketSequence
|
||||
console.log(`Using Ticket Sequence: ${useTicket}`)
|
||||
|
||||
console.log(`\n- Tickets issued by ${main_wallet.address}:\n`)
|
||||
for (let i = 0; i < ticket_response.result.account_objects.length; i++) {
|
||||
console.log(`Ticket ${i+1}: ${ticket_response.result.account_objects[i].TicketSequence}`)
|
||||
}
|
||||
// Prepare a multi-signed transaction that consumes the Ticket.
|
||||
// Omit LastLedgerSequence so the transaction does not expire while
|
||||
// signatures are being collected.
|
||||
const preparedTx = await client.autofill({
|
||||
TransactionType: 'AccountSet',
|
||||
Account: mainWallet.address,
|
||||
TicketSequence: useTicket,
|
||||
LastLedgerSequence: null,
|
||||
Sequence: 0
|
||||
}, 3) // signersCount for multi-sig fee calculation
|
||||
|
||||
// We'll use this ticket on our tx
|
||||
const ticket_1 = ticket_response.result.account_objects[0].TicketSequence
|
||||
|
||||
console.log(`\n Ticket sequence ${ticket_1} will be used for our multi-sig transaction`)
|
||||
|
||||
const Payment_tx = {
|
||||
"TransactionType": "AccountSet",
|
||||
"Account": main_wallet.address,
|
||||
"TicketSequence": ticket_1,
|
||||
"LastLedgerSequence": null,
|
||||
"Sequence": 0
|
||||
}
|
||||
// In a real workflow you would share preparedTx with each signer and
|
||||
// receive their signed blobs back; here we sign in one process for clarity.
|
||||
const { tx_blob: signedBlob1 } = signer1.sign(preparedTx, true)
|
||||
const { tx_blob: signedBlob2 } = signer2.sign(preparedTx, true)
|
||||
const { tx_blob: signedBlob3 } = signer3.sign(preparedTx, true)
|
||||
|
||||
const Payment_tx_prepared = await client.autofill(Payment_tx, signersCount=3)
|
||||
|
||||
// Each signer will sign the prepared tx (AccountSet_tx) and their signatures will be combines into 1 multi-sig transaction
|
||||
const { tx_blob: Payment_tx_signed_1 } = wallet_1.sign(Payment_tx_prepared, multisign=true)
|
||||
const { tx_blob: Payment_tx_signed_2 } = wallet_2.sign(Payment_tx_prepared, multisign=true)
|
||||
const { tx_blob: Payment_tx_signed_3 } = wallet_3.sign(Payment_tx_prepared, multisign=true)
|
||||
|
||||
console.log("\n All signers have signed the transaction with their corresponding keys")
|
||||
const multisignedBlob = xrpl.multisign([signedBlob1, signedBlob2, signedBlob3])
|
||||
console.log('Submitting multi-signed AccountSet...')
|
||||
const multisigResult = await client.submitAndWait(multisignedBlob)
|
||||
console.log(`Multi-sig hash: ${multisigResult.result.hash}, result: ${multisigResult.result.meta.TransactionResult}`)
|
||||
|
||||
// Combine 3 of the signers' signatures to form a multi-sig transaction
|
||||
const multisignedTx = xrpl.multisign([Payment_tx_signed_1, Payment_tx_signed_2, Payment_tx_signed_3])
|
||||
|
||||
const multisig_submit = await client.submitAndWait(multisignedTx)
|
||||
console.log("\n Multi-sig Submit result:", multisig_submit.result.meta.TransactionResult)
|
||||
console.log("\n Multi-sig Tx Binary:", multisignedTx)
|
||||
|
||||
client.disconnect()
|
||||
|
||||
// End main()
|
||||
}
|
||||
|
||||
main()
|
||||
// Disconnect when done (If you omit this, Node.js won't end the process)
|
||||
await client.disconnect()
|
||||
|
||||
@@ -1,76 +1,44 @@
|
||||
// Dependencies for Node.js.
|
||||
// In browsers, use a <script> tag instead.
|
||||
if (typeof module !== "undefined") {
|
||||
// Use var here because const/let are block-scoped to the if statement.
|
||||
var xrpl = require('xrpl')
|
||||
}
|
||||
import xrpl from 'xrpl'
|
||||
|
||||
// Example credentials
|
||||
const wallet = xrpl.Wallet.fromSeed("sn3nxiW7v8KXzPzAqzyHXbSSKNuN9")
|
||||
// Set up client and account
|
||||
const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233')
|
||||
await client.connect()
|
||||
|
||||
// Connect to Devnet (since that's where tickets are available)
|
||||
async function main() {
|
||||
const client = new xrpl.Client("wss://s.devnet.rippletest.net:51233")
|
||||
await client.connect()
|
||||
// Fund wallet --------------------------------------------------------------
|
||||
console.log('Getting a wallet from the faucet...')
|
||||
const { wallet } = await client.fundWallet()
|
||||
|
||||
// Get credentials from the Testnet Faucet -----------------------------------
|
||||
console.log("Getting a wallet from the faucet...")
|
||||
const {wallet, balance} = await client.fundWallet()
|
||||
// Create Tickets -----------------------------------------------------------
|
||||
console.log('Submitting TicketCreate transaction...')
|
||||
const ticketCreateResult = await client.submitAndWait({
|
||||
TransactionType: 'TicketCreate',
|
||||
Account: wallet.address,
|
||||
TicketCount: 10
|
||||
}, { wallet, autofill: true })
|
||||
console.log(`TicketCreate hash: ${ticketCreateResult.result.hash}, validated: ${ticketCreateResult.result.validated}`)
|
||||
|
||||
// Check Sequence Number -----------------------------------------------------
|
||||
const account_info = await client.request({
|
||||
"command": "account_info",
|
||||
"account": wallet.address
|
||||
})
|
||||
let current_sequence = account_info.result.account_data.Sequence
|
||||
// Check Available Tickets --------------------------------------------------
|
||||
const ticketsResponse = await client.request({
|
||||
command: 'account_objects',
|
||||
account: wallet.address,
|
||||
type: 'ticket'
|
||||
})
|
||||
const tickets = ticketsResponse.result.account_objects
|
||||
console.log(`Found ${tickets.length} Tickets`)
|
||||
|
||||
// Prepare and Sign TicketCreate ---------------------------------------------
|
||||
const prepared = await client.autofill({
|
||||
"TransactionType": "TicketCreate",
|
||||
"Account": wallet.address,
|
||||
"TicketCount": 10,
|
||||
"Sequence": current_sequence
|
||||
})
|
||||
const signed = wallet.sign(prepared)
|
||||
console.log(`Prepared TicketCreate transaction ${signed.hash}`)
|
||||
// Choose an arbitrary Ticket to use
|
||||
const useTicket = tickets[0].TicketSequence
|
||||
console.log(`Using Ticket Sequence: ${useTicket}`)
|
||||
|
||||
// Submit TicketCreate -------------------------------------------------------
|
||||
const tx = await client.submitAndWait(signed.tx_blob)
|
||||
console.log(tx)
|
||||
// Use a Ticket -------------------------------------------------------------
|
||||
console.log('Submitting ticketed AccountSet transaction...')
|
||||
const ticketedResult = await client.submitAndWait({
|
||||
TransactionType: 'AccountSet',
|
||||
Account: wallet.address,
|
||||
TicketSequence: useTicket,
|
||||
Sequence: 0
|
||||
}, { wallet, autofill: true })
|
||||
console.log(`Ticketed AccountSet hash: ${ticketedResult.result.hash}, validated: ${ticketedResult.result.validated}`)
|
||||
|
||||
// Wait for Validation -------------------------------------------------------
|
||||
// submitAndWait() handles this automatically, but it can take 4-7s.
|
||||
|
||||
// Check Available Tickets ---------------------------------------------------
|
||||
let response = await client.request({
|
||||
"command": "account_objects",
|
||||
"account": wallet.address,
|
||||
"type": "ticket"
|
||||
})
|
||||
console.log("Available Tickets:", response.result.account_objects)
|
||||
|
||||
// Choose an arbitrary Ticket to use
|
||||
use_ticket = response.result.account_objects[0].TicketSequence
|
||||
|
||||
// Prepare and Sign Ticketed Transaction -------------------------------------
|
||||
const prepared_t = await client.autofill({
|
||||
"TransactionType": "AccountSet",
|
||||
"Account": wallet.address,
|
||||
"TicketSequence": use_ticket,
|
||||
"LastLedgerSequence": null, // Never expire this transaction.
|
||||
"Sequence": 0
|
||||
})
|
||||
const signed_t = wallet.sign(prepared_t)
|
||||
console.log(`Prepared ticketed transaction ${signed_t.hash}`)
|
||||
|
||||
// Submit Ticketed Transaction -----------------------------------------------
|
||||
const tx_t = await client.submitAndWait(signed_t.tx_blob)
|
||||
console.log(tx_t)
|
||||
|
||||
// Wait for Validation (again) -----------------------------------------------
|
||||
|
||||
// Disconnect when done (If you omit this, Node.js won't end the process)
|
||||
await client.disconnect()
|
||||
}
|
||||
|
||||
main()
|
||||
// Disconnect when done (If you omit this, Node.js won't end the process)
|
||||
await client.disconnect()
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
Demonstrates how to use Tickets for out-of-order transaction submission on the XRP Ledger.
|
||||
|
||||
For more context, see the [Use Tickets tutorial](https://xrpl.org/docs/tutorials/best-practices/transaction-sending/use-tickets).
|
||||
|
||||
## Setup
|
||||
|
||||
```sh
|
||||
|
||||
@@ -1,136 +1,72 @@
|
||||
from xrpl.models.transactions import TicketCreate, AccountSet, SignerListSet, SignerEntry
|
||||
from xrpl.transaction import sign_and_submit, autofill, multisign, sign
|
||||
from xrpl.models.requests.account_objects import AccountObjects, AccountObjectType
|
||||
from xrpl.models.requests.submit_multisigned import SubmitMultisigned
|
||||
from xrpl.wallet import generate_faucet_wallet
|
||||
from xrpl.clients import JsonRpcClient
|
||||
from xrpl.models.requests import AccountObjects, AccountObjectType
|
||||
from xrpl.models.transactions import AccountSet, SignerEntry, SignerListSet, TicketCreate
|
||||
from xrpl.transaction import autofill, multisign, sign, submit_and_wait
|
||||
from xrpl.wallet import Wallet, generate_faucet_wallet
|
||||
|
||||
# This code sample shows how to use tickets with multisigning
|
||||
# to allow you to do a series of transactions in any order while you wait
|
||||
# for signers to come back with their signatures.
|
||||
# https://xrpl.org/tickets.html#tickets
|
||||
# https://xrpl.org/use-tickets.html
|
||||
# https://xrpl.org/set-up-multi-signing.html#set-up-multi-signing
|
||||
# https://xrpl.org/send-a-multi-signed-transaction.html#send-a-multi-signed-transaction
|
||||
# Use Tickets with multi-signing: each Ticket holds a Sequence slot for a
|
||||
# transaction while you collect signatures from multiple signers.
|
||||
|
||||
# Note that this will only work with xrpl-py version v2.0.0-beta.0 or later
|
||||
|
||||
# Connect to a testnet node
|
||||
JSON_RPC_URL = "https://s.altnet.rippletest.net:51234/"
|
||||
client = JsonRpcClient(JSON_RPC_URL)
|
||||
|
||||
# Generate a wallet and request faucet
|
||||
test_wallet = generate_faucet_wallet(client=client)
|
||||
myAddr = test_wallet.address
|
||||
# Set up the main account and three signers.
|
||||
# Signer accounts only need cryptographically valid keys; the addresses
|
||||
# don't need to be funded or exist on the ledger.
|
||||
print("Funding main account from the faucet...")
|
||||
main_wallet = generate_faucet_wallet(client=client)
|
||||
signer_1 = Wallet.create()
|
||||
signer_2 = Wallet.create()
|
||||
signer_3 = Wallet.create()
|
||||
print(f"Main account: {main_wallet.address}")
|
||||
|
||||
print("Setting up all the signers' accounts via the testnet faucet, this may take a while...")
|
||||
signer_1_wallet = generate_faucet_wallet(client=client)
|
||||
signer_2_wallet = generate_faucet_wallet(client=client)
|
||||
signer_3_wallet = generate_faucet_wallet(client=client)
|
||||
|
||||
# Set the list of accounts that are able to authorize transactions on behalf of our Account via a multi-sig transaction
|
||||
signers = [
|
||||
SignerEntry(account=signer_1_wallet.address, signer_weight=1),
|
||||
SignerEntry(account=signer_2_wallet.address, signer_weight=1),
|
||||
SignerEntry(account=signer_3_wallet.address, signer_weight=1)
|
||||
]
|
||||
|
||||
# Display all the signers' account address
|
||||
print("\nSigners:")
|
||||
signer_int = 1
|
||||
for signer in signers:
|
||||
print(f"{signer_int}. {signer}")
|
||||
signer_int += 1
|
||||
|
||||
# A multi-sig transaction is achievable by trusting a list of keys to authorize transactions on behalf of the account
|
||||
# Construct a SignerList transaction
|
||||
tx_set_signer_list = SignerListSet(
|
||||
account=myAddr,
|
||||
signer_quorum=3,
|
||||
signer_entries=signers
|
||||
# Configure the signer list (quorum 2 of 3)
|
||||
print("Submitting SignerListSet...")
|
||||
signer_list_set = SignerListSet(
|
||||
account=main_wallet.address,
|
||||
signer_quorum=2,
|
||||
signer_entries=[
|
||||
SignerEntry(account=signer_1.address, signer_weight=1),
|
||||
SignerEntry(account=signer_2.address, signer_weight=1),
|
||||
SignerEntry(account=signer_3.address, signer_weight=1),
|
||||
],
|
||||
)
|
||||
signer_list_result = submit_and_wait(signer_list_set, client, main_wallet)
|
||||
print(f"SignerListSet hash: {signer_list_result.result['hash']}")
|
||||
|
||||
# We'll set the signer_quorum to 3, each key will represent 1 signer weight
|
||||
# if 3 of the signers sign a particular transaction, it's an authorized transaction on the XRPL
|
||||
# Create Tickets
|
||||
print("Submitting TicketCreate (3 Tickets)...")
|
||||
ticket_create = TicketCreate(account=main_wallet.address, ticket_count=3)
|
||||
ticket_create_result = submit_and_wait(ticket_create, client, main_wallet)
|
||||
print(f"TicketCreate hash: {ticket_create_result.result['hash']}")
|
||||
|
||||
# Sign transaction locally and submit
|
||||
print("Submitting a SignerListSet transaction to update our account to use our new Signers...")
|
||||
tx_set_signer_list_signed = sign_and_submit(transaction=tx_set_signer_list, client=client, wallet=test_wallet)
|
||||
|
||||
# Construct a TicketCreate transaction, 3 tickets will be created
|
||||
tx_create_ticket = TicketCreate(
|
||||
account=myAddr,
|
||||
ticket_count=len(signers)
|
||||
)
|
||||
|
||||
# Sign transaction locally and submit
|
||||
print("Submitting a TicketCreate transaction to get Ticket Sequences for future transactions...")
|
||||
tx_create_ticket_signed = sign_and_submit(transaction=tx_create_ticket, client=client, wallet=test_wallet)
|
||||
|
||||
# Get a Ticket Sequence
|
||||
get_ticket_sequence = client.request(AccountObjects(
|
||||
account=myAddr,
|
||||
type=AccountObjectType.TICKET
|
||||
# Pick a Ticket
|
||||
tickets_response = client.request(AccountObjects(
|
||||
account=main_wallet.address,
|
||||
type=AccountObjectType.TICKET,
|
||||
))
|
||||
use_ticket = tickets_response.result["account_objects"][0]["TicketSequence"]
|
||||
print(f"Using Ticket Sequence: {use_ticket}")
|
||||
|
||||
# Since we created 3 Tickets previously, you're able to choose which Ticket you're going to use
|
||||
ticket1_sequence = get_ticket_sequence.result["account_objects"][0]["TicketSequence"]
|
||||
ticket2_sequence = get_ticket_sequence.result["account_objects"][1]["TicketSequence"]
|
||||
ticket3_sequence = get_ticket_sequence.result["account_objects"][2]["TicketSequence"]
|
||||
|
||||
print(f"\nTickets issued:\n"
|
||||
f"Ticket 1: {ticket1_sequence}\n"
|
||||
f"Ticket 2: {ticket2_sequence}\n"
|
||||
f"Ticket 3: {ticket3_sequence}")
|
||||
|
||||
ticket_sequence = int(input("\nPick 1 ticket sequence to use for your next multi-sig transaction: "))
|
||||
|
||||
# Construct AccountSet Transaction using a Ticket
|
||||
tx_1 = AccountSet(
|
||||
account=myAddr,
|
||||
fee="1000",
|
||||
# Prepare a multi-signed transaction that consumes the Ticket.
|
||||
# Omit last_ledger_sequence so the transaction doesn't expire while
|
||||
# signatures are being collected.
|
||||
account_set = AccountSet(
|
||||
account=main_wallet.address,
|
||||
ticket_sequence=use_ticket,
|
||||
sequence=0,
|
||||
last_ledger_sequence=None,
|
||||
ticket_sequence=ticket_sequence
|
||||
)
|
||||
prepared_tx = autofill(account_set, client, signers_count=3)
|
||||
|
||||
autofilled_account_set_tx = autofill(tx_1, client, len(signers))
|
||||
# In a real workflow you would share prepared_tx with each signer and
|
||||
# receive their signed transactions back; here we sign in one process for clarity.
|
||||
signed_1 = sign(prepared_tx, signer_1, multisign=True)
|
||||
signed_2 = sign(prepared_tx, signer_2, multisign=True)
|
||||
signed_3 = sign(prepared_tx, signer_3, multisign=True)
|
||||
|
||||
# Sign 'tx_1' using all the keys on signers[]
|
||||
# In a real app, you would share this transaction with key holders for them to sign
|
||||
# but since we own these accounts for test purposes, we can sign directly here.
|
||||
tx_result_1 = sign(transaction=autofilled_account_set_tx, wallet=signer_1_wallet, multisign=True)
|
||||
tx_result_2 = sign(transaction=autofilled_account_set_tx, wallet=signer_2_wallet, multisign=True)
|
||||
tx_result_3 = sign(transaction=autofilled_account_set_tx, wallet=signer_3_wallet, multisign=True)
|
||||
|
||||
print(f"\nTx signature by account 1: {tx_result_1.signers}")
|
||||
print(f"Tx signature by account 2: {tx_result_2.signers}")
|
||||
print(f"Tx signature by account 3: {tx_result_3.signers}")
|
||||
|
||||
# Combine all the signed transactions from the provided signed txs into a multi-sig transaction
|
||||
tx_1_filled = multisign(
|
||||
transaction=autofilled_account_set_tx,
|
||||
tx_list=[
|
||||
tx_result_1,
|
||||
tx_result_2,
|
||||
tx_result_3
|
||||
]
|
||||
)
|
||||
|
||||
response = client.request(SubmitMultisigned(tx_json=tx_1_filled))
|
||||
result = response.result
|
||||
|
||||
print(f"\n Account Set tx result: {result['engine_result']}"
|
||||
f"\n Tx content: {result}\n")
|
||||
|
||||
if result['engine_result'] == "tesSUCCESS":
|
||||
print("Multi-sig transaction using a Ticket was successful!")
|
||||
elif result == "terPRE_TICKET":
|
||||
print(f"The provided Ticket Sequence {ticket_sequence} does not exist in the ledger")
|
||||
elif result == "tefMAX_LEDGER":
|
||||
print("Transaction failed to achieve consensus")
|
||||
elif result == "tefPAST_SEQ":
|
||||
print("The sequence number is lower than the current sequence number of the account")
|
||||
elif result == "unknown":
|
||||
print("Transaction status unknown")
|
||||
print(f"The transaction's engine_result was {result['engine_result']}")
|
||||
multisigned_tx = multisign(prepared_tx, [signed_1, signed_2, signed_3])
|
||||
print("Submitting multi-signed AccountSet...")
|
||||
multisig_result = submit_and_wait(multisigned_tx, client, autofill=False, check_fee=False)
|
||||
print(f"Multi-sig hash: {multisig_result.result['hash']}, "
|
||||
f"result: {multisig_result.result['meta']['TransactionResult']}")
|
||||
|
||||
@@ -1,66 +1,44 @@
|
||||
from xrpl.clients import JsonRpcClient
|
||||
from xrpl.models.transactions import TicketCreate, AccountSet
|
||||
from xrpl.transaction import sign_and_submit
|
||||
from xrpl.models.requests import AccountObjects, AccountObjectType
|
||||
from xrpl.models.transactions import AccountSet, TicketCreate
|
||||
from xrpl.transaction import submit_and_wait
|
||||
from xrpl.wallet import generate_faucet_wallet
|
||||
from xrpl.models.requests.account_objects import AccountObjects, AccountObjectType
|
||||
|
||||
# Connect to a testnet node
|
||||
# Set up client and account
|
||||
JSON_RPC_URL = "https://s.altnet.rippletest.net:51234/"
|
||||
client = JsonRpcClient(JSON_RPC_URL)
|
||||
|
||||
# Generate a wallet and request faucet
|
||||
test_wallet = generate_faucet_wallet(client=client)
|
||||
myAddr = test_wallet.address
|
||||
# Fund wallet
|
||||
print("Getting a wallet from the faucet...")
|
||||
wallet = generate_faucet_wallet(client=client)
|
||||
|
||||
# Construct a TicketCreate transaction, 2 ticket created for future use
|
||||
tx = TicketCreate(
|
||||
account=myAddr,
|
||||
ticket_count=2
|
||||
# Create Tickets
|
||||
ticket_create = TicketCreate(
|
||||
account=wallet.address,
|
||||
ticket_count=10,
|
||||
)
|
||||
print("Submitting TicketCreate transaction...")
|
||||
ticket_create_result = submit_and_wait(ticket_create, client, wallet)
|
||||
print(f"TicketCreate hash: {ticket_create_result.result['hash']}, validated: {ticket_create_result.result['validated']}")
|
||||
|
||||
# Sign transaction locally and submit
|
||||
my_tx_payment_signed = sign_and_submit(transaction=tx, client=client, wallet=test_wallet)
|
||||
# Check Available Tickets
|
||||
tickets_response = client.request(AccountObjects(
|
||||
account=wallet.address,
|
||||
type=AccountObjectType.TICKET,
|
||||
))
|
||||
tickets = tickets_response.result["account_objects"]
|
||||
print(f"Found {len(tickets)} Tickets")
|
||||
|
||||
# Get a Ticket Sequence
|
||||
get_ticket_sequence = AccountObjects(
|
||||
account=myAddr,
|
||||
type=AccountObjectType.TICKET
|
||||
)
|
||||
# Choose an arbitrary Ticket to use
|
||||
use_ticket = tickets[0]["TicketSequence"]
|
||||
print(f"Using Ticket Sequence: {use_ticket}")
|
||||
|
||||
response = client.request(get_ticket_sequence)
|
||||
|
||||
# Since we created 2 Tickets previously, you're able to choose which Ticket you're going to use
|
||||
ticket1_sequence = response.result["account_objects"][0]["TicketSequence"]
|
||||
ticket2_sequence = response.result["account_objects"][1]["TicketSequence"]
|
||||
|
||||
print(f"Ticket 1: {ticket1_sequence}\n"
|
||||
f"Ticket 2: {ticket2_sequence}")
|
||||
|
||||
ticket_sequence = int(input("Pick a ticket sequence to use for your next transaction: "))
|
||||
|
||||
# Construct Transaction using a Ticket
|
||||
tx_1 = AccountSet(
|
||||
account=myAddr,
|
||||
fee="10",
|
||||
# Use a Ticket
|
||||
ticketed_tx = AccountSet(
|
||||
account=wallet.address,
|
||||
ticket_sequence=use_ticket,
|
||||
sequence=0,
|
||||
last_ledger_sequence=None,
|
||||
ticket_sequence=ticket_sequence
|
||||
)
|
||||
|
||||
# Send transaction (w/ Ticket)
|
||||
tx_result = sign_and_submit(transaction=tx_1, client=client, wallet=test_wallet)
|
||||
result = tx_result.result["engine_result"]
|
||||
|
||||
print(f"Account: {myAddr}")
|
||||
if result == "tesSUCCESS":
|
||||
print("Transaction successful!")
|
||||
elif result == "terPRE_TICKET":
|
||||
print("The provided Ticket Sequence does not exist in the ledger")
|
||||
elif result == "tefMAX_LEDGER":
|
||||
print("Transaction failed to achieve consensus")
|
||||
elif result == "tefPAST_SEQ":
|
||||
print("The sequence number is lower than the current sequence number of the account")
|
||||
elif result == "unknown":
|
||||
print("Transaction status unknown")
|
||||
else:
|
||||
print(f"Transaction failed with code {result}")
|
||||
print("Submitting ticketed AccountSet transaction...")
|
||||
ticketed_result = submit_and_wait(ticketed_tx, client, wallet)
|
||||
print(f"Ticketed AccountSet hash: {ticketed_result.result['hash']}, validated: {ticketed_result.result['validated']}")
|
||||
|
||||
Reference in New Issue
Block a user