diff --git a/include/solana.hpp b/include/solana.hpp index bb9497e..2ee2be6 100644 --- a/include/solana.hpp +++ b/include/solana.hpp @@ -1103,6 +1103,14 @@ class Connection { /// Websocket requests namespace subscription { +enum class LogsFilter : short { ALL, ALLWITHVOTES }; + +NLOHMANN_JSON_SERIALIZE_ENUM(LogsFilter, + { + {LogsFilter::ALL, "all"}, + {LogsFilter::ALLWITHVOTES, "allWithVotes"}, + }) + /** * Subscribe to an account to receive notifications when the lamports or data * for a given account public key changes @@ -1138,6 +1146,13 @@ class WebSocketSubscriber { /// @brief remove the account change listener for the given id /// @param sub_id the id for which removing subscription is needed void removeAccountChangeListener(RequestIdType sub_id); + + int onLogs(Callback callback, + const Commitment &commitment = Commitment::FINALIZED, + const LogsFilter &logFilter = LogsFilter::ALL, + Callback on_subscibe = nullptr, Callback on_unsubscribe = nullptr); + + void removeOnLogsListener(RequestIdType sub_id); }; } // namespace subscription } // namespace rpc diff --git a/lib/solana.cpp b/lib/solana.cpp index 835995a..18ed7be 100644 --- a/lib/solana.cpp +++ b/lib/solana.cpp @@ -1137,6 +1137,31 @@ int WebSocketSubscriber::onAccountChange(const solana::PublicKey &pub_key, void WebSocketSubscriber::removeAccountChangeListener(RequestIdType sub_id) { sess->unsubscribe(sub_id); } + +/*Function to subscribe to logs*/ +int WebSocketSubscriber::onLogs(Callback callback, const Commitment &commitment, + const LogsFilter &logFilter, + Callback on_subscibe, Callback on_unsubscribe) { + // create parameters using the user provided input + json param = {logFilter, {{"commitment", commitment}}}; + + // create a new request content + RequestContent req(curr_id, "logsSubscribe", "logsUnsubscribe", callback, + std::move(param), on_subscibe, on_unsubscribe); + + // subscribe the new request content + sess->subscribe(req); + + // increase the curr_id so that it can be used for the next request content + curr_id += 2; + + return req.id; +} + +void WebSocketSubscriber::removeOnLogsListener(RequestIdType sub_id) { + sess->unsubscribe(sub_id); +} + } // namespace subscription } // namespace rpc } // namespace solana \ No newline at end of file diff --git a/tests/main.cpp b/tests/main.cpp index 4a29aee..3ce5120 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -984,6 +984,35 @@ TEST_CASE("getBlocks") { CHECK_LE(Blocks[Blocks.size() - 1], latestslot); } +TEST_CASE("logs subscribe and unsubscribe") { + solana::Keypair keyPair = solana::Keypair::fromFile(KEY_PAIR_FILE); + const auto connection = solana::rpc::Connection(solana::DEVNET); + solana::rpc::subscription::WebSocketSubscriber sub("api.devnet.solana.com", + "80"); + bool subscribe_called = false; + auto call_on_subscribe = [&subscribe_called](const json& data) { + subscribe_called = true; + }; + + // solana::rpc::subscription::WebSocketSubscriber + // subscribe for account change + int sub_id = sub.onLogs(call_on_subscribe); + // change account data + connection.requestAirdrop(keyPair.publicKey, 50); + // wait for 40 seconds for transaction to process + sleep(40); + // assure that callback has been called + CHECK(subscribe_called); + // stop listening to websocket + sub.removeOnLogsListener(sub_id); + // change account data + connection.requestAirdrop(keyPair.publicKey, 50); + // wait for 40 seconds for transaction to process + sleep(40); + // ensure that callback wasn't called + CHECK(subscribe_called); +} + TEST_CASE("getTokenSupply") { const auto connection = solana::rpc::Connection(solana::MAINNET_BETA); const auto TokenSupply = @@ -1020,3 +1049,4 @@ TEST_CASE("getTokenAccountsByOwner") { solana::TokenAccountsByOwnerConfig{{}, "jsonParsed"}); CHECK_GT(TokenAccountsByOwner.value.size(), 0); } +