Starknet Provider WSS
Index
Supports for the Starknet JSON RPC spec v0.8.0 Web Socket API in the starknet_provider package.
I. Introduction
With this methods you will be able to communicate with web socket nodes interacting with Starknet in a more reactive way establishing full duplex connections and listen for blockchain events.
1) Contructor: StarknetWebSocketChannel(String nodeUrl);
Create a new web socket channel to communicate with a Starknet node.
Arguments:
nodeUrl
: The web socket node url.
6) sendReceive(String method, [Map<String, dynamic>? params])
Send a request to the web socket node and receive the response.
Arguments:
method
: The method to call.params
: The parameters to send to the method.
2) subscribeEvents([Felt? fromAddress, List<List<String>>? keys, dynamic blockIdentifier])
Subscribe to events.
Arguments:
fromAddress
: The address to start the subscription from.keys
: The keys to subscribe to.blockIdentifier
: The block identifier to subscribe to.
3) subscribeTransactionStatus(Felt transactionHash)
Subscribe to transaction status.
Arguments:
transactionHash
: The transaction hash to subscribe to.
4) subscribePendingTransactions([bool? transactionDetails, List<Felt>? senderAddress])
Subscribe to pending transactions.
Arguments:
transactionDetails
: The transaction details to subscribe to.senderAddress
: The sender address to subscribe to.
5) unsubscribe(String subscriptionId, [String? ref])
Unsubscribe from a subscription.
Arguments:
subscriptionId
: The subscription id to unsubscribe from.ref
: The reference to unsubscribe from.
1) onNewHeads(StarknetWebSocketChannel, WssSubscriptionNewHeadResponse)
Handle new block heads.
Arguments:
channel
: The channel to handle.response
: The response to handle.
Here is an example of a WssSubscriptionNewHeadResponse
:
{
"subscription_id": "0x1",
"result": {
"blockHash": "0x12345...",
"parentHash": "0xabcde...",
"blockNumber": 123456,
"newRoot": "0x67890...",
"timestamp": 1678901234,
"sequencerAddress": "0xfedcb...",
"l1GasPrice": {
"priceInFri": "0x123",
"priceInWei": "0x456"
},
"l2GasPrice": {
"priceInFri": "0x789",
"priceInWei": "0xabc"
},
"l1DataGasPrice": {
"priceInFri": "0xdef",
"priceInWei": "0x123"
},
"l1DaMode": "CALLDATA",
"starknetVersion": "0.12.1"
}
}
2) onEvents(StarknetWebSocketChannel, WssSubscriptionEventResponse)
Handle received events.
Arguments:
channel
: The channel to handle.response
: The response to handle.
Here is an example of a WssSubscriptionEventResponse
:
{
"subscription_id": "0x2",
"result": {
"events": [
{
"from_address": "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
"keys": [
"0x99cd8bde557814842a3121e8ddfd433a539b8c9f14bf31ebf108d12e6196e9"
],
"data": [
"0x0000000000000000000000000000000000000000000000000000000000000001",
"0x0000000000000000000000000000000000000000000000000000000000000002",
"0x0000000000000000000000000000000000000000000000000000000000000003"
],
"block_hash": "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"block_number": 123456,
"transaction_hash": "0xabcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
},
{
"from_address": "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
"keys": [
"0x99cd8bde557814842a3121e8ddfd433a539b8c9f14bf31ebf108d12e6196e9"
],
"data": [
"0x0000000000000000000000000000000000000000000000000000000000000004",
"0x0000000000000000000000000000000000000000000000000000000000000005",
"0x0000000000000000000000000000000000000000000000000000000000000006"
],
"block_hash": "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"block_number": 123456,
"transaction_hash": "0xfedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"
}
]
}
}
3) onTransactionStatus(StarknetWebSocketChannel, WssSubscriptionTransactionsStatusResponse)
Handle received transaction status.
Arguments:
channel
: The channel to handle.response
: The response to handle.
Here is an example of a WssSubscriptionTransactionsStatusResponse
:
{
"subscription_id": "0x3",
"result": {
"transaction_hash": "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"status": "ACCEPTED_ON_L2",
"block_hash": "0xfedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210",
"block_number": 123456,
"transaction_index": 42,
"execution_status": "SUCCEEDED",
"finality_status": "ACCEPTED_ON_L2",
"execution_resources": {
"steps": 1000,
"memory_holes": 50,
"range_check_builtin_applications": 10,
"pedersen_builtin_applications": 5,
"poseidon_builtin_applications": 3,
"ec_op_builtin_applications": 2,
"ecdsa_builtin_applications": 1,
"bitwise_builtin_applications": 4,
"keccak_builtin_applications": 2
},
"revert_reason": null
}
}
4) onPendingTransactions(StarknetWebSocketChannel, WssSubscriptionPendingTransactionsResponse)
Handle received pending transactions.
Arguments:
channel
: The channel to handle.response
: The response to handle.
Here is an example of a WssSubscriptionPendingTransactionsResponse
:
{
"subscription_id": "0x4",
"result": {
"transactions": [
{
"transaction_hash": "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"max_fee": "0x1234567890",
"version": "0x1",
"signature": [
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
],
"nonce": "0x42",
"type": "INVOKE",
"sender_address": "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"calldata": [
"0x1",
"0x2",
"0x3"
]
},
{
"transaction_hash": "0xfedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210",
"max_fee": "0x9876543210",
"version": "0x1",
"signature": [
"0x9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba",
"0xfedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"
],
"nonce": "0x43",
"type": "INVOKE",
"sender_address": "0xfedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210",
"calldata": [
"0x4",
"0x5",
"0x6"
]
}
]
}
}
5) onReorg(StarknetWebSocketChannel, WssSubscriptionReorgResponse)
Handle received reorg. This message could be received from subscribing to newHeads, Events or TransactionStatus.
Arguments:
channel
: The channel to handle.response
: The response to handle.
Here is an example of a WssSubscriptionReorgResponse
:
{
"subscription_id": "0x5",
"result": {
"old_head": {
"blockHash": "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"parentHash": "0xfedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210",
"blockNumber": 123456
},
"new_head": {
"blockHash": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"parentHash": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"blockNumber": 123456
}
}
}
Subscribe and listen for new block heads
In the following example, we will subscribe to new block heads and keep listening for 5 new block heads.
import 'package:starknet/starknet.dart';
final nodeUrl = 'wss://sepolia-pathfinder-rpc.spaceshard.io/rpc/v0_8';
final webSocketChannel = StarknetWebSocketChannel(nodeUrl: nodeUrl);
await webSocketChannel.waitForConnection();
// Setup onNewHeads handler function. In this example, we will
// process every new block head and store it in a list.
final eventCompleter = Completer<void>();
final blocks = <WssSubscriptionNewHeadResponse>[];
webSocketChannel.onNewHeads = (channel, response) {
blocks.add(response);
print("blocks.length: ${blocks.length}");
print("received block: ${response.result}");
if (blocks.length == 5) {
eventCompleter.complete();
}
};
// Then we initiate the subscription sending the new block heads
// subscription message to the wss endpoint.
final subId = await webSocketChannel.subscribeNewHeads();
subId.when(
result: (subId) {
// we can check if the subscription was successful
expect(subId, isNotNull);
},
error: (error) => fail('Should not return an error'),
);
// At this point, we will start to receive and process all the new block heads.
// Wait for the subscription events completion
// This is wait for eventCompleter.complete() to be called.
await eventCompleter.future;
// At this point, in the background we are still receiving and processing the new block heads.
// So, let's unsubscribe to stop receiving and processing the new block heads.
final status = await webSocketChannel.unsubscribeNewHeads();
status.when(
// we can check if the unsubscription was successful
result: (status) => expect(status, true),
error: (error) => fail('Should not return an error'),
);
// Disconnect from the web socket node
await webSocketChannel.disconnect();
await webSocketChannel.waitForDisconnect();
Subscribe and listen for new events
In the following example, we will subscribe to new events and keep listening for 5 events.
import 'package:starknet/starknet.dart';
final nodeUrl = 'wss://sepolia-pathfinder-rpc.spaceshard.io/rpc/v0_8';
final webSocketChannel = StarknetWebSocketChannel(nodeUrl: nodeUrl);
await webSocketChannel.waitForConnection();
// Setup onEvents handler function. In this example, we will
// process 5 events and print their transaction_hash.
final eventCompleter = Completer<void>();
int eventCount = 0;
webSocketChannel.onEvents = (channel, response) {
print("received event transaction_hash: ${response.result.transactionHash}");
eventCount++;
if (eventCount == 5) {
eventCompleter.complete();
}
};
// Then we initiate the subscription sending the events
// subscription message to the wss endpoint.
final subId = await webSocketChannel.subscribeEvents();
subId.when(
result: (subId) {
// we can check if the subscription was successful
expect(subId, isNotNull);
},
error: (error) => fail('Should not return an error'),
);
// At this point, we will start to receive and process all the events.
// Wait for the subscription events completion
// This is wait for eventCompleter.complete() to be called.
await eventCompleter.future;
// At this point, in the background we are still receiving and processing the events.
// So, let's unsubscribe to stop receiving and processing the events.
final status = await webSocketChannel.unsubscribeEvents();
status.when(
// we can check if the unsubscription was successful
result: (status) => expect(status, true),
error: (error) => fail('Should not return an error'),
);
// Disconnect from the web socket node
await webSocketChannel.disconnect();
await webSocketChannel.waitForDisconnect();
Subscribe and listen for pending transactions
In the following example, we will subscribe to pending transactions and keep listening for 5 pending transactions.
import 'package:starknet/starknet.dart';
final nodeUrl = 'wss://sepolia-pathfinder-rpc.spaceshard.io/rpc/v0_8';
final webSocketChannel = StarknetWebSocketChannel(nodeUrl: nodeUrl);
await webSocketChannel.waitForConnection();
// Setup onPendingTransactions handler function. In this example, we will
// process 5 pending transactions and print their transaction_hash.
final eventCompleter = Completer<void>();
int eventCount = 0;
webSocketChannel.onPendingTransactions = (channel, response) {
print("received pending transaction transaction_hash: ${response.result.transactionHash}");
eventCount++;
if (eventCount == 5) {
eventCompleter.complete();
}
};
// Then we initiate the subscription sending the pending transactions
// subscription message to the wss endpoint.
final subId = await webSocketChannel.subscribePendingTransactions();
subId.when(
result: (subId) {
// we can check if the subscription was successful
expect(subId, isNotNull);
},
error: (error) => fail('Should not return an error'),
);
// At this point, we will start to receive and process all the pending transactions.
// Wait for the subscription pending transactions completion
// This is wait for eventCompleter.complete() to be called.
await eventCompleter.future;
// At this point, in the background we are still receiving and processing the pending transactions.
// So, let's unsubscribe to stop receiving and processing the pending transactions.
final status = await webSocketChannel.unsubscribePendingTransactions();
status.when(
// we can check if the unsubscription was successful
result: (status) => expect(status, true),
error: (error) => fail('Should not return an error'),
);
// Disconnect from the web socket node
await webSocketChannel.disconnect();
await webSocketChannel.waitForDisconnect();
Subscribe and listen for transaction status
In the following example, we will subscribe to transaction status to keep listening for transaction status until it is accepted on L2.
import 'package:starknet/starknet.dart';
final nodeUrl = 'wss://sepolia-pathfinder-rpc.spaceshard.io/rpc/v0_8';
final webSocketChannel = StarknetWebSocketChannel(nodeUrl: nodeUrl);
await webSocketChannel.waitForConnection();
// Setup onTransactionStatus handler function. In this example, we will
// process the transaction status until it is accepted on L2.
final eventCompleter = Completer<void>();
webSocketChannel.onTransactionStatus = (channel, response) {
print("received transaction status: ${response.result.finalityStatus}");
if (response.result.finalityStatus == "ACCEPTED_ON_L2") {
eventCompleter.complete();
}
};
// Then we initiate the subscription sending the transaction status
// subscription message to the wss endpoint.
final transactionHash = Felt.fromHexString("0x194b07e7a536cbf2b94c74d558af8b9c689dbdd70a649a7ce1ca07375ae3cc9");
final subId = await webSocketChannel.subscribeTransactionStatus(transactionHash);
subId.when(
result: (subId) {
// we can check if the subscription was successful
expect(subId, isNotNull);
},
error: (error) => fail('Should not return an error'),
);
// At this point, we will start to receive and process all the transaction status events.
// Wait for the subscription transaction status completion
// This is wait for eventCompleter.complete() to be called.
await eventCompleter.future;
// At this point, in the background we are still receiving and processing the transaction status.
// So, let's unsubscribe to stop receiving and processing the transaction status.
final status = await webSocketChannel.unsubscribeTransactionStatus();
status.when(
// we can check if the unsubscription was successful
result: (status) => expect(status, true),
error: (error) => fail('Should not return an error'),
);
// Disconnect from the web socket node
await webSocketChannel.disconnect();
await webSocketChannel.waitForDisconnect();
Send and receive a regular rpc message
In the following example, we will query the chainId of the Starknet sepolia network.
import 'package:starknet/starknet.dart';
final nodeUrl = 'wss://sepolia-pathfinder-rpc.spaceshard.io/rpc/v0_8';
final webSocketChannel = StarknetWebSocketChannel(nodeUrl: nodeUrl);
await webSocketChannel.waitForConnection();
// Send a regular rpc message to the web socket node
final response = await webSocketChannel.sendReceive('starknet_chainId');
// Check if the response is the expected chainId
final snSepolia = '0x534e5f5345504f4c4941'; //SN_SEPOLIA
expect(response['result'], snSepolia);
// Disconnect from the web socket node
await webSocketChannel.disconnect();
await webSocketChannel.waitForDisconnect();
IV. How to run package tests
To run the tests, you need to set the environment variable for STARKNET_WSS
with the
corresponding values.
export STARKNET_WSS=wss://sepolia-pathfinder-rpc.spaceshard.io/rpc/v0_8
Then, run the tests with the following command:
cd packages/starknet_provider
dart test --fail-fast --name 'websocket specific endpoints - pathfinder test' --chain-stack-traces
dart test --fail-fast --name 'websocket regular endpoints - pathfinder test' --chain-stack-traces