Transaction Lifecycle
Overview
Treasury offers a rich, intent based API to create transfers, stakings, and other kinds of transactions. Policy is applied to the intent, and then a transaction is created if it's allowed. The transaction is serialized for the specific blockchain and then signed and broadcasted.
In short, the lifecycle of a transaction is as follows:
- A resource like a
Transfer
is created ("move Bitcoin from A to B"). - Immediately the policy is checked, and the request is rejected if it doesn't pass. It can also go into an
authorizing
state where additional approvers are required. - Blockchain inputs are fetched using RPC of the specific blockchain. The specific inputs depend on the chain, but often include fee information, sequence numbers, or specific UTXOs. This is collected by a treasury node designated as a "connecting" node (Treasury demos are always connecting nodes). By default, our connector API is used to grant universal RPC access, but you can use your own RPC endpoints as well.
- The transaction is serialized and a signature request is made.
- The serialized transaction is signed.
- The signed, serialized transaction is broadcasted (via connecting node, using connector API or your own RPC endpoint).
- The transaction is polled on chain + updated on treasury until it has sufficient confirmations.
This all happens in about 1 second. Let's walk through a demo.
Demo
Start a fresh Treasury demo.
treasury login
treasury demo start --keep --pull --port 8777
Now create a couple of addresses, and permit all transfers. The demo is connected to specific devnet or testnet networks. You should generate addresses for a chain that has an easy "faucet" to get funds to test with.
# create two SOL addresses
treasury addresses create SOL
treasury addresses create SOL
# permit all transfers
treasury transfer-rules create allow ALL --any-from --any-to --any-asset
# later delete the rule via `treasury transfer-rules delete ALL`
Now look at your addresses and request some devnet funds to it from your favorite faucet (e.g. Solana).
treasury addresses ls
Now create a transfer between your two addresses.
treasury transfers create --from "<FROM>" --to "<TO>" 0.001 --chain SOL
If you have any funds, this should succeed, going through full lifecycle very quickly.
In depth look
Now let's create another transfer + transaction and look closely at what happens.
First, open a terminal connecting to the events websocket.
websocat ws://127.0.0.1:8777/v1/events | jq 'select( .type != "Operation" )'
Now create another transfer and watch the events.
treasury transfers create --from "<FROM>" --to "<TO>" 0.001 --chain SOL
Each event has a type
and an action
, along with a corresponding resource that was created, updated, or deleted. Among the events you should see:
create Transfer
: This is the transfer intent initially created (post policy passing).create Transaction
: This is the transaction created from the transfer intent.update Transaction
: The transaction will be updated with an"input"
from the blockchain.create Signature
: The signature request created. Note that.message
is the payload that is requested to be signed.update Signature
: The signature request is being completed by the internal signing service (see.signature
).- Then you will a few updates to the
Transaction
by the demo's connector process, until the transaction has received sufficient confirmations.
Notes
- The signer process is fully modular, and could be replaced by an independent signer process (e.g. a co-located HSM).
- In production, the "connecting" process can run external to where any keys are stored, so no internet connectivity is required where keys are handled.
- Some events look like they occur out of order, but they are simply happening at the same time in an atomic fashion. Like when the transaction is updated with an input, and then the signature is created at the same time.