diff --git a/docs/diagrams/v2/core/transaction/transaction_id.md b/docs/diagrams/v2/core/transaction/transaction_id.md
new file mode 100644
index 000000000..346b8ecb9
--- /dev/null
+++ b/docs/diagrams/v2/core/transaction/transaction_id.md
@@ -0,0 +1,197 @@
+# Transaction IDs May Collide
+
+The class [`Transaction`](../../../../../packages/core/src/transaction/Transaction.ts)
+provides the computed property `Transaction.id()` to compute the transaction's identifier
+as (l.242)
+
+```typescript
+public get id(): Blake2b256 {
+    if (this.isSigned) {
+        return Blake2b256.of(
+            nc_utils.concatBytes(
+                this.getTransactionHash().bytes,
+                this.origin.bytes
+            )
+        );
+    }
+    throw new UnavailableTransactionField(
+        'Transaction.id()',
+        'not signed transaction: id unavailable',
+        {fieldName: 'id'}
+    );
+}
+```
+
+The following flowchart shows the inputs, methods and properties involved.
+
+```mermaid
+flowchart TD
+    start((Start))
+    stop(((stop)))
+    body[/TransactionBody/]
+    subgraph origin["Transaction.origin()"]
+        origin_txHash[["getTransactionHash()"]]
+        origin_signature[\signature\]
+        origin_recover[["Secp256k1.recover(txHash,signature)"]]
+        origin_address["Address.ofPublicKey(publicKey)"]
+        origin_recover --> origin_address
+        origin_signature --> origin_recover
+
+    end
+    subgraph tx_hash["Transaction.getTransactionHash(gasPayer?)"]
+        txHash_encodeHash[Blake256.of]
+        txHash_encodePayer[Blake2b256.of]
+        txHash_encode[[encode]]
+        txHash_payer?{gasPayer?}
+        txHash_payer[/gasPayer/]
+        txHash_encode --> txHash_encodeHash
+        txHash_encodeHash --> txHash_payer?
+        txHash_payer? -. yes .-> txHash_encodePayer
+        txHash_payer --> txHash_encodePayer
+    end
+    subgraph tx_id["Transaction.id()"]
+        id_hash[Blake2b256.of]
+        id_origin[["origin()"]]
+        id_txHash[["getTransactionHash()"]]
+        id_origin --> id_hash
+        id_txHash --> id_hash
+    end
+    body --> txHash_encode
+    id_hash --> stop
+    origin_address --> id_origin
+    origin_txHash --> origin_recover
+    start --> body
+    txHash_encodePayer --> id_txHash
+    txHash_payer? -- no --> id_txHash
+
+```
+
+## Not Delegated Transaction ID
+
+NCC claims
+
+_1. ... The transaction signature is not incorporated into the hash computation; only the transaction hash as well as
+the
+originator’s address are taken into account._ (p.12)
+
+but we observe **the origin's address is derived by the origin's signature because the computed property**
+`Transaction.origin` (l.304).
+
+```typescript
+public get origin(): Address
+{
+    if (this.signature !== undefined) {
+        return Address.ofPublicKey(
+            // Get the origin public key.
+            Secp256k1.recover(
+                this.getTransactionHash().bytes,
+                // Get the (r, s) of ECDSA digital signature without gas payer params.
+                this.signature.slice(0, Secp256k1.SIGNATURE_LENGTH)
+            )
+        );
+    }
+    throw new UnavailableTransactionField(
+        'Transaction.origin()',
+        'not signed transaction, no origin',
+        {fieldName: 'origin'}
+    );
+}
+```
+
+NCC clams
+
+_ ... if a transaction was signed by a signer multiple times (or in case of different
+transactions with colliding getTransactionHash()), the resulting signed transactions would
+have the same ID._ (p.13)
+
+We observe if the transaction is signed multiple times, it results the same id if the
+`TransactionBody` object is equal and the origin's signature is the same. 
+
+### Question - ask NCC
+
+Since the origin's address is a function of the origin's signature, **what is the difference
+between to compute BLAKE2B256**
+
+* **from the transaction hash and the origin's signature**, and
+* **from the transaction hash and the origin's address derived from its signature?**
+
+---
+
+## Delegated Transaction ID
+
+NCC claims
+
+_2. In the case of delegated transactions, the function above also fails to take the gas
+payer’s (aka delegator’s) signature and address into account._ (p.13)
+
+We observe the claim is true: in the `Transaction.id` computed property at l.246
+
+```typescript
+this.getTransactionHash().bytes
+```
+
+does not pass the `gasPayer` as argument, hence NCC is right to claim
+
+_... if a given to-be-delegated transaction were
+signed by multiple different gas payers, the resulting IDs would all be equal (and would also
+be equal to the ID of a non-delegated transaction)._ (p.13)
+
+We observe the `reserved` flag in the `Transaction` body participates in the computed hash, hence
+a delegated transaction should result in a different id then a not delegated transaction,
+this is the only input in the BLAKE2B256 hash to differentiate the id transactions of the same
+`TransactionBody` between the cases when the transaction is delegated and when is not. Since a transaction must
+include the origin signature and if is delegated, must include the `reserved` property and a gas payer's signature,
+we do not think there is a real useful case to forge the `reserved` field, nevertheless we think **
+
+NCC recommends
+
+_Consider modifying the id() function such that it includes the signer’s signature, and the
+delegator’s (wrong name for the "gas payer") address and signature, if the transaction is delegated._ (p.13)
+
+We think this should be done, but we must evaluate what is done in Thor as well and if the Thor ID algorithm must change
+following the NCC recommendations.
+
+NCC recommends
+
+_... reflect on whether the id() function should product a different digest than the
+getTransactionHash() of a delegated transaction._ (p.13)
+
+**We think the id of delegated transaction should be different form its transaction's hash, we observe they are
+different - matter of test - because the `Transaction.id` hashes the result of `Transaction.getTransactionHash` 
+concatenated with the transaction;s origin address.**
+
+---
+
+### Question - ask the protocol team
+
+**The `gasPayer` signature or its derived address is never considered in the computation of the transaction hash!**
+
+* **What is the algorithm used in the protocol**?
+  * **Is the same algorithm implemented the in e SDK?**
+  * **The algorithm in the protocol does consider the `taxPayer` address and signature in the computation of the id?**
+
+**According to our tests, Thor and SDK produce the same id for the same transaction!**
+---
+
+## ID Equality
+
+We presume is correct to state equal
+[`TransactionBody`](../../../../../packages/core/src/transaction/TransactionBody.d.ts)
+instances signed by the same origin and gas payer should return the same id.
+Since Thor rejects a transaction with a spent id, the above criteria should never be a problem;
+Thor blockchain should never have two transaction with the same id, ids should be unique.
+However, the `TransactionBody` class exposes the `nonce` property that is involved in the computation
+of the transaction's hash.
+
+### Question - ask the protocol team
+
+The `Transaction.encode` method processes an object of the
+[`TransactionBody`](../../../../../packages/core/src/transaction/TransactionBody.d.ts)
+class, the class provides the `nonce` property 
+The `nonce` property should be different for each transaction even if
+the other properties of the transaction are equal, however no assumption are made about `nonce`.
+* **Who sets the `nonce` property?**
+
+---
+
+
diff --git a/docs/diagrams/v2/net/http/http.md b/docs/diagrams/v2/net/http/http.md
new file mode 100644
index 000000000..0178766de
--- /dev/null
+++ b/docs/diagrams/v2/net/http/http.md
@@ -0,0 +1,35 @@
+```mermaid
+classDiagram
+    class FetchHttpClient {
+        baseURL: string
+        onRequest: OnRequest
+        onResponse: OnResponse
+        at(baseURL: string, onRequest: OnRequest, onResponse: OnResponse) FetchHttpClient
+    }
+    class HttpClient {
+        <<interface>>
+        get(httpPath: HttpPath, httpQuery: HttpQuery) Promise~Response~
+        post(httpPath: HttpPath, httpQuery: HttpQuery, body?: unknown) Promise~Response~
+    }
+    class HttpPath {
+        <<interface>>
+        path: string
+    }
+    class HttpQuery {
+        <<interface>>
+        query(): string;
+    }
+    class OnRequest {
+        <<callback>>
+        onRequest(request: Request) Request
+    }
+    class OnResponse{
+        <<callback>>
+        onResponse(response: Response) Response
+    }
+    FetchHttpClient *--> OnRequest
+    FetchHttpClient *--> OnResponse
+    HttpClient <|.. FetchHttpClient
+    HttpPath <-- "get - post" HttpClient
+    HttpQuery <-- "get - post" HttpClient
+```
diff --git a/docs/diagrams/v2/net/thor/accounts/accounts.md b/docs/diagrams/v2/net/thor/accounts/accounts.md
new file mode 100644
index 000000000..bf9020525
--- /dev/null
+++ b/docs/diagrams/v2/net/thor/accounts/accounts.md
@@ -0,0 +1,218 @@
+```mermaid
+classDiagram
+    namespace JS {
+        class Array~Type~ {
+            <<type>>
+        }
+    }
+    namespace http {
+        class HttpClient {
+            <<interface>>
+            get(httpPath: HttpPath) Promise~Response~
+            post(httpPath: HttpPath, body?: unknown) Promise~Response~
+        }
+        class HttpPath {
+            <<interface>>
+            path: string
+        }
+    }
+    namespace thor {
+        class ThorRequest~RequestClass~ {
+            <<interface>>
+            askTo(httpClient: HttpClient Promise~ThorResponse~ResponseClass~~;
+        }
+        class ThorResponse~ResponseClass~ {
+            <<interface>>
+            request: ThorRequest~RequestClass~
+            response: ResponseClass
+        }
+    }
+    namespace transactions {
+        class Clause {
+            to: Address | null
+            value: VET
+            data: HexUInt
+            constructor(json: ClauseJSON)
+            toJSON() ClauseJSON
+        }
+        class ClauseJSON {
+            to: string | null
+            value: string
+            data: string
+        }
+        class Event {
+            address: Address
+            topics: ThorId[]
+            data: HexUInt
+            constructor(json: EventJSON) Event
+            toJSON() EventJSON
+        }
+        class EventJSON {
+            <<interface>>
+            address: string
+            topics: string[]
+            data: string
+        }
+        class Transfer {
+            sender: Address
+            recipient: Address
+            amount: VET
+            constructor(json: TransferJSON) Transfer
+            toJSON() TransferJSON
+        }
+        class TransferJSON {
+            <<interface>>
+            sender: string
+            recipient: string
+            amount: string
+        }
+    }
+    class ContractBytecode {
+        code HexUInt
+        constructor(json ContractBytecodeJSON) ContractBytecode
+        toJSON() ContractBytecodeJSON
+    }
+    class ContractBytecodeJSON {
+        <<interface>>
+        code: string
+    }
+    class ExecuteCodesRequest {
+        provedWork?: string
+        gasPayer?: Address
+        expiration?: UInt
+        blockRef?: BlockRef
+        clauses?: Clause[]
+        gas?: VTHO
+        gasPrice?: VTHO
+        caller?: Address
+        constructor(json: ExecuteCodesRequestJSON) ExecuteCodesRequest
+        toJSON() ExecuteCodesRequestJSON
+    }
+    class ExecuteCodesRequestJSON {
+        <<interface>>
+        provedWork?: string
+        gasPayer?: string
+        expiration?: number
+        blockRef?: string
+        clauses?: ClauseJSON[]
+        gas?: number
+        gasPrice?: string
+        caller?: string
+    }
+    class ExecuteCodeResponse {
+        data: HexUInt
+        events: Event[]
+        transfers: Transfer[]
+        gasUsed: VTHO
+        reverted: boolean
+        vmError: string
+        constructor(json: ExecuteCodeResponseJSON)
+        toJSON() ExecuteCodeResponseJSON
+    }
+    class ExecuteCodeResponseJSON {
+        <<interface>>
+        data: string
+        events: EventJSON[]
+        transfers: TransferJSON[]
+        gasUsed: number
+        reverted: boolean
+        vmError: string
+    }
+    class ExecuteCodesResponse {
+        constructor(json: ExecuteCodesResponseJSON) ExecuteCodesResponse
+    }
+    class ExecuteCodesResponseJSON {
+        <<interface>>
+    }
+    class GetAccountResponse {
+        balance: VET
+        energy: VTHO
+        hasCode: boolean
+        constructor(json: GetAccountResponseJSON) GetAccountResponse
+    }
+    class GetAccountResponseJSON {
+        <<interface>>
+        balance: string
+        energy: string
+        hasCode: boolean
+    }
+    class GetStorageResponse {
+        value: ThorId
+        constructor(json: GetStorageResponseJSON) GetStorageResponse
+        toJSON() GetStorageResponseJSON
+    }
+    class GetStorageResponseJSON {
+        <<interface>>
+        value: string
+    }
+    class InspectClauses {
+        PATH: HttpPath$
+        askTo(httpClient: HttpClient) Promise~ThorResponse~ExecuteCodesResponse~~
+        of(request: ExecuteCodesRequestJSON) InspectClauses$
+        withRevision(revision: Revision) InspectClauses
+    }
+    class RetrieveAccountDetails {
+        path: RetrieveAccountDetailsPath
+        askTo(httpClient: HttpClient) Promise~ThorResponse~GetAccountResponse~~
+        of(address: Address) RetrieveAccountDetails$
+    }
+    class RetrieveAccountDetailsPath {
+        address: Address
+    }
+    class RetrieveContractBytecode {
+        path: RetrieveContractBytecodePath
+        askTo(httpClient: HttpClient) Promise~ThorResponse~ContractBytecode~~
+        of(address: Address) RetrieveContractBytecode
+    }
+    class RetrieveContractBytecodePath {
+        address: Address
+    }
+    class RetrieveStoragePositionValue {
+        path: RetrieveStoragePositionValuePath
+        askTo(httpClient: HttpClient) Promise~ThorResponse~GetStorageResponse~~
+        of(address: Address, key: BlockId) RetrieveStoragePositionValue$
+    }
+    class RetrieveStoragePositionValuePath {
+        address: Address
+        key: BlockId
+    }
+    Clause --> "new - toJSON" ClauseJSON
+    ContractBytecode --> "new - toJSON" ContractBytecodeJSON
+    Event --> "new - toJSON" EventJSON
+    ExecuteCodeResponse *--> Event
+    ExecuteCodeResponse *--> Transfer
+    ExecuteCodeResponse --> "new - toJSON" ExecuteCodeResponseJSON
+    ExecuteCodeResponseJSON *--> EventJSON
+    ExecuteCodeResponseJSON *--> TransferJSON
+    ExecuteCodeResponseJSON --|> Array
+    ExecuteCodesRequest *--> Clause
+    ExecuteCodesRequest --> "new - toJSON" ExecuteCodesRequestJSON
+    ExecuteCodesRequestJSON *--> ClauseJSON
+    ExecuteCodesResponse *--> "ExecuteCodeResponseJSON[]" ExecuteCodeResponse
+    ExecuteCodesResponse --> "new - toJSON" ExecuteCodesResponseJSON
+    ExecuteCodesResponse --|> Array 
+    ExecuteCodesResponseJSON *--> "ExecuteCodeResponseJSON[]" ExecuteCodeResponseJSON
+    GetAccountResponse --> "new - toJSON" GetAccountResponseJSON
+    GetStorageResponse --> "new - toJSON" GetStorageResponseJSON
+    HttpClient --> "get - post" HttpPath
+    HttpPath <|.. RetrieveAccountDetailsPath
+    HttpPath <|.. RetrieveContractBytecodePath
+    HttpPath <|.. RetrieveStoragePositionValuePath
+    InspectClauses --> "askTo" ExecuteCodesResponse
+    RetrieveAccountDetails *--> RetrieveAccountDetailsPath
+    RetrieveAccountDetails --> "askTo" GetAccountResponse
+    RetrieveContractBytecode *--> RetrieveContractBytecodePath
+    RetrieveContractBytecode --> "askTo" ContractBytecode
+    RetrieveStoragePositionValue *--> RetrieveStoragePositionValuePath
+    RetrieveStoragePositionValue --> "askTo" GetStorageResponse
+    ThorRequest <--* ThorResponse
+    ThorRequest <|.. InspectClauses
+    ThorRequest <|.. RetrieveAccountDetails
+    ThorRequest <|.. RetrieveContractBytecode
+    ThorRequest <|.. RetrieveStoragePositionValue
+    ThorResponse <-- "askTo" InspectClauses
+    ThorResponse <-- "askTo" RetrieveAccountDetails
+    ThorResponse <-- "askTo" RetrieveContractBytecode
+    ThorResponse <-- "askTo" RetrieveStoragePositionValue
+    Transfer --> "new - toJSON" TransferJSON
+```
diff --git a/docs/diagrams/v2/net/thor/blocks/blocks.md b/docs/diagrams/v2/net/thor/blocks/blocks.md
new file mode 100644
index 000000000..1b7c16e9a
--- /dev/null
+++ b/docs/diagrams/v2/net/thor/blocks/blocks.md
@@ -0,0 +1,84 @@
+```mermaid
+classDiagram
+    namespace http {
+        class HttpClient {
+            <<interface>>
+            get(httpPath: HttpPath) Promise~Response~
+            post(httpPath: HttpPath, body?: unknown) Promise~Response~
+        }
+        class HttpPath {
+            <<interface>>
+            path: string
+        }
+    }
+    namespace thor {
+        class ThorRequest~RequestClass~ {
+            <<interface>>
+            askTo(httpClient: HttpClient Promise~ThorResponse~ResponseClass~~;
+        }
+        class ThorResponse~ResponseClass~ {
+            <<interface>>
+            request: ThorRequest~RequestClass~
+            response: ResponseClass
+        }
+    }
+    class RegularBlockResponse {
+        number: UInt
+        id: ThorId
+        size: UInt
+        parentID: ThorId
+        timestamp: bigint
+        gasLimit: VTHO
+        beneficiary: Address
+        gasUsed: VTHO
+        totalScore: UInt
+        txsRoot: ThorId
+        txsFeatures: UInt
+        stateRoot: ThorId
+        receiptsRoot: ThorId
+        com: boolean
+        signer: Address
+        isTrunk: boolean
+        isFinalized: boolean
+        transactions: ThorId[]
+        constructor(json: RegularBlockResponseJSON) RegularBlockResponse
+        toJSON() RegularBlockResponseJSON
+    }
+    class RegularBlockResponseJSON {
+        <<interface>>
+        number: number
+        id: string
+        size: number
+        parentID: string
+        timestamp: bigint
+        gasLimit: number
+        beneficiary: string
+        gasUsed: number
+        totalScore: number
+        txsRoot: string
+        txsFeatures: number
+        stateRoot: string
+        receiptsRoot: string
+        com: boolean
+        signer: string
+        isTrunk: boolean
+        isFinalized: boolean
+        transactions: string[]
+    }
+    class RetrieveBlock {
+        path: RetrieveBlockPath
+        askTo(httpClient: HttpClient) Promise~ThorResponse~RegularBlockResponse~~
+        of(revision: Revision) RetrieveBlock$
+    }
+    class RetrieveBlockPath {
+        revision: Revision
+    }
+    ThorResponse <-- "askTo" RetrieveBlock
+    ThorRequest <|.. RetrieveBlock
+    ThorRequest <--* ThorResponse
+    HttpPath <|.. RetrieveBlockPath
+    RetrieveBlock --> "askTo" RegularBlockResponse
+    RetrieveBlock *--> RetrieveBlockPath
+    RegularBlockResponse --> "new - toJSON" RegularBlockResponseJSON
+    HttpClient --> "get - post" HttpPath
+```
diff --git a/docs/diagrams/v2/net/thor/debug/debug.md b/docs/diagrams/v2/net/thor/debug/debug.md
new file mode 100644
index 000000000..f18a551b6
--- /dev/null
+++ b/docs/diagrams/v2/net/thor/debug/debug.md
@@ -0,0 +1,205 @@
+```mermaid
+classDiagram
+    namespace JS {
+        class unknown {
+            <<type>>
+        }
+    }
+    namespace http {
+        class HttpClient {
+            <<interface>>
+            get(httpPath: HttpPath) Promise~Response~
+            post(httpPath: HttpPath, body?: unknown) Promise~Response~
+        }
+        class HttpPath {
+            <<interface>>
+            path: string
+        }
+    }
+    namespace thor {
+        class ThorRequest~RequestClass~ {
+            <<interface>>
+            askTo(httpClient: HttpClient Promise~ThorResponse~ResponseClass~~
+        }
+        class ThorResponse~ResponseClass~ {
+            <<interface>>
+            request: ThorRequest~RequestClass~
+            response: ResponseClass
+        }
+    }
+    class Bigram {
+        NAME: string$
+    }
+    class Call {
+        NAME: string$
+    }
+    class EvmDis {
+        NAME: string$
+    }
+    class FourByte {
+        NAME: string$
+    }
+    class Noop {
+        NAME: string$
+    }
+    class Null {
+        NAME: string$
+    }
+    class OpCount {
+        NAME: string$
+    }
+    class PostDebugTracerCallRequest {
+        name?: TracerName
+        config?: unknown
+        value: VET
+        data: HexUInt
+        to?: Address
+        gas?: VTHO
+        gasPrice?: VTHO
+        caller?: Address
+        provedWork?: string
+        gasPayer?: Address
+        expiration?: UInt
+        blockRef?: BlockRef
+        constructor(json: PostDebugTracerCallRequestJSON) PostDebugTracerCallRequest
+        toJSON() PostDebugTracerCallRequestJSON
+    }
+    class PostDebugTracerCallRequestJSON {
+        <<interface>>
+        name?: string
+        config?: unknown
+        value: string
+        data: string
+        to?: string
+        gas?: number
+        gasPrice?: string
+        caller?: string
+        provedWork?: string
+        gasPayer?: string
+        expiration?: number
+        blockRef?: string
+    }
+    class PostDebugTracerRequest {
+        name?: TracerName
+        config?: unknown
+        target: string
+        constructor(json: PostDebugTracerRequestJSON) PostDebugTracerRequest
+        toJSON() PostDebugTracerRequestJSON
+    }
+    class PostDebugTracerRequestJSON {
+        <<interface>>
+        name?: string
+        config?: unknown
+        target: string
+    }
+    class Presate {
+        NAME: string$
+    }
+    class RetrieveStorageRange {
+        PATH: HttpPath$
+        request: StorageRangeOption
+        askTo(httpClient: HttpClient): Promise~ThorResponse~StorageRange~~
+        of(request: StorageRangeOptionJSON) RetrieveStorageRange$
+    }
+    class StorageRange {
+        nextKey?: ThorId
+        storage: unknown
+        constructor(json: StorageRangeJSON) StorageRange
+        toJSON() StorageRangeJSON
+    }
+    class StorageRangeJSON {
+        <<interface>>
+        nextKey?: string
+        storage: unknown
+    }
+    class StorageRangeOption {
+        address: Address
+        keyStart?: ThorId
+        maxResult?: UInt
+        target: string
+    }
+    class StorageRangeOptionJSON {
+        <<interface>>
+        address: string
+        keyStart?: string
+        maxResult?: number
+        target: string
+    }
+    class StructLogger {
+        NAME: string$
+    }
+    class TraceCall {
+        PATH: HttpPath$
+        request: PostDebugTracerCallRequest
+        askTo(httpClient: HttpClient): Promise~ThorResponse~unknown~~
+        of(request: PostDebugTracerCallRequestJSON) TraceCall$
+    }
+    class Tracer {
+        of(name: string): TracerName$
+    }
+    class TracerName {
+        <<abstract>>
+        toString: string
+    }
+    class TraceTransactionClause {
+        PATH: HttpPath$
+        request: PostDebugTracerRequest
+        askTo(httpClient: HttpClient): Promise~ThorResponse~unknown~~
+        of(request: PostDebugTracerRequestJSON) TraceTransactionClause$
+    }
+    class Trigram {
+        NAME: string$
+    }
+    class Unigram {
+        NAME: string$
+    }
+    Bigram <-- "of" Tracer
+    Call <-- "of" Tracer
+    EvmDis <-- "of" Tracer
+    FourByte <-- "of" Tracer
+    HttpClient --> "get - post" HttpPath
+    HttpPath <--* RetrieveStorageRange
+    HttpPath <--* TraceCall
+    HttpPath <--* TraceTransactionClause
+    Noop <-- "of" Tracer
+    Null <-- "of" Tracer
+    OpCount <-- "of" Tracer
+    PostDebugTracerCallRequest *--> TracerName
+    PostDebugTracerCallRequest --> "new - toJSON" PostDebugTracerCallRequestJSON
+    PostDebugTracerRequest *--> TracerName
+    PostDebugTracerRequest --> "new - toJSON" PostDebugTracerRequestJSON
+    Presate <-- "of" Tracer
+    Prestate <-- "of" Tracer
+    RetrieveStorageRange *--> StorageRangeOption
+    RetrieveStorageRange --> "askTo" StorageRange
+    RetrieveStorageRange --> "of" StorageRangeOptionJSON
+    StorageRange --> "new - toJSON" StorageRangeJSON
+    StorageRangeOption --> "new - toJSON" StorageRangeOptionJSON
+    StructLogger <-- "of" Tracer
+    ThorRequest <--* ThorResponse
+    ThorRequest <|.. RetrieveStorageRange
+    ThorRequest <|.. TraceCall
+    ThorRequest <|.. TraceTransactionClause
+    ThorResponse <-- "askTo" RetrieveStorageRange
+    ThorResponse <-- "askTo" TraceCall
+    TraceCall *--> PostDebugTracerCallRequest
+    TraceCall --> "askTo" unknown
+    TraceCall --> "of" PostDebugTracerCallRequestJSON
+    TraceTransactionClause *--> PostDebugTracerCallRequest
+    TraceTransactionClause --> "askTo" unknown
+    TraceTransactionClause --> "of" PostDebugTracerRequestJSON
+    TracerName <|-- Bigram
+    TracerName <|-- Call
+    TracerName <|-- EvmDis
+    TracerName <|-- FourByte
+    TracerName <|-- Noop
+    TracerName <|-- Null
+    TracerName <|-- OpCount
+    TracerName <|-- Presate
+    TracerName <|-- Prestate
+    TracerName <|-- StructLogger
+    TracerName <|-- Trigram
+    TracerName <|-- Unigram
+    Trigram <-- "of" Tracer
+    Unigram <-- "of" Tracer
+```
diff --git a/docs/diagrams/v2/net/thor/logs/logs.md b/docs/diagrams/v2/net/thor/logs/logs.md
new file mode 100644
index 000000000..752be6233
--- /dev/null
+++ b/docs/diagrams/v2/net/thor/logs/logs.md
@@ -0,0 +1,230 @@
+```mermaid
+classDiagram
+    namespace JS {
+        class Array~Type~ {
+            <<type>>
+        }
+    }
+    namespace http {
+        class HttpClient {
+            <<interface>>
+            get(httpPath: HttpPath) Promise~Response~
+            post(httpPath: HttpPath, body?: unknown) Promise~Response~
+        }
+        class HttpPath {
+            <<interface>>
+            path: string
+        }
+    }
+    namespace thor {
+        class ThorRequest~RequestClass~ {
+            <<interface>>
+            askTo(httpClient: HttpClient Promise~ThorResponse~ResponseClass~~
+        }
+        class ThorResponse~ResponseClass~ {
+            <<interface>>
+            request: ThorRequest~RequestClass~
+            response: ResponseClass
+        }
+    }
+    class EventCriteria {
+        address?: Address
+        topic0?: ThorId
+        topic1?: ThorId
+        topic2?: ThorId
+        topic3?: ThorId
+        topic4?: ThorId
+        constructor(json: EventCriteriaJSON) EventCriteria
+        toJSON() EventCriteriaJSON
+    }
+    class EventCriteriaJSON {
+        <<interface>>
+        address?: string
+        topic0?: string
+        topic1?: string
+        topic2?: string
+        topic3?: string
+        topic4?: string
+    }
+    class EventLogFilterRequest {
+        range?: FilterRange
+        options?: FilterOptions
+        criteriaSet?: EventCriteria[]
+        order?: LogSort
+        constructor(json: EventLogFilterRequestJSON) EventLogFilterRequest
+        toJSON() EventLogFilterRequestJSON
+    }
+    class EventLogFilterRequestJSON {
+        range?: FilterRangeJSON
+        options?: FilterOptionsJSON
+        criteriaSet?: EventCriteriaJSON[]
+        order?: string
+    }
+    class EventLogResponse {
+        address: Address
+        topics: ThorId[]
+        data: HexUInt
+        meta: LogMeta
+        constructor(json: EventLogResponseJSON) EventLogResponse
+        toJSON() EventLogResponseJSON
+    }
+    class EventLogResponseJSON {
+        <<interface>>
+        address: string
+        topics: string[]
+        data: string
+        meta: LogMetaJSON
+    }
+    class EventLogsResponse {
+        constructor(json: EventLogsResponseJSON) EventLogsResponse
+    }
+    class EventLogsResponseJSON {
+        <<interface>>
+    }
+    class FilterOptions {
+        limit?: UInt
+        offset?: UInt
+        constructor(json: FilterOptionsJSON) FilterOptions
+        toJSON() FilterOptionsJSON
+    }
+    class FilterOptionsJSON {
+        limit?: number
+        offset?: number
+    }
+    class LogMeta {
+        blockID: BlockId
+        blockNumber: UInt
+        blockTimestamp: UInt
+        txID: TxId
+        txOrigin: Address
+        clauseIndex: UInt
+        constructor(json: LogMetaJSON)
+        toJSON() LogMetaJSON
+    }
+    class LogMetaJSON {
+        <<interface>>
+        blockID: string
+        blockNumber: number
+        blockTimestamp: number
+        txID: string
+        txOrigin: string
+        clauseIndex: number
+    }
+    class LogSort {
+        <<enumeration>>
+        asc: string
+        desc: string
+    }
+    class FilterRange {
+        unit?: FilterRangeUnits
+        from?: UInt
+        to?: UInt
+        constructor(json: FilterRangeJSON) FilterRange
+        toJSON() FilterRangeJSON
+    }
+    class FilterRangeJSON {
+        unit?: string
+        from?: number
+        to?: number
+    }
+    class FilterRangeUnits {
+        <<enumeration>>
+        block = 'block',
+        time = 'time'
+    }
+    class QuerySmartContractEvents {
+        PATH: HttpPath$
+        request: EventLogFilterRequest
+        constructor(request: EventLogFilterRequest) QuerySmartContractEvents
+        askTo(httpClient: HttpClient) Promise~ThorResponse~EventLogsResponse~~
+        static of(request: EventLogFilterRequestJSON) QuerySmartContractEvents$
+    }
+    class QueryVETTransferEvents {
+        PATH: HttpPath$
+        request: TransferLogFilterRequest
+        constructor(request: TransferLogFilterRequest) QueryVETTransferEvents
+        askTo(httpClient: HttpClient) Promise~ThorResponse~TransferLogsResponse~~
+        of(request: TransferLogFilterRequestJSON) QueryVETTransferEvents$
+    }
+    class TransferCriteria {
+        txOrigin?: Address
+        sender?: Address
+        recipient?: Address
+        constructor(json: TransferCriteriaJSON) TransferCriteria
+        toJSON() TransferCriteriaJSON
+    }
+    class TransferCriteriaJSON {
+        txOrigin?: string
+        sender?: string
+        recipient?: string
+    }
+    class TransferLogFilterRequest {
+        range?: FilterRange
+        options?: FilterOptions
+        criteriaSet?: TransferCriteria[]
+        order?: LogSort
+        constructor(json: TransferLogFilterRequestJSON) TransferLogFilterRequest
+        toJSON() TransferLogFilterRequestJSON
+    }
+    class TransferLogFilterRequestJSON {
+        range?: FilterRangeJSON
+        options?: FilterOptionsJSON
+        criteriaSet?: TransferCriteriaJSON[]
+        order?: string
+    }
+    class TransferLogResponse {
+        sender: Address
+        recipient: Address
+        amount: VET
+        meta: LogMeta
+        constructor(json: TransferLogResponseJSON) TransferLogResponse
+        toJSON() TransferLogResponseJSON
+    }
+    class TransferLogResponseJSON {
+        sender: string
+        recipient: string
+        amount: string
+        meta: LogMetaJSON
+    }
+    EventCriteria --> "new - toJSON" EventCriteriaJSON
+    EventLogFilterRequest *--> EventCriteria
+    EventLogFilterRequest *--> LogSort
+    EventLogFilterRequest --> "new - toJSON" EventLogFilterRequestJSON
+    EventLogFilterRequestJSON *--> EventCriteriaJSON
+    EventLogResponse *--> LogMeta
+    EventLogResponse --> "new - toJSON" EventLogResponseJSON
+    EventLogResponseJSON *--> LogMetaJSON
+    EventLogsResponse *--> EventLogResponse
+    EventLogsResponse --|> Array
+    EventLogsResponseJSON *--> EventLogResponseJSON
+    EventLogsResponseJSON --|> Array
+    FilterOptions --> "new - toJSON" FilterOptionsJSON
+    FilterRange *--> FilterRangeUnits
+    FilterRange --> "new - toJSON" FilterRangeJSON
+    HttpClient --> "get - post" HttpPath
+    HttpPath <--* QuerySmartContractEvents
+    HttpPath <--* QueryVETTransferEvents
+    LogMeta --> "new - toJSON" LogMetaJSON
+    QuerySmartContractEvents *--> EventLogFilterRequest
+    QuerySmartContractEvents --> "askTo" EventLogsResponse
+    QueryVETTransferEvents *--> TransferLogFilterRequest
+    QueryVETTransferEvents --> "askTo" TransferLogsResponse
+    ThorRequest <--* ThorResponse
+    ThorRequest <|.. QuerySmartContractEvents
+    ThorRequest <|.. QueryVETTransferEvents
+    ThorResponse <-- "askTo" QuerySmartContractEvents
+    ThorResponse <-- "askTo" QueryVETTransferEvents
+    TransferCriteria --> "new - toJSON" TransferCriteriaJSON
+    TransferLogFilterRequest *--> FilterOptions
+    TransferLogFilterRequest *--> FilterRange
+    TransferLogFilterRequest *--> LogSort
+    TransferLogFilterRequest *--> TransferCriteria
+    TransferLogFilterRequest --> "new - toJSON" TransferLogFilterRequestJSON
+    TransferLogResponse *--> LogMeta
+    TransferLogResponse --> "new - toJSON" TransferLogResponseJSON
+    TransferLogsResponse *--> TransferLogResponse
+    TransferLogsResponse --> "new - toJSON" TransferLogsResponseJSON
+    TransferLogsResponse --|> Array
+    TransferLogsResponseJSON *--> TransferLogResponseJSON
+    TransferLogsResponseJSON --|> Array
+```
diff --git a/docs/diagrams/v2/net/thor/node/node.md b/docs/diagrams/v2/net/thor/node/node.md
new file mode 100644
index 000000000..9e59c80be
--- /dev/null
+++ b/docs/diagrams/v2/net/thor/node/node.md
@@ -0,0 +1,71 @@
+```mermaid
+classDiagram
+    namespace JS {
+        class Array~Type~ {
+            <<type>>
+        }
+    }
+    namespace http {
+        class HttpClient {
+            <<interface>>
+            get(httpPath: HttpPath) Promise~Response~
+            post(httpPath: HttpPath, body?: unknown) Promise~Response~
+        }
+        class HttpPath {
+            <<interface>>
+            path: string
+        }
+    }
+    namespace thor {
+        class ThorRequest~RequestClass~ {
+            <<interface>>
+            askTo(httpClient: HttpClient Promise~ThorResponse~ResponseClass~~
+        }
+        class ThorResponse~ResponseClass~ {
+            <<interface>>
+            request: ThorRequest~RequestClass~
+            response: ResponseClass
+        }
+    }
+    class GetPeersResponse {
+        constructor(json: GetPeersResponseJSON) GetPeersResponse
+    }
+    class GetPeersResponseJSON {
+        <<interface>>
+    }
+    class PeerStat {
+        name: string
+        bestBlockID: BlockId
+        totalScore: UInt
+        peerID: string
+        netAddr: string
+        inbound: boolean
+        duration: UInt
+        constructor(json: PeerStatJSON) PeerStat
+        toJSON() PeerStatJSON
+    }
+    class PeerStatJSON {
+        name: string
+        bestBlockID: string
+        totalScore: number
+        peerID: string
+        netAddr: string
+        inbound: boolean
+        duration: number
+    }
+    class RetrieveConnectedPeers {
+        PATH: HttpPath$
+        askTo(httpClient: HttpClient) Promise~ThorResponse~GetPeersResponse~~
+    }
+    GetPeersResponse *--> PeerStat
+    GetPeersResponse --> "new - toJSON" GetPeersResponseJSON
+    GetPeersResponse --|> Array
+    GetPeersResponseJSON --|> Array
+    GetPeersResponseJSON *--> PeerStatJSON
+    HttpClient --> "get - post" HttpPath
+    HttpPath <--* RetrieveConnectedPeers
+    PeerStat --> "new - toJSON" PeerStatJSON
+    RetrieveConnectedPeers --> "askTo" GetPeersResponse
+    ThorRequest <-- "askTo" RetrieveConnectedPeers
+    ThorResponse <|.. RetrieveConnectedPeers
+```
diff --git a/docs/diagrams/v2/net/thor/subscriptions/subscriptions.md b/docs/diagrams/v2/net/thor/subscriptions/subscriptions.md
new file mode 100644
index 000000000..f9484eac0
--- /dev/null
+++ b/docs/diagrams/v2/net/thor/subscriptions/subscriptions.md
@@ -0,0 +1,215 @@
+```mermaid
+classDiagram
+    namespace log {
+        class LogMeta {
+            blockID: BlockId
+            blockNumber: UInt
+            blockTimestamp: UInt
+            txID: TxId
+            txOrigin: Address
+            clauseIndex: UInt
+            constructor(json: LogMetaJSON) LogMeta
+            toJSON() LogMetaJSON
+        }
+        class LogMetaJSON {
+            blockID: string
+            blockNumber: number
+            blockTimestamp: number
+            txID: string
+            txOrigin: string
+            clauseIndex: number
+        }
+    }
+    namespace trasactions {
+        class TXID {
+            id: ThorId
+            constructor(json: TXIDJSON): TXID
+            toJSON() TXIDJSON
+        }
+        class TXIDJSON {
+            <<interface>>
+            id: string
+        }
+    }
+    namespace ws {
+        class WebSocketClient {
+            <<interface>>
+            baseURL: string
+            addMessageListener(listener: WebSocketListener)
+            close(): WebSocketClient
+            open(path: HttpPath): WebSocketClient
+            removeListener(listener: WebSocketListener): WebSocketClient
+        }
+        class WebSocketListener~EventType~ {
+            <<interface>>
+            onClose(event: Event)
+            onError(event: Event)
+            onMessage(event: MessageEvent~EventType~)
+            onOpen(event: Event)
+        }
+    }
+    class BeatsSubscription {
+        PATH: HttpPath$
+        addMessageListener(listener: WebSocketListener~SubscriptionBeat2Response~) BeatsSubscription
+        at(wsc: WebSocketClient) BeatsSubscription$
+        close() BeatsSubscription
+        open() BeatsSubscription
+        removeListener(listener: WebSocketListener~SubscriptionBeat2Response~) BeatsSubscription
+    }
+    class BlocksSubscription {
+        PATH: HttpPath$
+        addMessageListener(listener: WebSocketListener~SubscriptionBlockResponse~) BlocksSubscription
+        at(wsc: WebSocketClient) BlocksSubscription$
+        atPos(pos?: BlockId)
+        close() BlocksSubscription
+        open() BlocksSubscription
+        removeListener(listener: WebSocketListener~SubscriptionBlockResponse~) BlocksSubscription
+    }
+    class EventsSubscription {
+        PATH: HttpPath$
+        addMessageListener(listener: WebSocketListener~SubscriptionEventResponse~) EventsSubscription
+        at(wsc: WebSocketClient) EventsSubscription$
+        atPos(pos?: ThorId) EventsSubscription
+        close() EventsSubscription
+        open() EventsSubscription
+        removeListener(listener: WebSocketListener~SubscriptionEventResponse~) EventsSubscription
+        withContractAddress(contractAddress?: Address) EventsSubscription
+        withFilters(t0?: ThorId?, t1?: ThorId, t2: ThorId, t3: ThorId) EventsSubscription
+    }
+    class NewTransactionSubscription {
+        PATH: HttpPath$
+        addMessageListener(listener: WebSocketListener~TXID~) NewTransactionSubscription
+        at(wsc: WebSocketClient) NewTransactionSubscription$
+        close() NewTransactionSubscription
+        open() NewTransactionSubscription
+    }
+    class TransfersSubscription {
+        PATH: HttpPath$
+        addMessageListener(listener: WebSocketListener~SubscriptionTransferResponse~) TransfersSubscription
+        at(wsc: WebSocketClient) TransfersSubscription
+        close() TransfersSubscription
+        open() TransfersSubscription
+        removeListener(listener: WebSocketListener~SubscriptionTransferResponse~) TransfersSubscription
+    }
+    class SubscriptionBeat2Response {
+        gasLimit: VTHO
+        obsolete: boolean
+        number: UInt
+        id: BlockId
+        parentID: BlockId
+        timestamp: UInt
+        txsFeatures: UInt
+        bloom: HexUInt
+        k: UInt
+        constructor(json: SubscriptionBeat2ResponseJSON): SubscriptionBeat2Response
+        toJSON() SubscriptionBeat2ResponseJSON
+    }
+    class SubscriptionBeat2ResponseJSON {
+        <<interface>>
+        gasLimit: number
+        obsolete: boolean
+        number: number
+        id: string
+        parentID: string
+        timestamp: number
+        txsFeatures: number
+        bloom: string
+        k: number
+    }
+    class SubscriptionBlockResponse {
+        number: UInt
+        id: BlockId
+        size: UInt
+        parentID: BlockId
+        timestamp: UInt
+        gasLimit: VTHO
+        beneficiary: Address
+        gasUsed: VTHO
+        totalScore: UInt
+        txsRoot: ThorId
+        txsFeatures: UInt
+        stateRoot: ThorId
+        receiptsRoot: ThorId
+        com: boolean
+        signer: Address
+        obsolete: boolean
+        transactions: TxId[]
+    }
+    class SubscriptionBlockResponseJSON {
+        <<interface>>
+        number: number
+        id: string
+        size: number
+        parentID: string
+        timestamp: number
+        gasLimit: number
+        beneficiary: string
+        gasUsed: number
+        totalScore: number
+        txsRoot: string
+        txsFeatures: number
+        stateRoot: string
+        receiptsRoot: string
+        com: boolean
+        signer: string
+        obsolete: boolean
+        transactions: string[]
+    }
+    class SubscriptionEventResponse {
+        address: Address;
+        topics: ThorId[];
+        data: HexUInt;
+        obsolete: boolean;
+        meta: LogMeta;
+        constructor(json: SubscriptionEventResponseJSON) SubscriptionEventResponse
+        toJSON() SubscriptionEventResponseJSON
+    }
+    class SubscriptionEventResponseJSON {
+        <<interface>>
+        address: string
+        topics: string[]
+        data: string
+        obsolete: boolean
+        meta: LogMetaJSON
+    }
+    class SubscriptionTransferResponse {
+        sender: Address
+        recipient: Address
+        amount: VET
+        obsolete: boolean
+        meta: LogMeta
+    }
+    class SubscriptionTransferResponseJSON {
+        <<interface>>
+        sender: string
+        recipient: string
+        amount: string
+        obsolete: boolean
+        meta: LogMetaJSON
+    }
+    WebSocketClient <|.. BeatsSubscription
+    WebSocketClient <|.. BlocksSubscription
+    WebSocketClient <|.. EventsSubscription
+    WebSocketClient <|.. NewTransactionSubscription
+    WebSocketClient <|.. TransfersSubscription
+    WebSocketListener <|.. BeatsSubscription
+    WebSocketListener <|.. BlocksSubscription
+    WebSocketListener <|.. EventsSubscription
+    WebSocketListener <|.. NewTransactionSubscription
+    WebSocketListener <|.. TransfersSubscription
+    BeatsSubscription --> "onMessage" SubscriptionBeat2Response
+    BlocksSubscription --> "onMessage" SubscriptionBlockResponse
+    EventsSubscription --> "onMessage" SubscriptionEventResponse
+    NewTransactionSubscription --> "onMessage" TXID
+    TransfersSubscription --> "onMessage" SubscriptionTransferResponse
+    SubscriptionEventResponse *--> LogMeta
+    SubscriptionEventResponseJSON *--> LogMetaJSON
+    SubscriptionTransferResponse *--> LogMeta
+    SubscriptionTransferResponseJSON *-- LogMetaJSON
+    LogMeta --> "new - toJSON" LogMetaJSON
+    SubscriptionBeat2Response --> "new - toJSON" SubscriptionBeat2ResponseJSON
+    SubscriptionBlockResponse --> "new - toJSON" SubscriptionBlockResponseJSON
+    SubscriptionEventResponse --> "new - toJSON" SubscriptionEventResponseJSON
+    SubscriptionTransferResponse --> "new - toJSON" SubscriptionTransferResponseJSON
+    TXID --> "new - toJSON" TXIDJSON
+```
diff --git a/docs/diagrams/v2/net/thor/thor.md b/docs/diagrams/v2/net/thor/thor.md
new file mode 100644
index 000000000..fe72d1385
--- /dev/null
+++ b/docs/diagrams/v2/net/thor/thor.md
@@ -0,0 +1,18 @@
+```mermaid
+classDiagram
+    class ThorNetworks {
+        <<enumeration>>
+        MAINNET: string
+        TESTNET: string
+    }
+    class ThorRequest~RequestClass~ {
+        <<interface>>
+        askTo(httpClient: HttpClient Promise~ThorResponse~ResponseClass~~;
+    }
+    class ThorResponse~ResponseClass~ {
+        <<interface>>
+        request: ThorRequest~RequestClass~
+        response: ResponseClass
+    }
+    ThorRequest <--* ThorResponse
+```
diff --git a/docs/diagrams/v2/net/thor/transactions/transactions.md b/docs/diagrams/v2/net/thor/transactions/transactions.md
new file mode 100644
index 000000000..ae2d308f9
--- /dev/null
+++ b/docs/diagrams/v2/net/thor/transactions/transactions.md
@@ -0,0 +1,281 @@
+```mermaid
+classDiagram
+    namespace http {
+        class HttpClient {
+            <<interface>>
+            get(httpPath: HttpPath) Promise~Response~
+            post(httpPath: HttpPath, body?: unknown) Promise~Response~
+        }
+        class HttpPath {
+            <<interface>>
+            path: string
+        }
+        class HttpQuery {
+            <<interface>>
+            query(): string;
+        }
+    }
+    namespace thor {
+        class ThorRequest~RequestClass~ {
+            <<interface>>
+            askTo(httpClient: HttpClient Promise~ThorResponse~ResponseClass~~
+            of(txId: TxId) RetrieveTransactionByID$
+            withHead(head?: BlockId) RetrieveTransactionByID
+            withPending(pending: boolean) RetrieveTransactionByID
+        }
+        class ThorResponse~ResponseClass~ {
+            <<interface>>
+            request: ThorRequest~RequestClass~
+            response: ResponseClass
+        }
+    }
+    class Clause {
+        to?: Address | null
+        value: VET
+        data: HexUInt
+        constructor(json: ClauseJSON) Clause
+        toJSON() ClauseJSON
+    }
+    class ClauseJSON {
+        to?: string | null
+        value: string
+        data: string
+    }
+    class Event {
+        address: Address
+        topics: ThorId[]
+        data: HexUInt
+        constructor(json: EventJSON) Event
+        toJSON() EventJSON
+    }
+    class EventJSON {
+        address: string
+        topics: string[]
+        data: string
+    }
+    class GetRawTxResponse {
+        raw: HexUInt
+        meta: TxMeta
+        constructor(json: GetRawTxResponseJSON) GetRawTxResponse
+        toJSON() GetRawTxResponseJSON
+    }
+    class GetRawTxResponseJSON {
+        raw: string
+        meta: TxMetaJSON
+    }
+    class GetTxReceiptResponse {
+        meta: ReceiptMeta
+        constructor(json: GetTxReceiptResponseJSON) GetTxReceiptResponse
+        toJSON() GetTxReceiptResponseJSON
+    }
+    class GetTxReceiptResponseJSON {
+        meta: ReceiptMetaJSON
+    }
+    class GetTxResponse {
+        id: TxId
+        origin: Address
+        delegator: Address | null
+        size: UInt
+        chainTag: UInt
+        blockRef: BlockId
+        expiration: UInt
+        clauses: Clause[]
+        gasPriceCoef: UInt
+        gas: VTHO
+        dependsOn?: TxId
+        nonce: Nonce
+        meta: TxMeta
+        constructor(json: GetTxResponseJSON) GetTxResponse
+        toJSON() GetTxResponseJSON
+    }
+    class GetTxResponseJSON {
+        id: string
+        origin: string
+        delegator: string | null
+        size: number
+        chainTag: number
+        blockRef: string
+        expiration: number
+        clauses: ClauseJSON[]
+        gasPriceCoef: number
+        gas: number
+        dependsOn?: string
+        nonce: string
+        meta: TxMetaJSON
+    }
+    class TxMeta {
+        blockID: BlockId
+        blockNumber: UInt
+        blockTimestamp: bigint
+        constructor(json: TxMetaJSON) TxMeta
+        toJSON() TxMetaJSON
+    }
+    class TxMetaJSON {
+        blockID: string
+        blockNumber: number
+        blockTimestamp: bigint
+    }
+    class Receipt {
+        gasUsed: VTHO
+        gasPayer: Address
+        paid: VTHO
+        reward: VTHO
+        reverted: boolean
+        outputs: ReceiptOutput[]
+        constructor(json: ReceiptJSON) Receipt
+        toJSON() ReceiptJSON
+    }
+    class ReceiptJSON {
+        gasUsed: number
+        gasPayer: string
+        paid: string
+        reward: string
+        reverted: boolean
+        outputs: ReceiptOutputJSON[]
+    }
+    class ReceiptMeta {
+        txID: TxId
+        txOrigin: Address
+        constructor(json: ReceiptMetaJSON) ReceiptMeta
+        toJSON() ReceiptMetaJSON
+    }
+    class ReceiptMetaJSON {
+        txID: string
+        txOrigin: string
+    }
+    class ReceiptOutput {
+        contractAddress: Address
+        events: Event[]
+        transfers: Transfer[]
+        constructor(json: ReceiptOutputJSON) ReceiptOutput
+        toJSON() ReceiptOutputJSON
+    }
+    class ReceiptOutputJSON {
+        contractAddress: string
+        events: EventJSON[]
+        transfers: TransferJSON[]
+    }
+    class RetrieveRawTransactionByID {
+        path: RetrieveRawTransactionByIDPath
+        query: RetrieveRawTransactionByIDQuery
+        askTo(httpClient: HttpClient) Promise~ThorResponse~
+        of(txId: TxId) RetrieveRawTransactionByID$
+        withHead(head?: BlockId) RetrieveTransactionByID$
+        withPending(pending: boolean) RetrieveTransactionByID$
+    }
+    class RetrieveTransactionByID {
+        path: RetrieveTransactionByIDPath
+        query: RetrieveTransactionByIDQuery
+        askTo(httpClient: HttpClient) Promise~ThorRespons~GetTxResponse~~
+    }
+    class RetrieveTransactionByIDPath {
+        txId: TxId
+    }
+    class RetrieveTransactionByIDQuery {
+        head?: BlockId
+        pending: boolean
+    }
+    class RetrieveTransactionReceipt {
+        path: RetrieveTransactionReceiptPath
+        query: RetrieveTransactionReceiptQuery
+        askTo(httpPath: HttpPath) Promise~ThorResponse~GetTxReceiptResponse~~
+        of(txId: TxId) RetrieveTransactionReceipt$
+        withHead(head?: BlockId) RetrieveTransactionReceipt
+    }
+    class RetrieveTransactionReceiptPath {
+        txId: TxId
+    }
+    class RetrieveTransactionReceiptQuery {
+        head?: BlockId
+    }
+    class SendTransaction {
+        PATH: HttpPath$
+        encoded: Uint8Array
+        askTo(httpPath: HttpPath) Promise~ThorResponse~TXID~~
+        of(encoded: Uint8Array) SendTransaction$
+    }
+    class Transfer {
+        sender: Address
+        recipient: Address
+        amount: VET
+        constructor(json: TransferJSON) Transfer
+        toJSON() TransferJSON
+    }
+    class TransferJSON {
+        sender: string
+        recipient: string
+        amount: string
+    }
+    class TXID {
+        id: ThorId
+        constructor(json: TXIDJSON): TXID
+        toJSON() TXIDJSON
+    }
+    class TXIDJSON {
+        <<interface>>
+        id: string
+    }
+    class TxMeta {
+        blockID: BlockId
+        blockNumber: UInt
+        blockTimestamp: bigint
+        constructor(json: TxMetaJSON) TxMeta
+        toJSON() TxMetaJSON
+    }
+    class TxMetaJSON {
+        blockID: string
+        blockNumber: number
+        blockTimestamp: bigint
+    }
+    Clause --> "new - toJSON" ClauseJSON
+    Event --> "new - toJSON" EventJSON
+    GetRawTxResponse *--> TxMeta
+    GetRawTxResponse --> "new - toJSON" GetRawTxResponseJSON
+    GetRawTxResponse <-- "askTo" RetrieveRawTransactionByID
+    GetRawTxResponseJSON *--> TxMetaJSON
+    GetTxReceiptResponse *--> ReceiptMeta
+    GetTxReceiptResponse --> "new - toJSON" GetTxReceiptResponseJSON
+    GetTxReceiptResponseJSON *--> ReceiptMetaJSON
+    GetTxResponse *--> "*" Clause
+    GetTxResponse --> "new - toJSON" GetTxResponseJSON
+    GetTxResponseJSON *--> "*" ClauseJSON
+    HttpPath <--* SendTransaction
+    HttpPath <|.. RetrieveTransactionByIDPath
+    HttpPath <|.. RetrieveTransactionReceiptPath
+    HttpQuery <|.. RetrieveTransactionByIDQuery
+    HttpQuery <|.. RetrieveTransactionReceiptQuery
+    Receipt *--> "*" ReceiptOutput
+    Receipt --> "new - toJSON" ReceiptJSON
+    Receipt <|-- GetTxReceiptResponse
+    ReceiptJSON *--> "*" ReceiptOutputJSON
+    ReceiptJSON <|-- GetTxReceiptResponseJSON
+    ReceiptMeta --> "new - toJSON" ReceiptMetaJSON
+    ReceiptOutput *--> "*" Event
+    ReceiptOutput *--> "*" Transfer
+    ReceiptOutput --> "new - toJSON" ReceiptOutputJSON
+    ReceiptOutputJSON *--> "*" EventJSON
+    ReceiptOutputJSON *--> "*" TransferJSON
+    RetrieveRawTransactionByID *--> RetrieveRawTransactionByIDPath
+    RetrieveRawTransactionByID *--> RetrieveRawTransactionByIDQuery
+    RetrieveRawTransactionByID --> "askTo" GetRawTxResponse
+    RetrieveTransactionByID --> "askTo" GetTxResponse
+    RetrieveTransactionByIDPath <|-- RetrieveRawTransactionByIDPath
+    RetrieveTransactionByIDQuery <|-- RetrieveRawTransactionByIDQuery
+    RetrieveTransactionReceipt *--> RetrieveTransactionReceiptPath
+    RetrieveTransactionReceipt *--> RetrieveTransactionReceiptQuery
+    RetrieveTransactionReceipt --> "askTo" GetTxReceiptResponse
+    SendTransaction --> "askTo" TXID
+    ThorRequest <|.. RetrieveRawTransactionByID
+    ThorRequest <|.. RetrieveTransactionByID
+    ThorRequest <|.. RetrieveTransactionReceipt
+    ThorRequest <|.. SendTransaction
+    ThorResponse <-- "askTo" RetrieveRawTransactionByID
+    ThorResponse <-- "askTo" RetrieveTransactionByID
+    ThorResponse <-- "askTo" RetrieveTransactionReceipt
+    ThorResponse <-- "askTo" SendTransaction
+    Transfer --> "new - toJSON" TransferJSON
+    TXID --> "new - toJSON" TXIDJSON
+    TxMeta --> "new - toJSON" TxMetaJSON
+    TxMeta <|-- ReceiptMeta
+    TxMetaJSON <|-- ReceiptMetaJSON
+```
diff --git a/docs/diagrams/v2/net/ws/ws.md b/docs/diagrams/v2/net/ws/ws.md
new file mode 100644
index 000000000..a23307e2d
--- /dev/null
+++ b/docs/diagrams/v2/net/ws/ws.md
@@ -0,0 +1,23 @@
+```mermaid
+classDiagram
+    class MozillaWebSocketClient {
+        constructor(baseURL: string) MozillaWebSocketClient
+    }
+    class WebSocketClient {
+        <<interface>>
+        baseURL: string
+        addMessageListener(listener: WebSocketListener~EventType~)
+        close(): WebSocketClient
+        open(path: HttpPath): WebSocketClient
+        removeListener(listener: WebSocketListener<unknown>): WebSocketClient
+    }
+    class WebSocketListener~EventType~ {
+        <<interface>>
+        onClose(event: Event)
+        onError(event: Event)
+        onMessage(event: MessageEvent~EventType~)
+        onOpen(event: Event)
+    }
+    WebSocketClient <|.. MozillaWebSocketClient
+    WebSocketListener <--o WebSocketClient
+```
diff --git a/docs/examples/transactions/full-flow-delegator-url.ts b/docs/examples/transactions/full-flow-delegator-url.ts
index 71822faff..7889ed3fa 100644
--- a/docs/examples/transactions/full-flow-delegator-url.ts
+++ b/docs/examples/transactions/full-flow-delegator-url.ts
@@ -129,3 +129,4 @@ expect(delegatedSigned.isDelegated).toEqual(true);
 expect(txReceipt).toBeDefined();
 expect(txReceipt?.gasUsed).toBe(gasResult.totalGas);
 expect(sendTransactionResult.id).toBe(txReceipt?.meta.txID);
+console.log(txReceipt);
diff --git a/packages/core/src/transaction/Transaction.ts b/packages/core/src/transaction/Transaction.ts
index 3e36b9e13..d041c88d3 100644
--- a/packages/core/src/transaction/Transaction.ts
+++ b/packages/core/src/transaction/Transaction.ts
@@ -634,7 +634,7 @@ class Transaction {
      * @remarks Security auditable method, depends on
      * - {@link Address.ofPublicKey}
      * - {@link Secp256k1.isValidPrivateKey};
-     * - {@link Secp256k1.sign}.
+     * - {@link Secp256k1.sign}.l
      */
     public signAsSenderAndGasPayer(
         senderPrivateKey: Uint8Array,
diff --git a/packages/core/src/vcdm/BlockId.ts b/packages/core/src/vcdm/BlockId.ts
index e2bb39b41..d72d9aaaa 100644
--- a/packages/core/src/vcdm/BlockId.ts
+++ b/packages/core/src/vcdm/BlockId.ts
@@ -66,6 +66,7 @@ class BlockId extends HexUInt {
      * @throws {InvalidDataType} If the given expression is not a valid hexadecimal positive integer expression.
      */
     public static of(
+        // eslint-disable-next-line sonarjs/use-type-alias
         exp: bigint | number | string | Uint8Array | HexUInt
     ): BlockId {
         try {
@@ -107,4 +108,27 @@ class ThorId extends BlockId {
     }
 }
 
-export { BlockId, ThorId };
+/**
+ * This class is an alias of {@link TxId} for back compatibility.
+ */
+class TxId extends BlockId {
+    /**
+     * Constructs an instance of the class with the specified transaction ID.
+     *
+     * @param {TxId} blockId - The unique identifier for the block.
+     */
+    protected constructor(blockId: BlockId) {
+        super(blockId);
+    }
+
+    /**
+     * See {@link BlockId.of}.
+     */
+    public static of(
+        exp: bigint | number | string | Uint8Array | HexUInt
+    ): TxId {
+        return new TxId(BlockId.of(exp));
+    }
+}
+
+export { BlockId, ThorId, TxId };
diff --git a/packages/core/src/vcdm/Int.ts b/packages/core/src/vcdm/Int.ts
new file mode 100644
index 000000000..4caf84c43
--- /dev/null
+++ b/packages/core/src/vcdm/Int.ts
@@ -0,0 +1,18 @@
+import { InvalidDataType } from '@vechain/sdk-errors';
+
+class Int extends Number {
+    protected constructor(value: number) {
+        super(value);
+    }
+
+    static of(exp: number): Int {
+        if (Number.isInteger(exp)) {
+            return new Int(exp);
+        }
+        throw new InvalidDataType('Int.of', 'not an integer expression', {
+            exp: `${exp}`
+        });
+    }
+}
+
+export { Int };
diff --git a/packages/core/src/vcdm/Nonce.ts b/packages/core/src/vcdm/Nonce.ts
new file mode 100644
index 000000000..4b91232ae
--- /dev/null
+++ b/packages/core/src/vcdm/Nonce.ts
@@ -0,0 +1,41 @@
+import { HexUInt } from './HexUInt';
+import { Hex } from './Hex';
+import { InvalidDataType } from '@vechain/sdk-errors';
+
+class Nonce extends HexUInt {
+    private static readonly DIGITS = 8;
+
+    protected constructor(huint: HexUInt) {
+        super(Hex.POSITIVE, huint.fit(Nonce.DIGITS).digits);
+    }
+
+    public static isValid(exp: string): boolean {
+        return Hex.isValid(exp) && HexUInt.REGEX_HEXUINT_PREFIX.test(exp)
+            ? exp.length === Nonce.DIGITS + 2
+            : exp.length === Nonce.DIGITS;
+    }
+
+    public static isValid0x(exp: string): boolean {
+        return HexUInt.REGEX_HEXUINT_PREFIX.test(exp) && Nonce.isValid(exp);
+    }
+
+    public static of(
+        exp: bigint | number | string | Uint8Array | HexUInt
+    ): Nonce {
+        try {
+            if (exp instanceof HexUInt) {
+                return new Nonce(exp);
+            }
+            return new Nonce(HexUInt.of(exp));
+        } catch (e) {
+            throw new InvalidDataType(
+                'Nonce.of',
+                'not a Nonce expression',
+                { exp: `${exp}` }, // Needed to serialize bigint values.
+                e
+            );
+        }
+    }
+}
+
+export { Nonce };
diff --git a/packages/core/src/vcdm/UInt.ts b/packages/core/src/vcdm/UInt.ts
new file mode 100644
index 000000000..fdd9b10c1
--- /dev/null
+++ b/packages/core/src/vcdm/UInt.ts
@@ -0,0 +1,23 @@
+import { Int } from './Int';
+import { InvalidDataType } from '@vechain/sdk-errors';
+
+class UInt extends Int {
+    protected constructor(value: number) {
+        super(value);
+    }
+
+    static of(exp: number): UInt {
+        if (exp >= 0 && Number.isInteger(exp)) {
+            return new UInt(exp);
+        }
+        throw new InvalidDataType(
+            'UInt.of',
+            'not an unsigned integer expression',
+            {
+                exp: `${exp}`
+            }
+        );
+    }
+}
+
+export { UInt };
diff --git a/packages/core/src/vcdm/index.ts b/packages/core/src/vcdm/index.ts
index 9c12b8af5..29f3ed4fb 100644
--- a/packages/core/src/vcdm/index.ts
+++ b/packages/core/src/vcdm/index.ts
@@ -10,9 +10,12 @@ export * from './hash';
 export * from './Hex';
 export * from './HexInt';
 export * from './HexUInt';
+export * from './Int';
 export * from './Mnemonic';
+export * from './Nonce';
 export * from './Quantity';
 export * from './Revision';
 export * from './BlockId';
 export * from './Txt';
+export * from './UInt';
 export type * from './VeChainDataModel';
diff --git a/packages/net/README.md b/packages/net/README.md
new file mode 100644
index 000000000..a6db39615
--- /dev/null
+++ b/packages/net/README.md
@@ -0,0 +1 @@
+# @vechain/sdk-v2-network
diff --git a/packages/net/eslint.config.mjs b/packages/net/eslint.config.mjs
new file mode 100644
index 000000000..ca549b46e
--- /dev/null
+++ b/packages/net/eslint.config.mjs
@@ -0,0 +1,19 @@
+import baseConfig from "../../eslint.config.mjs";
+
+export default [
+    ...baseConfig,
+    {
+        rules: {
+            "import/no-restricted-paths": ["error", {
+                zones: [{
+                    target: "./src",
+
+                    from: [
+                        "../errors"
+                    ],
+
+                    message: "Please import using @vechain/sdk-<the-module>",
+                }],
+            }],
+        },
+    }];
diff --git a/packages/net/jest.browser-setup.js b/packages/net/jest.browser-setup.js
new file mode 100644
index 000000000..f5a510b90
--- /dev/null
+++ b/packages/net/jest.browser-setup.js
@@ -0,0 +1,18 @@
+require('whatwg-fetch');
+
+const fetchMock = require('jest-fetch-mock');
+
+// Don't auto-enable mocks
+fetchMock.dontMock();
+
+// Jest configuration for WebSocket mocking based on environment
+if (typeof window === 'undefined') {
+  // Running in Node.js environment
+  jest.mock('ws', () => require('ws'));
+} else {
+  // Running in browser environment
+  global.WebSocket = window.WebSocket;
+}
+
+// Make fetch global
+global.fetch = fetch;
\ No newline at end of file
diff --git a/packages/net/jest.config.browser.js b/packages/net/jest.config.browser.js
new file mode 100644
index 000000000..349145031
--- /dev/null
+++ b/packages/net/jest.config.browser.js
@@ -0,0 +1,10 @@
+/** @type {import('ts-jest').JestConfigWithTsJest} */
+module.exports = {
+    preset: 'ts-jest',
+    testEnvironment: '../../customEnv.js',
+    setupFiles: ['./jest.browser-setup.js'],
+    coverageReporters: ['html', 'lcov', 'json'],
+    runner: 'groups',
+    reporters: ['default', 'jest-junit'],
+    workerThreads: true
+};
diff --git a/packages/net/jest.config.js b/packages/net/jest.config.js
new file mode 100644
index 000000000..27eab725f
--- /dev/null
+++ b/packages/net/jest.config.js
@@ -0,0 +1,23 @@
+/** @type {import('ts-jest').JestConfigWithTsJest} */
+// Coverage threshold would apply to yarn test, not yarn test:unit
+const isUnitTest = process.env.UNIT;
+
+module.exports = {
+    preset: 'ts-jest',
+    testEnvironment: 'node',
+    coverageReporters: ['html', 'lcov', 'json'],
+    runner: 'groups',
+    reporters: ['default', 'jest-junit'],
+    workerThreads: true,
+    coverageThreshold:
+        isUnitTest !== 'true'
+            ? {
+                  global: {
+                      branches: 98,
+                      functions: 99,
+                      lines: 99,
+                      statements: 99
+                  }
+              }
+            : undefined
+};
diff --git a/packages/net/package.json b/packages/net/package.json
new file mode 100644
index 000000000..481d1f382
--- /dev/null
+++ b/packages/net/package.json
@@ -0,0 +1,49 @@
+{
+  "name": "@vechain/sdk-v2-network",
+  "version": "1.0.0-rc.6",
+  "description": "This module serves connects decentralized applications (dApps) the VeChainThor blockchain",
+  "author": "VeChain Foundation",
+  "license": "MIT",
+  "homepage": "https://github.com/vechain/vechain-sdk-js",
+  "repository": {
+    "type": "git",
+    "url": "github:vechain/vechain-sdk-js"
+  },
+  "keywords": [
+    "VeChain"
+  ],
+  "main": "dist/index.js",
+  "module": "dist/index.mjs",
+  "types": "dist/index.d.ts",
+  "files": [
+    "dist",
+    "src",
+    "package.json",
+    "README.md",
+    "LICENSE"
+  ],
+  "scripts": {
+    "build": "rm -rf ./dist && tsup-node src/index.ts --format cjs,esm --dts",
+    "check:circular-dependencies": "npx madge --json --circular --extensions ts src | jq '. | length' | awk '{if($1 > 15) exit 1}'",
+    "lint": "eslint",
+    "format": "prettier --write src/**/*.ts tests/**/*.ts solo-seeding/**/*.ts",
+    "start-thor-solo": "echo 'Starting thor solo node ...' && docker compose -f ../../docker-compose.thor.yml up -d --wait && echo '\nThor solo node started ...'",
+    "stop-thor-solo": "echo 'Stopping thor solo node ...' && docker compose -f ../../docker-compose.thor.yml down && echo 'Thor solo node stopped ...'",
+    "test:unit": "rm -rf ./coverageUnit && UNIT=true jest --coverage --coverageDirectory=coverageUnit --group=unit",
+    "test:integration": "rm -rf ./coverageIntegration && jest --coverage --coverageDirectory=coverageIntegration --group=integration",
+    "test:integration:solo": "(yarn start-thor-solo && yarn test:integration && yarn stop-thor-solo) || yarn stop-thor-solo",
+    "test:browser": "rm -rf ./coverage && jest --coverage --coverageDirectory=coverage --group=integration --group=unit --config ./jest.config.browser.js",
+    "test": "rm -rf ./coverage && jest --coverage --coverageDirectory=coverage --group=integration --group=unit",
+    "test:solo": "(yarn start-thor-solo && yarn test && yarn stop-thor-solo) || yarn stop-thor-solo",
+    "test:browser:solo": "(yarn start-thor-solo && yarn test:browser && yarn stop-thor-solo) || yarn stop-thor-solo"
+  },
+  "dependencies": {
+    "@vechain/sdk-core": "1.0.0-rc.6",
+    "ws": "^8.18.0"
+  },
+  "devDependencies": {
+    "@types/ws": "^8.5.13",
+    "jest-fetch-mock": "^3.0.3",
+    "whatwg-fetch": "^3.6.20"
+  }
+}
diff --git a/packages/net/src/http/FetchHttpClient.ts b/packages/net/src/http/FetchHttpClient.ts
new file mode 100644
index 000000000..e699e59ac
--- /dev/null
+++ b/packages/net/src/http/FetchHttpClient.ts
@@ -0,0 +1,76 @@
+import { type HttpClient } from './HttpClient';
+import { type HttpPath } from './HttpPath';
+import { type HttpQuery } from './HttpQuery';
+
+class FetchHttpClient implements HttpClient {
+    private static readonly PATH_SEPARATOR = '/';
+
+    public readonly baseURL: string;
+
+    private readonly onRequest: (request: Request) => Request;
+
+    private readonly onResponse: (response: Response) => Response;
+
+    constructor(
+        baseURL: string,
+        onRequest: (request: Request) => Request,
+        onResponse: (response: Response) => Response
+    ) {
+        this.baseURL = baseURL.endsWith(FetchHttpClient.PATH_SEPARATOR)
+            ? baseURL.substring(0, baseURL.length - 1)
+            : baseURL;
+        this.onRequest = onRequest;
+        this.onResponse = onResponse;
+    }
+
+    static at(
+        baseURL: string,
+        onRequest: (request: Request) => Request = (request) => request,
+        onResponse: (response: Response) => Response = (response) => response
+    ): FetchHttpClient {
+        return new FetchHttpClient(baseURL, onRequest, onResponse);
+    }
+
+    async get(
+        httpPath: HttpPath = {
+            path: ''
+        },
+        httpQuery: HttpQuery = {
+            query: ''
+        }
+    ): Promise<Response> {
+        const path = httpPath.path.startsWith(FetchHttpClient.PATH_SEPARATOR)
+            ? httpPath.path.substring(1)
+            : httpPath.path;
+        const request = new Request(
+            `${this.baseURL}${FetchHttpClient.PATH_SEPARATOR}${path}${httpQuery.query}`
+        );
+        const response = await fetch(this.onRequest(request));
+        return this.onResponse(response);
+    }
+
+    async post(
+        httpPath: HttpPath = {
+            path: ''
+        },
+        httpQuery: HttpQuery = {
+            query: ''
+        },
+        body?: unknown
+    ): Promise<Response> {
+        const path = httpPath.path.startsWith(FetchHttpClient.PATH_SEPARATOR)
+            ? httpPath.path.substring(1)
+            : httpPath.path;
+        const request = new Request(
+            `${this.baseURL}${FetchHttpClient.PATH_SEPARATOR}${path}${httpQuery.query}`,
+            {
+                body: JSON.stringify(body),
+                method: 'POST'
+            }
+        );
+        const response = await fetch(this.onRequest(request));
+        return this.onResponse(response);
+    }
+}
+
+export { FetchHttpClient };
diff --git a/packages/net/src/http/HttpClient.ts b/packages/net/src/http/HttpClient.ts
new file mode 100644
index 000000000..aea4e43e2
--- /dev/null
+++ b/packages/net/src/http/HttpClient.ts
@@ -0,0 +1,12 @@
+import { type HttpPath } from './HttpPath';
+import { type HttpQuery } from './HttpQuery';
+
+export interface HttpClient {
+    get: (httpPath: HttpPath, httpQuery: HttpQuery) => Promise<Response>;
+
+    post: (
+        httpPath: HttpPath,
+        httpQuery: HttpQuery,
+        body?: unknown
+    ) => Promise<Response>;
+}
diff --git a/packages/net/src/http/HttpPath.ts b/packages/net/src/http/HttpPath.ts
new file mode 100644
index 000000000..487f63980
--- /dev/null
+++ b/packages/net/src/http/HttpPath.ts
@@ -0,0 +1,3 @@
+export interface HttpPath {
+    get path(): string;
+}
diff --git a/packages/net/src/http/HttpQuery.ts b/packages/net/src/http/HttpQuery.ts
new file mode 100644
index 000000000..3e0a56bc9
--- /dev/null
+++ b/packages/net/src/http/HttpQuery.ts
@@ -0,0 +1,3 @@
+export interface HttpQuery {
+    get query(): string;
+}
diff --git a/packages/net/src/http/index.ts b/packages/net/src/http/index.ts
new file mode 100644
index 000000000..fca06d72a
--- /dev/null
+++ b/packages/net/src/http/index.ts
@@ -0,0 +1,4 @@
+export * from './FetchHttpClient';
+export * from './HttpClient';
+export * from './HttpPath';
+export * from './HttpQuery';
diff --git a/packages/net/src/index.ts b/packages/net/src/index.ts
new file mode 100644
index 000000000..9624102dd
--- /dev/null
+++ b/packages/net/src/index.ts
@@ -0,0 +1,2 @@
+export * from './http';
+export * from './thor';
diff --git a/packages/net/src/thor/ThorNetworks.ts b/packages/net/src/thor/ThorNetworks.ts
new file mode 100644
index 000000000..0d570c137
--- /dev/null
+++ b/packages/net/src/thor/ThorNetworks.ts
@@ -0,0 +1,7 @@
+enum ThorNetworks {
+    MAINNET = 'https://mainnet.vechain.org/',
+    SOLONET = 'http://localhost:8669/',
+    TESTNET = 'https://testnet.vechain.org/'
+}
+
+export { ThorNetworks };
diff --git a/packages/net/src/thor/ThorRequest.ts b/packages/net/src/thor/ThorRequest.ts
new file mode 100644
index 000000000..76ad64746
--- /dev/null
+++ b/packages/net/src/thor/ThorRequest.ts
@@ -0,0 +1,11 @@
+import { type HttpClient } from '../http';
+import { type ThorResponse } from './ThorResponse';
+
+export interface ThorRequest<
+    RequestClass extends ThorRequest<RequestClass, ResponseClass>,
+    ResponseClass
+> {
+    askTo: (
+        httpClient: HttpClient
+    ) => Promise<ThorResponse<RequestClass, ResponseClass>>;
+}
diff --git a/packages/net/src/thor/ThorResponse.ts b/packages/net/src/thor/ThorResponse.ts
new file mode 100644
index 000000000..e925587c2
--- /dev/null
+++ b/packages/net/src/thor/ThorResponse.ts
@@ -0,0 +1,9 @@
+import { type ThorRequest } from './ThorRequest';
+
+export interface ThorResponse<
+    RequestClass extends ThorRequest<RequestClass, ResponseClass>,
+    ResponseClass
+> {
+    request: RequestClass;
+    response: ResponseClass;
+}
diff --git a/packages/net/src/thor/accounts/ContractBytecode.ts b/packages/net/src/thor/accounts/ContractBytecode.ts
new file mode 100644
index 000000000..a6cac2c9b
--- /dev/null
+++ b/packages/net/src/thor/accounts/ContractBytecode.ts
@@ -0,0 +1,21 @@
+import { HexUInt } from '@vechain/sdk-core';
+
+interface ContractBytecodeJSON {
+    code: string;
+}
+
+class ContractBytecode {
+    readonly code: HexUInt;
+
+    constructor(json: ContractBytecodeJSON) {
+        this.code = HexUInt.of(json.code);
+    }
+
+    toJSON(): ContractBytecodeJSON {
+        return {
+            code: this.code.toString()
+        };
+    }
+}
+
+export { ContractBytecode, type ContractBytecodeJSON };
diff --git a/packages/net/src/thor/accounts/ExecuteCodesRequest.ts b/packages/net/src/thor/accounts/ExecuteCodesRequest.ts
new file mode 100644
index 000000000..0ea2860d3
--- /dev/null
+++ b/packages/net/src/thor/accounts/ExecuteCodesRequest.ts
@@ -0,0 +1,71 @@
+import { Clause, type ClauseJSON } from '../transactions';
+import { UInt } from '../../../../core/src';
+import { Address, BlockRef, Units, VTHO } from '@vechain/sdk-core';
+
+class ExecuteCodesRequest {
+    readonly provedWork?: string;
+    readonly gasPayer?: Address;
+    readonly expiration?: UInt;
+    readonly blockRef?: BlockRef;
+    readonly clauses?: Clause[];
+    readonly gas?: VTHO;
+    readonly gasPrice?: VTHO;
+    readonly caller?: Address;
+
+    constructor(json: ExecuteCodesRequestJSON) {
+        this.provedWork = json.provedWork;
+        this.gasPayer =
+            json.gasPayer === undefined ? undefined : Address.of(json.gasPayer);
+        this.expiration =
+            json.expiration === undefined
+                ? undefined
+                : UInt.of(json.expiration);
+        this.blockRef =
+            json.blockRef === undefined
+                ? undefined
+                : BlockRef.of(json.blockRef);
+        this.clauses =
+            json.clauses === undefined
+                ? undefined
+                : json.clauses.map(
+                      (clauseJSON: ClauseJSON): Clause => new Clause(clauseJSON)
+                  );
+        this.gas =
+            json.gas === undefined ? undefined : VTHO.of(json.gas, Units.wei);
+        this.gasPrice =
+            json.gasPrice === undefined
+                ? undefined
+                : VTHO.of(json.gasPrice, Units.wei);
+        this.caller =
+            json.caller === undefined ? undefined : Address.of(json.caller);
+    }
+
+    toJSON(): ExecuteCodesRequestJSON {
+        return {
+            provedWork: this.provedWork,
+            gasPayer: this.gasPayer?.toString(),
+            expiration: this.expiration?.valueOf(),
+            blockRef: this.blockRef?.toString(),
+            clauses: this.clauses?.map((clause: Clause) => clause.toJSON()),
+            gas: this.gas === undefined ? undefined : Number(this.gas.wei),
+            gasPrice:
+                this.gasPrice === undefined
+                    ? undefined
+                    : this.gasPrice.wei.toString(),
+            caller: this.caller?.toString()
+        } satisfies ExecuteCodesRequestJSON;
+    }
+}
+
+class ExecuteCodesRequestJSON {
+    provedWork?: string;
+    gasPayer?: string;
+    expiration?: number;
+    blockRef?: string;
+    clauses?: ClauseJSON[];
+    gas?: number;
+    gasPrice?: string;
+    caller?: string;
+}
+
+export { ExecuteCodesRequest, type ExecuteCodesRequestJSON };
diff --git a/packages/net/src/thor/accounts/ExecuteCodesResponse.ts b/packages/net/src/thor/accounts/ExecuteCodesResponse.ts
new file mode 100644
index 000000000..bdd547304
--- /dev/null
+++ b/packages/net/src/thor/accounts/ExecuteCodesResponse.ts
@@ -0,0 +1,73 @@
+import {
+    Event,
+    type EventJSON,
+    Transfer,
+    type TransferJSON
+} from '../transactions';
+import { HexUInt, VTHO } from '@vechain/sdk-core';
+
+class ExecuteCodeResponse {
+    readonly data: HexUInt;
+    readonly events: Event[];
+    readonly transfers: Transfer[];
+    readonly gasUsed: VTHO;
+    readonly reverted: boolean;
+    readonly vmError: string;
+
+    constructor(json: ExecuteCodeResponseJSON) {
+        this.data = HexUInt.of(json.data);
+        this.events = json.events.map(
+            (eventJSON: EventJSON): Event => new Event(eventJSON)
+        );
+        this.transfers = json.transfers.map(
+            (transferJSON: TransferJSON): Transfer => new Transfer(transferJSON)
+        );
+        this.gasUsed = VTHO.of(json.gasUsed);
+        this.reverted = json.reverted;
+        this.vmError = json.vmError;
+    }
+
+    toJSON(): ExecuteCodeResponseJSON {
+        return {
+            data: this.data.toString(),
+            events: this.events.map(
+                (event: Event): EventJSON => event.toJSON()
+            ),
+            transfers: this.transfers.map(
+                (transfer: Transfer): TransferJSON => transfer.toJSON()
+            ),
+            gasUsed: Number(this.gasUsed.wei),
+            reverted: this.reverted,
+            vmError: this.vmError
+        } satisfies ExecuteCodeResponseJSON;
+    }
+}
+
+class ExecuteCodesResponse extends Array<ExecuteCodeResponse> {
+    constructor(json: ExecuteCodesResponseJSON) {
+        super(
+            ...json.map(
+                (json: ExecuteCodeResponseJSON): ExecuteCodeResponse =>
+                    new ExecuteCodeResponse(json)
+            )
+        );
+    }
+}
+
+interface ExecuteCodeResponseJSON {
+    data: string;
+    events: EventJSON[];
+    transfers: TransferJSON[];
+    gasUsed: number;
+    reverted: boolean;
+    vmError: string;
+}
+
+interface ExecuteCodesResponseJSON extends Array<ExecuteCodeResponseJSON> {}
+
+export {
+    ExecuteCodeResponse,
+    ExecuteCodesResponse,
+    type ExecuteCodeResponseJSON,
+    type ExecuteCodesResponseJSON
+};
diff --git a/packages/net/src/thor/accounts/GetAccountResponse.ts b/packages/net/src/thor/accounts/GetAccountResponse.ts
new file mode 100644
index 000000000..fd4fc2ee8
--- /dev/null
+++ b/packages/net/src/thor/accounts/GetAccountResponse.ts
@@ -0,0 +1,29 @@
+import { Quantity, VET, VTHO } from '@vechain/sdk-core';
+
+class GetAccountResponse {
+    readonly balance: VET;
+    readonly energy: VTHO;
+    readonly hasCode: boolean;
+
+    constructor(json: GetAccountResponseJSON) {
+        this.balance = VET.of(json.balance);
+        this.energy = VTHO.of(json.energy);
+        this.hasCode = json.hasCode;
+    }
+
+    toJSON(): GetAccountResponseJSON {
+        return {
+            balance: Quantity.of(this.balance.wei).toString(),
+            energy: Quantity.of(this.energy.wei).toString(),
+            hasCode: this.hasCode
+        };
+    }
+}
+
+interface GetAccountResponseJSON {
+    balance: string;
+    energy: string;
+    hasCode: boolean;
+}
+
+export { GetAccountResponse, type GetAccountResponseJSON };
diff --git a/packages/net/src/thor/accounts/GetStorageResponse.ts b/packages/net/src/thor/accounts/GetStorageResponse.ts
new file mode 100644
index 000000000..406ba6b2d
--- /dev/null
+++ b/packages/net/src/thor/accounts/GetStorageResponse.ts
@@ -0,0 +1,21 @@
+import { ThorId } from '@vechain/sdk-core';
+
+class GetStorageResponse {
+    readonly value: ThorId;
+
+    constructor(json: GetStorageResponseJSON) {
+        this.value = ThorId.of(json.value);
+    }
+
+    toJSON(): GetStorageResponseJSON {
+        return {
+            value: this.value.toString()
+        } satisfies GetStorageResponseJSON;
+    }
+}
+
+interface GetStorageResponseJSON {
+    value: string;
+}
+
+export { GetStorageResponse, type GetStorageResponseJSON };
diff --git a/packages/net/src/thor/accounts/InspectClauses.ts b/packages/net/src/thor/accounts/InspectClauses.ts
new file mode 100644
index 000000000..c976271d9
--- /dev/null
+++ b/packages/net/src/thor/accounts/InspectClauses.ts
@@ -0,0 +1,71 @@
+import {
+    ExecuteCodesResponse,
+    type ExecuteCodesResponseJSON
+} from './ExecuteCodesResponse';
+import { type ThorRequest } from '../ThorRequest';
+import { type HttpClient, type HttpPath, type HttpQuery } from '../../http';
+import { type ThorResponse } from '../ThorResponse';
+import {
+    ExecuteCodesRequest,
+    type ExecuteCodesRequestJSON
+} from './ExecuteCodesRequest';
+import { Revision } from '@vechain/sdk-core';
+
+class InspectClauses
+    implements ThorRequest<InspectClauses, ExecuteCodesResponse>
+{
+    static readonly PATH: HttpPath = { path: '/accounts/*' };
+
+    private readonly query: InspectClauseQuery;
+
+    private readonly request: ExecuteCodesRequest;
+
+    constructor(query: InspectClauseQuery, request: ExecuteCodesRequest) {
+        this.query = query;
+        this.request = request;
+    }
+
+    async askTo(
+        httpClient: HttpClient
+    ): Promise<ThorResponse<InspectClauses, ExecuteCodesResponse>> {
+        const response = await httpClient.post(
+            InspectClauses.PATH,
+            this.query,
+            this.request.toJSON()
+        );
+        const responseBody =
+            (await response.json()) as ExecuteCodesResponseJSON;
+        return {
+            request: this,
+            response: new ExecuteCodesResponse(responseBody)
+        };
+    }
+
+    static of(request: ExecuteCodesRequestJSON): InspectClauses {
+        return new InspectClauses(
+            new InspectClauseQuery(Revision.BEST),
+            new ExecuteCodesRequest(request)
+        );
+    }
+
+    withRevision(revision: Revision = Revision.BEST): InspectClauses {
+        return new InspectClauses(
+            new InspectClauseQuery(revision),
+            this.request
+        );
+    }
+}
+
+class InspectClauseQuery implements HttpQuery {
+    readonly revision: Revision;
+
+    constructor(revision: Revision) {
+        this.revision = revision;
+    }
+
+    get query(): string {
+        return `?revision=${this.revision}`;
+    }
+}
+
+export { InspectClauses };
diff --git a/packages/net/src/thor/accounts/RetrieveAccountDetails.ts b/packages/net/src/thor/accounts/RetrieveAccountDetails.ts
new file mode 100644
index 000000000..d6bc13dcf
--- /dev/null
+++ b/packages/net/src/thor/accounts/RetrieveAccountDetails.ts
@@ -0,0 +1,49 @@
+import { type HttpClient, type HttpPath } from '../../http';
+import { type ThorRequest } from '../ThorRequest';
+import { type ThorResponse } from '../ThorResponse';
+import {
+    GetAccountResponse,
+    type GetAccountResponseJSON
+} from './GetAccountResponse';
+import { type Address } from '@vechain/sdk-core';
+
+class RetrieveAccountDetails
+    implements ThorRequest<RetrieveAccountDetails, GetAccountResponse>
+{
+    readonly path: RetrieveAccountDetailsPath;
+
+    constructor(path: RetrieveAccountDetailsPath) {
+        this.path = path;
+    }
+
+    async askTo(
+        httpClient: HttpClient
+    ): Promise<ThorResponse<RetrieveAccountDetails, GetAccountResponse>> {
+        const response = await httpClient.get(this.path, { query: '' });
+        const responseBody = (await response.json()) as GetAccountResponseJSON;
+        return {
+            request: this,
+            response: new GetAccountResponse(responseBody)
+        };
+    }
+
+    static of(address: Address): RetrieveAccountDetails {
+        return new RetrieveAccountDetails(
+            new RetrieveAccountDetailsPath(address)
+        );
+    }
+}
+
+class RetrieveAccountDetailsPath implements HttpPath {
+    readonly address: Address;
+
+    constructor(address: Address) {
+        this.address = address;
+    }
+
+    get path(): string {
+        return `/accounts/${this.address}`;
+    }
+}
+
+export { RetrieveAccountDetails, RetrieveAccountDetailsPath };
diff --git a/packages/net/src/thor/accounts/RetrieveContractBytecode.ts b/packages/net/src/thor/accounts/RetrieveContractBytecode.ts
new file mode 100644
index 000000000..7bf4b293f
--- /dev/null
+++ b/packages/net/src/thor/accounts/RetrieveContractBytecode.ts
@@ -0,0 +1,49 @@
+import {
+    ContractBytecode,
+    type ContractBytecodeJSON
+} from './ContractBytecode';
+import { type ThorRequest } from '../ThorRequest';
+import { type HttpClient, type HttpPath } from '../../http';
+import type { Address } from '@vechain/sdk-core';
+import { type ThorResponse } from '../ThorResponse';
+
+class RetrieveContractBytecode
+    implements ThorRequest<RetrieveContractBytecode, ContractBytecode>
+{
+    readonly path: RetrieveContractBytecodePath;
+
+    constructor(path: RetrieveContractBytecodePath) {
+        this.path = path;
+    }
+
+    async askTo(
+        httpClient: HttpClient
+    ): Promise<ThorResponse<RetrieveContractBytecode, ContractBytecode>> {
+        const response = await httpClient.get(this.path, { query: '' });
+        const responseBody = (await response.json()) as ContractBytecodeJSON;
+        return {
+            request: this,
+            response: new ContractBytecode(responseBody)
+        };
+    }
+
+    static of(address: Address): RetrieveContractBytecode {
+        return new RetrieveContractBytecode(
+            new RetrieveContractBytecodePath(address)
+        );
+    }
+}
+
+class RetrieveContractBytecodePath implements HttpPath {
+    readonly address: Address;
+
+    constructor(address: Address) {
+        this.address = address;
+    }
+
+    get path(): string {
+        return `/accounts/${this.address}/code`;
+    }
+}
+
+export { RetrieveContractBytecode, RetrieveContractBytecodePath };
diff --git a/packages/net/src/thor/accounts/RetrieveStoragePositionValue.ts b/packages/net/src/thor/accounts/RetrieveStoragePositionValue.ts
new file mode 100644
index 000000000..19ed82d6a
--- /dev/null
+++ b/packages/net/src/thor/accounts/RetrieveStoragePositionValue.ts
@@ -0,0 +1,52 @@
+import { type HttpClient, type HttpPath } from '../../http';
+import { type Address, type BlockId } from '@vechain/sdk-core';
+import { type ThorRequest } from '../ThorRequest';
+import {
+    GetStorageResponse,
+    type GetStorageResponseJSON
+} from './GetStorageResponse';
+import { type ThorResponse } from '../ThorResponse';
+
+class RetrieveStoragePositionValue
+    implements ThorRequest<RetrieveStoragePositionValue, GetStorageResponse>
+{
+    readonly path: RetrieveStoragePositionValuePath;
+
+    constructor(path: RetrieveStoragePositionValuePath) {
+        this.path = path;
+    }
+
+    async askTo(
+        httpClient: HttpClient
+    ): Promise<ThorResponse<RetrieveStoragePositionValue, GetStorageResponse>> {
+        const response = await httpClient.get(this.path, { query: '' });
+        const responseBody = (await response.json()) as GetStorageResponseJSON;
+        return {
+            request: this,
+            response: new GetStorageResponse(responseBody)
+        };
+    }
+
+    static of(address: Address, key: BlockId): RetrieveStoragePositionValue {
+        return new RetrieveStoragePositionValue(
+            new RetrieveStoragePositionValuePath(address, key)
+        );
+    }
+}
+
+class RetrieveStoragePositionValuePath implements HttpPath {
+    readonly address: Address;
+
+    readonly key: BlockId;
+
+    constructor(address: Address, key: BlockId) {
+        this.address = address;
+        this.key = key;
+    }
+
+    get path(): string {
+        return `/accounts/${this.address}/storage/${this.key}`;
+    }
+}
+
+export { RetrieveStoragePositionValue, RetrieveStoragePositionValuePath };
diff --git a/packages/net/src/thor/accounts/index.ts b/packages/net/src/thor/accounts/index.ts
new file mode 100644
index 000000000..bed432f86
--- /dev/null
+++ b/packages/net/src/thor/accounts/index.ts
@@ -0,0 +1,9 @@
+export * from './ContractBytecode';
+export * from './ExecuteCodesRequest';
+export * from './ExecuteCodesResponse';
+export * from './GetAccountResponse';
+export * from './InspectClauses';
+export * from './RetrieveAccountDetails';
+export * from './RetrieveContractBytecode';
+export * from './RetrieveStoragePositionValue';
+export * from './GetStorageResponse';
diff --git a/packages/net/src/thor/blocks/RegularBlockResponse..ts b/packages/net/src/thor/blocks/RegularBlockResponse..ts
new file mode 100644
index 000000000..cfab6b47d
--- /dev/null
+++ b/packages/net/src/thor/blocks/RegularBlockResponse..ts
@@ -0,0 +1,94 @@
+import { Address, ThorId, Units, VTHO } from '@vechain/sdk-core';
+import { UInt } from '../../../../core/src';
+
+class RegularBlockResponse {
+    readonly number: UInt;
+    readonly id: ThorId;
+    readonly size: UInt;
+    readonly parentID: ThorId;
+    readonly timestamp: bigint;
+    readonly gasLimit: VTHO;
+    readonly beneficiary: Address;
+    readonly gasUsed: VTHO;
+    readonly totalScore: UInt;
+    readonly txsRoot: ThorId;
+    readonly txsFeatures: UInt;
+    readonly stateRoot: ThorId;
+    readonly receiptsRoot: ThorId;
+    readonly com: boolean;
+    readonly signer: Address;
+    readonly isTrunk: boolean;
+    readonly isFinalized: boolean;
+    readonly transactions: ThorId[];
+
+    constructor(json: RegularBlockResponseJSON) {
+        this.number = UInt.of(json.number);
+        this.id = ThorId.of(json.id);
+        this.size = UInt.of(json.size);
+        this.parentID = ThorId.of(json.parentID);
+        this.timestamp = json.timestamp;
+        this.gasLimit = VTHO.of(json.gasLimit, Units.wei);
+        this.beneficiary = Address.of(json.beneficiary);
+        this.gasUsed = VTHO.of(json.gasUsed, Units.wei);
+        this.totalScore = UInt.of(json.totalScore);
+        this.txsRoot = ThorId.of(json.txsRoot);
+        this.txsFeatures = UInt.of(json.txsFeatures);
+        this.stateRoot = ThorId.of(json.stateRoot);
+        this.receiptsRoot = Address.of(json.receiptsRoot);
+        this.com = json.com;
+        this.signer = Address.of(json.signer);
+        this.isTrunk = json.isTrunk;
+        this.isFinalized = json.isFinalized;
+        this.transactions = json.transactions.map(
+            (txId: string): ThorId => ThorId.of(txId)
+        );
+    }
+
+    toJSON(): RegularBlockResponseJSON {
+        return {
+            number: this.number.valueOf(),
+            id: this.id.toString(),
+            size: this.size.valueOf(),
+            parentID: this.parentID.toString(),
+            timestamp: this.timestamp,
+            gasLimit: Number(this.gasLimit.wei),
+            beneficiary: this.beneficiary.toString(),
+            gasUsed: Number(this.gasUsed.wei),
+            totalScore: this.totalScore.valueOf(),
+            txsRoot: this.txsRoot.toString(),
+            txsFeatures: this.txsFeatures.valueOf(),
+            stateRoot: this.stateRoot.toString(),
+            receiptsRoot: this.receiptsRoot.toString(),
+            com: this.com,
+            signer: this.signer.toString(),
+            isTrunk: this.isTrunk,
+            isFinalized: this.isFinalized,
+            transactions: this.transactions.map((txId: ThorId) =>
+                txId.toString()
+            )
+        } satisfies RegularBlockResponseJSON;
+    }
+}
+
+interface RegularBlockResponseJSON {
+    number: number;
+    id: string;
+    size: number;
+    parentID: string;
+    timestamp: bigint;
+    gasLimit: number;
+    beneficiary: string;
+    gasUsed: number;
+    totalScore: number;
+    txsRoot: string;
+    txsFeatures: number;
+    stateRoot: string;
+    receiptsRoot: string;
+    com: boolean;
+    signer: string;
+    isTrunk: boolean;
+    isFinalized: boolean;
+    transactions: string[];
+}
+
+export { RegularBlockResponse, type RegularBlockResponseJSON };
diff --git a/packages/net/src/thor/blocks/RetrieveBlock.ts b/packages/net/src/thor/blocks/RetrieveBlock.ts
new file mode 100644
index 000000000..02a30cd2c
--- /dev/null
+++ b/packages/net/src/thor/blocks/RetrieveBlock.ts
@@ -0,0 +1,48 @@
+import { type HttpClient, type HttpPath } from '../../http';
+import { type ThorRequest } from '../ThorRequest';
+import { type ThorResponse } from '../ThorResponse';
+import {
+    RegularBlockResponse,
+    type RegularBlockResponseJSON
+} from './RegularBlockResponse.';
+import { type Revision } from '@vechain/sdk-core';
+
+class RetrieveBlock
+    implements ThorRequest<RetrieveBlock, RegularBlockResponse>
+{
+    public readonly path: RetrieveBlockPath;
+
+    constructor(path: RetrieveBlockPath) {
+        this.path = path;
+    }
+
+    async askTo(
+        httpClient: HttpClient
+    ): Promise<ThorResponse<RetrieveBlock, RegularBlockResponse>> {
+        const response = await httpClient.get(this.path, { query: '' });
+        const responseBody =
+            (await response.json()) as RegularBlockResponseJSON;
+        return {
+            request: this,
+            response: new RegularBlockResponse(responseBody)
+        };
+    }
+
+    static of(revision: Revision): RetrieveBlock {
+        return new RetrieveBlock(new RetrieveBlockPath(revision));
+    }
+}
+
+class RetrieveBlockPath implements HttpPath {
+    readonly revision: Revision;
+
+    constructor(revision: Revision) {
+        this.revision = revision;
+    }
+
+    get path(): string {
+        return `/blocks/${this.revision}`;
+    }
+}
+
+export { RetrieveBlock, RetrieveBlockPath };
diff --git a/packages/net/src/thor/blocks/index.ts b/packages/net/src/thor/blocks/index.ts
new file mode 100644
index 000000000..99bfdd062
--- /dev/null
+++ b/packages/net/src/thor/blocks/index.ts
@@ -0,0 +1,2 @@
+export * from './RetrieveBlock';
+export * from './RegularBlockResponse.';
diff --git a/packages/net/src/thor/debug/PostDebugTracerCallRequest.ts b/packages/net/src/thor/debug/PostDebugTracerCallRequest.ts
new file mode 100644
index 000000000..70663df62
--- /dev/null
+++ b/packages/net/src/thor/debug/PostDebugTracerCallRequest.ts
@@ -0,0 +1,85 @@
+import { Tracer, type TracerName } from './TracerName';
+import {
+    Address,
+    type BlockRef,
+    HexUInt,
+    Units,
+    VET,
+    VTHO
+} from '@vechain/sdk-core';
+import { UInt } from '../../../../core/src';
+
+class PostDebugTracerCallRequest {
+    readonly name?: TracerName;
+    readonly config?: unknown;
+    readonly value: VET;
+    readonly data: HexUInt;
+    readonly to?: Address;
+    readonly gas?: VTHO;
+    readonly gasPrice?: VTHO;
+    readonly caller?: Address;
+    readonly provedWork?: string;
+    readonly gasPayer?: Address;
+    readonly expiration?: UInt;
+    readonly blockRef?: BlockRef;
+
+    constructor(json: PostDebugTracerCallRequestJSON) {
+        this.name = json.name === undefined ? undefined : Tracer.of(json.name);
+        this.config = json.config;
+        this.value = VET.of(json.value);
+        this.data = HexUInt.of(json.data);
+        this.to = json.to === undefined ? undefined : Address.of(json.to);
+        this.gas =
+            json.gas === undefined ? undefined : VTHO.of(json.gas, Units.wei);
+        this.gasPrice =
+            json.gasPrice === undefined
+                ? undefined
+                : VTHO.of(json.gasPrice, Units.wei);
+        this.caller =
+            json.caller === undefined ? undefined : Address.of(json.caller);
+        this.provedWork = json.provedWork;
+        this.gasPayer =
+            json.gasPayer === undefined ? undefined : Address.of(json.gasPayer);
+        this.expiration =
+            json.expiration === undefined
+                ? undefined
+                : UInt.of(json.expiration);
+    }
+
+    toJSON(): PostDebugTracerCallRequestJSON {
+        return {
+            name: this.name?.toString(),
+            config: this.config,
+            value: HexUInt.of(this.value.wei).toString(),
+            data: this.data.toString(),
+            to: this.to?.toString(),
+            gas: this.gas === undefined ? undefined : Number(this.gas.wei),
+            gasPrice:
+                this.gasPrice === undefined
+                    ? undefined
+                    : this.gasPrice.wei?.toString(),
+            caller: this.caller?.toString(),
+            provedWork: this.provedWork,
+            gasPayer: this.gasPayer?.toString(),
+            expiration: this.expiration?.valueOf(),
+            blockRef: this.blockRef?.toString()
+        };
+    }
+}
+
+interface PostDebugTracerCallRequestJSON {
+    name?: string;
+    config?: unknown;
+    value: string;
+    data: string;
+    to?: string;
+    gas?: number;
+    gasPrice?: string;
+    caller?: string;
+    provedWork?: string;
+    gasPayer?: string;
+    expiration?: number;
+    blockRef?: string;
+}
+
+export { PostDebugTracerCallRequest, type PostDebugTracerCallRequestJSON };
diff --git a/packages/net/src/thor/debug/PostDebugTracerRequest.ts b/packages/net/src/thor/debug/PostDebugTracerRequest.ts
new file mode 100644
index 000000000..91bc81484
--- /dev/null
+++ b/packages/net/src/thor/debug/PostDebugTracerRequest.ts
@@ -0,0 +1,29 @@
+import { Tracer, type TracerName } from './TracerName';
+
+class PostDebugTracerRequest {
+    readonly name?: TracerName;
+    readonly config?: unknown;
+    readonly target: string;
+
+    constructor(json: PostDebugTracerRequestJSON) {
+        this.name = json.name === undefined ? undefined : Tracer.of(json.name);
+        this.config = json.config;
+        this.target = json.target;
+    }
+
+    toJSON(): PostDebugTracerRequestJSON {
+        return {
+            name: this.name?.toString(),
+            config: this.config,
+            target: this.target
+        };
+    }
+}
+
+interface PostDebugTracerRequestJSON {
+    name?: string;
+    config?: unknown;
+    target: string;
+}
+
+export { PostDebugTracerRequest, type PostDebugTracerRequestJSON };
diff --git a/packages/net/src/thor/debug/RetrieveStorageRange.ts b/packages/net/src/thor/debug/RetrieveStorageRange.ts
new file mode 100644
index 000000000..f50827612
--- /dev/null
+++ b/packages/net/src/thor/debug/RetrieveStorageRange.ts
@@ -0,0 +1,41 @@
+import type { HttpClient, HttpPath } from '../../http';
+import { type ThorRequest } from '../ThorRequest';
+import {
+    StorageRangeOption,
+    type StorageRangeOptionJSON
+} from './StorageRangeOption';
+import { type ThorResponse } from '../ThorResponse';
+import { type StorageRange } from './StorageRange';
+
+class RetrieveStorageRange
+    implements ThorRequest<RetrieveStorageRange, StorageRange>
+{
+    static readonly PATH: HttpPath = { path: '/debug/storage-range' };
+
+    readonly request: StorageRangeOption;
+
+    constructor(request: StorageRangeOption) {
+        this.request = request;
+    }
+
+    async askTo(
+        httpClient: HttpClient
+    ): Promise<ThorResponse<RetrieveStorageRange, StorageRange>> {
+        const response = await httpClient.post(
+            RetrieveStorageRange.PATH,
+            { query: '' },
+            this.request.toJSON()
+        );
+        const responseBody = (await response.json()) as StorageRange;
+        return {
+            request: this,
+            response: responseBody
+        };
+    }
+
+    static of(request: StorageRangeOptionJSON): RetrieveStorageRange {
+        return new RetrieveStorageRange(new StorageRangeOption(request));
+    }
+}
+
+export { RetrieveStorageRange };
diff --git a/packages/net/src/thor/debug/StorageRange.ts b/packages/net/src/thor/debug/StorageRange.ts
new file mode 100644
index 000000000..363d6b799
--- /dev/null
+++ b/packages/net/src/thor/debug/StorageRange.ts
@@ -0,0 +1,26 @@
+import { ThorId } from '@vechain/sdk-core';
+
+class StorageRange {
+    readonly nextKey?: ThorId;
+    readonly storage: unknown;
+
+    constructor(json: StorageRangeJSON) {
+        this.nextKey =
+            json.nextKey === undefined ? undefined : ThorId.of(json.nextKey);
+        this.storage = json.storage;
+    }
+
+    toJSON(): StorageRangeJSON {
+        return {
+            nextKey: this.nextKey?.toString(),
+            storage: this.storage
+        } satisfies StorageRangeJSON;
+    }
+}
+
+interface StorageRangeJSON {
+    nextKey?: string;
+    storage: unknown;
+}
+
+export { StorageRange, type StorageRangeJSON };
diff --git a/packages/net/src/thor/debug/StorageRangeOption.ts b/packages/net/src/thor/debug/StorageRangeOption.ts
new file mode 100644
index 000000000..e521daedf
--- /dev/null
+++ b/packages/net/src/thor/debug/StorageRangeOption.ts
@@ -0,0 +1,36 @@
+import { Address, ThorId } from '@vechain/sdk-core';
+import { UInt } from '../../../../core/src';
+
+class StorageRangeOption {
+    readonly address: Address;
+    readonly keyStart?: ThorId;
+    readonly maxResult?: UInt;
+    readonly target: string; // Path class?
+
+    constructor(json: StorageRangeOptionJSON) {
+        this.address = Address.of(json.address);
+        this.keyStart =
+            json.keyStart === undefined ? undefined : ThorId.of(json.keyStart);
+        this.maxResult =
+            json.maxResult === undefined ? undefined : UInt.of(json.maxResult);
+        this.target = json.target;
+    }
+
+    toJSON(): StorageRangeOptionJSON {
+        return {
+            address: this.address.toString(),
+            keyStart: this.keyStart?.toString(),
+            maxResult: this.maxResult?.valueOf(),
+            target: this.target
+        } satisfies StorageRangeOptionJSON;
+    }
+}
+
+interface StorageRangeOptionJSON {
+    address: string;
+    keyStart?: string;
+    maxResult?: number;
+    target: string;
+}
+
+export { StorageRangeOption, type StorageRangeOptionJSON };
diff --git a/packages/net/src/thor/debug/TraceCall.ts b/packages/net/src/thor/debug/TraceCall.ts
new file mode 100644
index 000000000..d74a28023
--- /dev/null
+++ b/packages/net/src/thor/debug/TraceCall.ts
@@ -0,0 +1,38 @@
+import { type ThorRequest } from '../ThorRequest';
+import { type HttpClient, type HttpPath } from '../../http';
+import {
+    PostDebugTracerCallRequest,
+    type PostDebugTracerCallRequestJSON
+} from './PostDebugTracerCallRequest';
+import { type ThorResponse } from '../ThorResponse';
+
+class TraceCall implements ThorRequest<TraceCall, undefined> {
+    static readonly PATH: HttpPath = { path: '/debug/tracers/call' };
+
+    readonly request: PostDebugTracerCallRequest;
+
+    constructor(request: PostDebugTracerCallRequest) {
+        this.request = request;
+    }
+
+    async askTo(
+        httpClient: HttpClient
+    ): Promise<ThorResponse<TraceCall, undefined>> {
+        const response = await httpClient.post(
+            TraceCall.PATH,
+            { query: '' },
+            this.request.toJSON()
+        );
+        const responseBody: unknown = await response.json();
+        return {
+            request: this,
+            response: responseBody as undefined
+        };
+    }
+
+    static of(request: PostDebugTracerCallRequestJSON): TraceCall {
+        return new TraceCall(new PostDebugTracerCallRequest(request));
+    }
+}
+
+export { TraceCall };
diff --git a/packages/net/src/thor/debug/TraceTransactionClause.ts b/packages/net/src/thor/debug/TraceTransactionClause.ts
new file mode 100644
index 000000000..73e0d6835
--- /dev/null
+++ b/packages/net/src/thor/debug/TraceTransactionClause.ts
@@ -0,0 +1,40 @@
+import { type ThorRequest } from '../ThorRequest';
+import type { HttpClient, HttpPath } from '../../http';
+import {
+    PostDebugTracerRequest,
+    type PostDebugTracerRequestJSON
+} from './PostDebugTracerRequest';
+import { type ThorResponse } from '../ThorResponse';
+
+class TraceTransactionClause
+    implements ThorRequest<TraceTransactionClause, unknown>
+{
+    static readonly PATH: HttpPath = { path: '/debug/tracers' };
+
+    readonly request: PostDebugTracerRequest;
+
+    constructor(request: PostDebugTracerRequest) {
+        this.request = request;
+    }
+
+    async askTo(
+        httpClient: HttpClient
+    ): Promise<ThorResponse<TraceTransactionClause, unknown>> {
+        const response = await httpClient.post(
+            TraceTransactionClause.PATH,
+            { query: '' },
+            this.request.toJSON()
+        );
+        const responseBody: unknown = await response.json();
+        return {
+            request: this,
+            response: responseBody as undefined
+        };
+    }
+
+    static of(request: PostDebugTracerRequestJSON): TraceTransactionClause {
+        return new TraceTransactionClause(new PostDebugTracerRequest(request));
+    }
+}
+
+export { TraceTransactionClause };
diff --git a/packages/net/src/thor/debug/TracerName.ts b/packages/net/src/thor/debug/TracerName.ts
new file mode 100644
index 000000000..2372d7ef7
--- /dev/null
+++ b/packages/net/src/thor/debug/TracerName.ts
@@ -0,0 +1,105 @@
+abstract class TracerName {
+    abstract toString: () => string;
+}
+
+class StructLogger extends TracerName {
+    static readonly NAME = 'structLogger';
+    toString: () => string = () => StructLogger.NAME;
+}
+
+class FourByte extends TracerName {
+    static readonly NAME = '4byte';
+    toString: () => string = () => FourByte.NAME;
+}
+
+class Call extends TracerName {
+    static readonly NAME = 'call';
+    toString: () => string = () => Call.NAME;
+}
+
+class Noop extends TracerName {
+    static readonly NAME = 'noop';
+    toString: () => string = () => Noop.NAME;
+}
+
+class Prestate extends TracerName {
+    static readonly NAME = 'prestate';
+    toString: () => string = () => Prestate.NAME;
+}
+
+class Unigram extends TracerName {
+    static readonly NAME = 'unigram';
+    toString: () => string = () => Unigram.NAME;
+}
+
+class Bigram extends TracerName {
+    static readonly NAME = 'bigram';
+    toString: () => string = () => Bigram.NAME;
+}
+
+class Trigram extends TracerName {
+    static readonly NAME = 'trigram';
+    toString: () => string = () => Trigram.NAME;
+}
+
+class EvmDis extends TracerName {
+    static readonly NAME = 'evmdis';
+    toString: () => string = () => EvmDis.NAME;
+}
+
+class OpCount extends TracerName {
+    static readonly NAME = 'opcount';
+    toString: () => string = () => OpCount.NAME;
+}
+
+class Null extends TracerName {
+    static readonly NAME = 'null';
+    toString: () => string = () => Null.NAME;
+}
+
+// eslint-disable-next-line @typescript-eslint/no-extraneous-class
+class Tracer {
+    static of(name: string): TracerName {
+        switch (name) {
+            case StructLogger.NAME:
+                return new StructLogger();
+            case FourByte.NAME:
+                return new FourByte();
+            case Call.NAME:
+                return new Call();
+            case Noop.NAME:
+                return new Noop();
+            case Prestate.NAME:
+                return new Prestate();
+            case Unigram.NAME:
+                return new Unigram();
+            case Bigram.NAME:
+                return new Bigram();
+            case Trigram.NAME:
+                return new Trigram();
+            case EvmDis.NAME:
+                return new EvmDis();
+            case OpCount.NAME:
+                return new OpCount();
+            case Null.NAME:
+                return new Null();
+        }
+        throw new Error(`TracerName ${name} not found`);
+    }
+}
+
+export {
+    type TracerName,
+    StructLogger,
+    FourByte,
+    Call,
+    Noop,
+    Prestate,
+    Unigram,
+    Bigram,
+    Trigram,
+    EvmDis,
+    OpCount,
+    Null,
+    Tracer
+};
diff --git a/packages/net/src/thor/debug/index.ts b/packages/net/src/thor/debug/index.ts
new file mode 100644
index 000000000..0a03ce0a4
--- /dev/null
+++ b/packages/net/src/thor/debug/index.ts
@@ -0,0 +1,8 @@
+export * from './PostDebugTracerCallRequest';
+export * from './PostDebugTracerRequest';
+export * from './RetrieveStorageRange';
+export * from './StorageRange';
+export * from './StorageRangeOption';
+export * from './TraceCall';
+export * from './TracerName';
+export * from './TraceTransactionClause';
diff --git a/packages/net/src/thor/explorer/index.ts b/packages/net/src/thor/explorer/index.ts
new file mode 100644
index 000000000..21fb5819e
--- /dev/null
+++ b/packages/net/src/thor/explorer/index.ts
@@ -0,0 +1,42 @@
+import { FetchHttpClient } from '../../http';
+import { ThorNetworks } from '../ThorNetworks';
+import { RetrieveBlock, RetrieveBlockPath } from '../blocks';
+import { Revision } from '@vechain/sdk-core';
+import { RetrieveTransactionByID } from '../transactions';
+
+import { TxId } from '../../../../core/src/vcdm/BlockId';
+
+async function getBestBlockNumber(
+    httpClient: FetchHttpClient
+): Promise<number> {
+    const r = await new RetrieveBlock(
+        new RetrieveBlockPath(Revision.BEST)
+    ).askTo(httpClient);
+    return r.response.number.valueOf();
+}
+
+async function explore(): Promise<void> {
+    const httpClient = FetchHttpClient.at(ThorNetworks.SOLONET);
+    const lastBlockNumber = await getBestBlockNumber(httpClient);
+    for (let blockNumber = lastBlockNumber; blockNumber >= 0; blockNumber--) {
+        const block = (
+            await RetrieveBlock.of(Revision.of(blockNumber)).askTo(httpClient)
+        ).response;
+        console.log(`BLOCK ${block.number} of ${lastBlockNumber}`);
+        for (const txid of block.transactions) {
+            console.log(`TXID ${txid}`);
+            const tx = (
+                await RetrieveTransactionByID.of(
+                    TxId.of(txid.toString())
+                ).askTo(httpClient)
+            ).response;
+
+            console.log(`TX: ${JSON.stringify(tx, null, 2)}`);
+        }
+    }
+}
+
+console.log('Thor Scanner...');
+void explore().then((_r) => {
+    console.log('End of all jobs.');
+});
diff --git a/packages/net/src/thor/index.ts b/packages/net/src/thor/index.ts
new file mode 100644
index 000000000..846caa20b
--- /dev/null
+++ b/packages/net/src/thor/index.ts
@@ -0,0 +1,7 @@
+export * from './accounts';
+export * from './node';
+export * from './blocks';
+export * from './ThorNetworks';
+export * from './ThorRequest';
+export * from './ThorResponse';
+export * from './transactions';
diff --git a/packages/net/src/thor/logs/EventCriteria.ts b/packages/net/src/thor/logs/EventCriteria.ts
new file mode 100644
index 000000000..9831188de
--- /dev/null
+++ b/packages/net/src/thor/logs/EventCriteria.ts
@@ -0,0 +1,47 @@
+import { Address, ThorId } from '@vechain/sdk-core';
+
+class EventCriteria {
+    readonly address?: Address;
+    readonly topic0?: ThorId;
+    readonly topic1?: ThorId;
+    readonly topic2?: ThorId;
+    readonly topic3?: ThorId;
+    readonly topic4?: ThorId;
+
+    constructor(json: EventCriteriaJSON) {
+        this.address =
+            json.address === undefined ? undefined : Address.of(json.address);
+        this.topic0 =
+            json.topic0 === undefined ? undefined : ThorId.of(json.topic0);
+        this.topic1 =
+            json.topic1 === undefined ? undefined : ThorId.of(json.topic1);
+        this.topic2 =
+            json.topic2 === undefined ? undefined : ThorId.of(json.topic2);
+        this.topic3 =
+            json.topic3 === undefined ? undefined : ThorId.of(json.topic3);
+        this.topic4 =
+            json.topic4 === undefined ? undefined : ThorId.of(json.topic4);
+    }
+
+    toJSON(): EventCriteriaJSON {
+        return {
+            address: this.address?.toString(),
+            topic0: this.topic0?.toString(),
+            topic1: this.topic1?.toString(),
+            topic2: this.topic2?.toString(),
+            topic3: this.topic3?.toString(),
+            topic4: this.topic4?.toString()
+        } satisfies EventCriteriaJSON;
+    }
+}
+
+interface EventCriteriaJSON {
+    address?: string;
+    topic0?: string;
+    topic1?: string;
+    topic2?: string;
+    topic3?: string;
+    topic4?: string;
+}
+
+export { EventCriteria, type EventCriteriaJSON };
diff --git a/packages/net/src/thor/logs/EventLogFilterRequest.ts b/packages/net/src/thor/logs/EventLogFilterRequest.ts
new file mode 100644
index 000000000..197ee1ac8
--- /dev/null
+++ b/packages/net/src/thor/logs/EventLogFilterRequest.ts
@@ -0,0 +1,46 @@
+import { FilterRange, type FilterRangeJSON } from './FilterRange';
+import { EventCriteria, type EventCriteriaJSON } from './EventCriteria';
+import { FilterOptions, type FilterOptionsJSON } from './FilterOptions';
+import { type LogSort } from './LogSort';
+
+class EventLogFilterRequest {
+    readonly range?: FilterRange;
+    readonly options?: FilterOptions;
+    readonly criteriaSet?: EventCriteria[];
+    readonly order?: LogSort;
+
+    constructor(json: EventLogFilterRequestJSON) {
+        this.range =
+            json.range === undefined ? undefined : new FilterRange(json.range);
+        this.options =
+            json.options === undefined
+                ? undefined
+                : new FilterOptions(json.options);
+        this.criteriaSet =
+            json.criteriaSet === undefined
+                ? undefined
+                : json.criteriaSet.map(
+                      (criteriaJSON) => new EventCriteria(criteriaJSON)
+                  );
+        this.order =
+            json.order === undefined ? undefined : (json.order as LogSort);
+    }
+
+    toJSON(): EventLogFilterRequestJSON {
+        return {
+            range: this.range?.toJSON(),
+            options: this.options?.toJSON(),
+            criteriaSet: this.criteriaSet?.map((criteria) => criteria.toJSON()),
+            order: this.order?.toString()
+        } satisfies EventLogFilterRequestJSON;
+    }
+}
+
+interface EventLogFilterRequestJSON {
+    range?: FilterRangeJSON;
+    options?: FilterOptionsJSON;
+    criteriaSet?: EventCriteriaJSON[];
+    order?: string;
+}
+
+export { EventLogFilterRequest, type EventLogFilterRequestJSON };
diff --git a/packages/net/src/thor/logs/EventLogsResponse.ts b/packages/net/src/thor/logs/EventLogsResponse.ts
new file mode 100644
index 000000000..1ce266b29
--- /dev/null
+++ b/packages/net/src/thor/logs/EventLogsResponse.ts
@@ -0,0 +1,56 @@
+import { LogMeta, type LogMetaJSON } from './LogMeta';
+import { Address, HexUInt, ThorId } from '@vechain/sdk-core';
+
+class EventLogResponse {
+    readonly address: Address;
+    readonly topics: ThorId[];
+    readonly data: HexUInt;
+    readonly meta: LogMeta;
+
+    constructor(json: EventLogResponseJSON) {
+        this.address = Address.of(json.address);
+        this.topics = json.topics.map(
+            (topic: string): ThorId => ThorId.of(topic)
+        );
+        this.data = HexUInt.of(json.data);
+        this.meta = new LogMeta(json.meta);
+    }
+
+    toJSON(): EventLogResponseJSON {
+        return {
+            address: this.address.toString(),
+            topics: this.topics.map((topic: ThorId): string =>
+                topic.toString()
+            ),
+            data: this.data.toString(),
+            meta: this.meta.toJSON()
+        } satisfies EventLogResponseJSON;
+    }
+}
+
+interface EventLogResponseJSON {
+    address: string;
+    topics: string[];
+    data: string;
+    meta: LogMetaJSON;
+}
+
+class EventLogsResponse extends Array<EventLogResponse> {
+    constructor(json: EventLogsResponseJSON) {
+        super(
+            ...json.map(
+                (response: EventLogResponseJSON): EventLogResponse =>
+                    new EventLogResponse(response)
+            )
+        );
+    }
+}
+
+interface EventLogsResponseJSON extends Array<EventLogResponseJSON> {}
+
+export {
+    EventLogResponse,
+    type EventLogResponseJSON,
+    type EventLogsResponseJSON,
+    EventLogsResponse
+};
diff --git a/packages/net/src/thor/logs/FilterOptions.ts b/packages/net/src/thor/logs/FilterOptions.ts
new file mode 100644
index 000000000..54c212af6
--- /dev/null
+++ b/packages/net/src/thor/logs/FilterOptions.ts
@@ -0,0 +1,26 @@
+import { UInt } from '../../../../core/src';
+
+class FilterOptions {
+    readonly limit?: UInt;
+    readonly offset?: UInt;
+
+    constructor(json: FilterOptionsJSON) {
+        this.limit = json.limit === undefined ? undefined : UInt.of(json.limit);
+        this.offset =
+            json.offset === undefined ? undefined : UInt.of(json.offset);
+    }
+
+    toJSON(): FilterOptionsJSON {
+        return {
+            limit: this.limit?.valueOf(),
+            offset: this.offset?.valueOf()
+        } satisfies FilterOptionsJSON;
+    }
+}
+
+interface FilterOptionsJSON {
+    limit?: number;
+    offset?: number;
+}
+
+export { FilterOptions, type FilterOptionsJSON };
diff --git a/packages/net/src/thor/logs/FilterRange.ts b/packages/net/src/thor/logs/FilterRange.ts
new file mode 100644
index 000000000..658df799e
--- /dev/null
+++ b/packages/net/src/thor/logs/FilterRange.ts
@@ -0,0 +1,38 @@
+import { UInt } from '../../../../core/src';
+
+class FilterRange {
+    readonly unit?: FilterRangeUnits;
+    readonly from?: UInt;
+    readonly to?: UInt;
+
+    constructor(json: FilterRangeJSON) {
+        this.unit =
+            typeof json.unit === 'string'
+                ? (json.unit as FilterRangeUnits)
+                : undefined;
+        this.from =
+            typeof json.from === 'number' ? UInt.of(json.from) : undefined;
+        this.to = typeof json.to === 'number' ? UInt.of(json.to) : undefined;
+    }
+
+    toJSON(): FilterRangeJSON {
+        return {
+            unit: this.unit?.toString(),
+            from: this.from?.valueOf(),
+            to: this.to?.valueOf()
+        } satisfies FilterRangeJSON;
+    }
+}
+
+interface FilterRangeJSON {
+    unit?: string;
+    from?: number;
+    to?: number;
+}
+
+enum FilterRangeUnits {
+    block = 'block',
+    time = 'time'
+}
+
+export { FilterRange, type FilterRangeJSON, FilterRangeUnits };
diff --git a/packages/net/src/thor/logs/LogMeta.ts b/packages/net/src/thor/logs/LogMeta.ts
new file mode 100644
index 000000000..7a2b3b61b
--- /dev/null
+++ b/packages/net/src/thor/logs/LogMeta.ts
@@ -0,0 +1,42 @@
+import { Address, BlockId } from '@vechain/sdk-core';
+import { TxId, UInt } from '../../../../core/src/vcdm';
+
+class LogMeta {
+    readonly blockID: BlockId;
+    readonly blockNumber: UInt;
+    readonly blockTimestamp: UInt;
+    readonly txID: TxId;
+    readonly txOrigin: Address;
+    readonly clauseIndex: UInt;
+
+    constructor(json: LogMetaJSON) {
+        this.blockID = BlockId.of(json.blockID);
+        this.blockNumber = UInt.of(json.blockNumber);
+        this.blockTimestamp = UInt.of(json.blockTimestamp);
+        this.txID = TxId.of(json.txID);
+        this.txOrigin = Address.of(json.txOrigin);
+        this.clauseIndex = UInt.of(json.clauseIndex);
+    }
+
+    toJSON(): LogMetaJSON {
+        return {
+            blockID: this.blockID.toString(),
+            blockNumber: this.blockNumber.valueOf(),
+            blockTimestamp: this.blockTimestamp.valueOf(),
+            txID: this.txID.toString(),
+            txOrigin: this.txOrigin.toString(),
+            clauseIndex: this.clauseIndex.valueOf()
+        } satisfies LogMetaJSON;
+    }
+}
+
+interface LogMetaJSON {
+    blockID: string;
+    blockNumber: number;
+    blockTimestamp: number;
+    txID: string;
+    txOrigin: string;
+    clauseIndex: number;
+}
+
+export { LogMeta, type LogMetaJSON };
diff --git a/packages/net/src/thor/logs/LogSort.ts b/packages/net/src/thor/logs/LogSort.ts
new file mode 100644
index 000000000..d31aec9de
--- /dev/null
+++ b/packages/net/src/thor/logs/LogSort.ts
@@ -0,0 +1,6 @@
+enum LogSort {
+    asc = 'asc',
+    desc = 'desc'
+}
+
+export { LogSort };
diff --git a/packages/net/src/thor/logs/QuerySmartContractEvents.ts b/packages/net/src/thor/logs/QuerySmartContractEvents.ts
new file mode 100644
index 000000000..097298b51
--- /dev/null
+++ b/packages/net/src/thor/logs/QuerySmartContractEvents.ts
@@ -0,0 +1,44 @@
+import { type ThorRequest } from '../ThorRequest';
+import type { HttpClient, HttpPath } from '../../http';
+import {
+    EventLogFilterRequest,
+    type EventLogFilterRequestJSON
+} from './EventLogFilterRequest';
+import type { ThorResponse } from '../ThorResponse';
+import {
+    EventLogsResponse,
+    type EventLogsResponseJSON
+} from './EventLogsResponse';
+
+class QuerySmartContractEvents
+    implements ThorRequest<QuerySmartContractEvents, EventLogsResponse>
+{
+    static readonly PATH: HttpPath = { path: '/logs/event' };
+
+    readonly request: EventLogFilterRequest;
+
+    constructor(request: EventLogFilterRequest) {
+        this.request = request;
+    }
+
+    async askTo(
+        httpClient: HttpClient
+    ): Promise<ThorResponse<QuerySmartContractEvents, EventLogsResponse>> {
+        const response = await httpClient.post(
+            QuerySmartContractEvents.PATH,
+            { query: '' },
+            this.request.toJSON()
+        );
+        const responseBody = (await response.json()) as EventLogsResponseJSON;
+        return {
+            request: this,
+            response: new EventLogsResponse(responseBody)
+        };
+    }
+
+    static of(request: EventLogFilterRequestJSON): QuerySmartContractEvents {
+        return new QuerySmartContractEvents(new EventLogFilterRequest(request));
+    }
+}
+
+export { QuerySmartContractEvents };
diff --git a/packages/net/src/thor/logs/QueryVETTransferEvents.ts b/packages/net/src/thor/logs/QueryVETTransferEvents.ts
new file mode 100644
index 000000000..4296b3e59
--- /dev/null
+++ b/packages/net/src/thor/logs/QueryVETTransferEvents.ts
@@ -0,0 +1,52 @@
+import {
+    TransferLogResponse,
+    type TransferLogResponseJSON,
+    type TransferLogsResponse,
+    type TransferLogsResponseJSON
+} from './TransferLogsResponse';
+import { type ThorRequest } from '../ThorRequest';
+import type { HttpClient, HttpPath } from '../../http';
+import {
+    TransferLogFilterRequest,
+    type TransferLogFilterRequestJSON
+} from './TransferLogFilterRequest';
+import { type ThorResponse } from '../ThorResponse';
+
+class QueryVETTransferEvents
+    implements ThorRequest<QueryVETTransferEvents, TransferLogsResponse>
+{
+    static readonly PATH: HttpPath = { path: '/logs/transfer' };
+
+    readonly request: TransferLogFilterRequest;
+
+    constructor(request: TransferLogFilterRequest) {
+        this.request = request;
+    }
+
+    async askTo(
+        httpClient: HttpClient
+    ): Promise<ThorResponse<QueryVETTransferEvents, TransferLogsResponse>> {
+        const response = await httpClient.post(
+            QueryVETTransferEvents.PATH,
+            { query: '' },
+            this.request.toJSON()
+        );
+        const responseBody =
+            (await response.json()) as TransferLogsResponseJSON;
+        return {
+            request: this,
+            response: responseBody.map(
+                (json: TransferLogResponseJSON): TransferLogResponse =>
+                    new TransferLogResponse(json)
+            ) as TransferLogsResponse
+        };
+    }
+
+    static of(request: TransferLogFilterRequestJSON): QueryVETTransferEvents {
+        return new QueryVETTransferEvents(
+            new TransferLogFilterRequest(request)
+        );
+    }
+}
+
+export { QueryVETTransferEvents };
diff --git a/packages/net/src/thor/logs/TransferCriteria.ts b/packages/net/src/thor/logs/TransferCriteria.ts
new file mode 100644
index 000000000..e7eadbb8c
--- /dev/null
+++ b/packages/net/src/thor/logs/TransferCriteria.ts
@@ -0,0 +1,34 @@
+import { Address } from '@vechain/sdk-core';
+
+class TransferCriteria {
+    readonly txOrigin?: Address;
+    readonly sender?: Address;
+    readonly recipient?: Address;
+
+    constructor(json: TransferCriteriaJSON) {
+        this.txOrigin =
+            json.txOrigin === undefined ? undefined : Address.of(json.txOrigin);
+        this.sender =
+            json.sender === undefined ? undefined : Address.of(json.sender);
+        this.recipient =
+            json.recipient === undefined
+                ? undefined
+                : Address.of(json.recipient);
+    }
+
+    toJSON(): TransferCriteriaJSON {
+        return {
+            txOrigin: this.txOrigin?.toString(),
+            sender: this.sender?.toString(),
+            recipient: this.recipient?.toString()
+        } satisfies TransferCriteriaJSON;
+    }
+}
+
+interface TransferCriteriaJSON {
+    txOrigin?: string;
+    sender?: string;
+    recipient?: string;
+}
+
+export { TransferCriteria, type TransferCriteriaJSON };
diff --git a/packages/net/src/thor/logs/TransferLogFilterRequest.ts b/packages/net/src/thor/logs/TransferLogFilterRequest.ts
new file mode 100644
index 000000000..8257c903f
--- /dev/null
+++ b/packages/net/src/thor/logs/TransferLogFilterRequest.ts
@@ -0,0 +1,46 @@
+import { FilterRange, type FilterRangeJSON } from './FilterRange';
+import { FilterOptions, type FilterOptionsJSON } from './FilterOptions';
+import { type LogSort } from './LogSort';
+import {
+    TransferCriteria,
+    type TransferCriteriaJSON
+} from './TransferCriteria';
+
+class TransferLogFilterRequest {
+    readonly range?: FilterRange;
+    readonly options?: FilterOptions;
+    readonly criteriaSet?: TransferCriteria[];
+    readonly order?: LogSort;
+
+    constructor(json: TransferLogFilterRequestJSON) {
+        this.range =
+            json.range === undefined ? undefined : new FilterRange(json.range);
+        this.options =
+            json.options === undefined
+                ? undefined
+                : new FilterOptions(json.options);
+        this.criteriaSet = json.criteriaSet?.map(
+            (criteriaJSON) => new TransferCriteria(criteriaJSON)
+        );
+        this.order =
+            json.order === undefined ? undefined : (json.order as LogSort);
+    }
+
+    toJSON(): TransferLogFilterRequestJSON {
+        return {
+            range: this.range?.toJSON(),
+            options: this.options?.toJSON(),
+            criteriaSet: this.criteriaSet?.map((criteria) => criteria.toJSON()),
+            order: this.order
+        } satisfies TransferLogFilterRequestJSON;
+    }
+}
+
+interface TransferLogFilterRequestJSON {
+    range?: FilterRangeJSON;
+    options?: FilterOptionsJSON;
+    criteriaSet?: TransferCriteriaJSON[];
+    order?: string;
+}
+
+export { TransferLogFilterRequest, type TransferLogFilterRequestJSON };
diff --git a/packages/net/src/thor/logs/TransferLogsResponse.ts b/packages/net/src/thor/logs/TransferLogsResponse.ts
new file mode 100644
index 000000000..c0b597e8d
--- /dev/null
+++ b/packages/net/src/thor/logs/TransferLogsResponse.ts
@@ -0,0 +1,43 @@
+import { LogMeta, type LogMetaJSON } from './LogMeta';
+import { Address, HexUInt, Units, VET } from '@vechain/sdk-core';
+
+class TransferLogResponse {
+    readonly sender: Address;
+    readonly recipient: Address;
+    readonly amount: VET;
+    readonly meta: LogMeta;
+
+    constructor(json: TransferLogResponseJSON) {
+        this.sender = Address.of(json.sender);
+        this.recipient = Address.of(json.recipient);
+        this.amount = VET.of(HexUInt.of(json.amount).bi, Units.wei);
+        this.meta = new LogMeta(json.meta);
+    }
+
+    toJSON(): TransferLogResponseJSON {
+        return {
+            sender: this.sender.toString(),
+            recipient: this.recipient.toString(),
+            amount: HexUInt.of(this.amount.wei).toString(),
+            meta: this.meta.toJSON()
+        } satisfies TransferLogResponseJSON;
+    }
+}
+
+interface TransferLogResponseJSON {
+    sender: string;
+    recipient: string;
+    amount: string;
+    meta: LogMetaJSON;
+}
+
+interface TransferLogsResponse extends Array<TransferLogResponse> {}
+
+interface TransferLogsResponseJSON extends Array<TransferLogResponseJSON> {}
+
+export {
+    TransferLogResponse,
+    type TransferLogResponseJSON,
+    type TransferLogsResponse,
+    type TransferLogsResponseJSON
+};
diff --git a/packages/net/src/thor/logs/index.ts b/packages/net/src/thor/logs/index.ts
new file mode 100644
index 000000000..2a810e494
--- /dev/null
+++ b/packages/net/src/thor/logs/index.ts
@@ -0,0 +1,10 @@
+export * from './EventCriteria';
+export * from './EventLogFilterRequest';
+export * from './EventLogsResponse';
+export * from './FilterOptions';
+export * from './FilterRange';
+export * from './LogMeta';
+export * from './LogSort';
+export * from './QuerySmartContractEvents';
+export * from './TransferLogsResponse';
+export * from './TransferCriteria';
diff --git a/packages/net/src/thor/node/GetPeersResponse.ts b/packages/net/src/thor/node/GetPeersResponse.ts
new file mode 100644
index 000000000..90fd3b3f5
--- /dev/null
+++ b/packages/net/src/thor/node/GetPeersResponse.ts
@@ -0,0 +1,11 @@
+import { PeerStat, type PeerStatJSON } from './PeerStat';
+
+class GetPeersResponse extends Array<PeerStat> {
+    constructor(json: GetPeersResponseJSON) {
+        super(...json.map((json: PeerStatJSON) => new PeerStat(json)));
+    }
+}
+
+interface GetPeersResponseJSON extends Array<PeerStatJSON> {}
+
+export { GetPeersResponse, type GetPeersResponseJSON };
diff --git a/packages/net/src/thor/node/PeerStat.ts b/packages/net/src/thor/node/PeerStat.ts
new file mode 100644
index 000000000..35e187716
--- /dev/null
+++ b/packages/net/src/thor/node/PeerStat.ts
@@ -0,0 +1,47 @@
+import { BlockId } from '@vechain/sdk-core';
+
+import { UInt } from '../../../../core/src/vcdm/UInt';
+
+class PeerStat {
+    readonly name: string;
+    readonly bestBlockID: BlockId;
+    readonly totalScore: UInt;
+    readonly peerID: string;
+    readonly netAddr: string;
+    readonly inbound: boolean;
+    readonly duration: UInt;
+
+    constructor(json: PeerStatJSON) {
+        this.name = json.name;
+        this.bestBlockID = BlockId.of(json.bestBlockID);
+        this.totalScore = UInt.of(json.totalScore);
+        this.peerID = json.peerID;
+        this.netAddr = json.netAddr;
+        this.inbound = json.inbound;
+        this.duration = UInt.of(json.duration);
+    }
+
+    toJSON(): PeerStatJSON {
+        return {
+            name: this.name,
+            bestBlockID: this.bestBlockID.toString(),
+            totalScore: this.totalScore.valueOf(),
+            peerID: this.peerID,
+            netAddr: this.netAddr,
+            inbound: this.inbound,
+            duration: this.duration.valueOf()
+        };
+    }
+}
+
+interface PeerStatJSON {
+    name: string;
+    bestBlockID: string;
+    totalScore: number;
+    peerID: string;
+    netAddr: string;
+    inbound: boolean;
+    duration: number;
+}
+
+export { PeerStat, type PeerStatJSON };
diff --git a/packages/net/src/thor/node/RetrieveConnectedPeers.ts b/packages/net/src/thor/node/RetrieveConnectedPeers.ts
new file mode 100644
index 000000000..5a07dde0e
--- /dev/null
+++ b/packages/net/src/thor/node/RetrieveConnectedPeers.ts
@@ -0,0 +1,29 @@
+import {
+    GetPeersResponse,
+    type GetPeersResponseJSON
+} from './GetPeersResponse';
+import { type HttpClient, type HttpPath } from '../../http';
+import { type ThorRequest } from '../ThorRequest';
+import { type ThorResponse } from '../ThorResponse';
+
+class RetrieveConnectedPeers
+    implements ThorRequest<RetrieveConnectedPeers, GetPeersResponse>
+{
+    private static readonly PATH: HttpPath = { path: '/node/network/peers' };
+
+    async askTo(
+        httpClient: HttpClient
+    ): Promise<ThorResponse<RetrieveConnectedPeers, GetPeersResponse>> {
+        const response = await httpClient.get(RetrieveConnectedPeers.PATH, {
+            query: ''
+        });
+        const responseBody: GetPeersResponseJSON =
+            (await response.json()) as GetPeersResponseJSON;
+        return {
+            request: this,
+            response: new GetPeersResponse(responseBody)
+        };
+    }
+}
+
+export { RetrieveConnectedPeers };
diff --git a/packages/net/src/thor/node/index.ts b/packages/net/src/thor/node/index.ts
new file mode 100644
index 000000000..95e495133
--- /dev/null
+++ b/packages/net/src/thor/node/index.ts
@@ -0,0 +1,3 @@
+export * from './GetPeersResponse';
+export * from './PeerStat';
+export * from './RetrieveConnectedPeers';
diff --git a/packages/net/src/thor/subscriptions/BeatsSubscription.ts b/packages/net/src/thor/subscriptions/BeatsSubscription.ts
new file mode 100644
index 000000000..c9af00f69
--- /dev/null
+++ b/packages/net/src/thor/subscriptions/BeatsSubscription.ts
@@ -0,0 +1,101 @@
+import { type WebSocketClient, type WebSocketListener } from '../../ws';
+import type { HttpPath } from '../../http';
+import type { BlockId } from '@vechain/sdk-core';
+import {
+    SubscriptionBeat2Response,
+    type SubscriptionBeat2ResponseJSON
+} from './SubscriptionBeat2Response';
+
+class BeatsSubscription implements WebSocketClient, WebSocketListener<unknown> {
+    static readonly PATH: HttpPath = { path: '/subscriptions/beat2' };
+
+    private readonly listeners: Array<
+        WebSocketListener<SubscriptionBeat2Response>
+    > = [];
+
+    private readonly query: BeatsSubscriptionQuery;
+
+    private readonly wsc: WebSocketClient;
+
+    protected constructor(wsc: WebSocketClient, query: BeatsSubscriptionQuery) {
+        this.wsc = wsc;
+        this.query = query;
+    }
+
+    addListener(listener: WebSocketListener<SubscriptionBeat2Response>): this {
+        this.listeners.push(listener);
+        return this;
+    }
+
+    static at(wsc: WebSocketClient): BeatsSubscription {
+        return new BeatsSubscription(wsc, new BeatsSubscriptionQuery());
+    }
+
+    get baseURL(): string {
+        return this.wsc.baseURL;
+    }
+
+    close(): this {
+        this.wsc.close();
+        return this;
+    }
+
+    onClose(event: Event): void {
+        this.listeners.forEach((listener) => {
+            listener.onClose(event);
+        });
+    }
+
+    onError(event: Event): void {
+        this.listeners.forEach((listener) => {
+            listener.onError(event);
+        });
+    }
+
+    onMessage(event: MessageEvent<unknown>): void {
+        const json = JSON.parse(
+            event.data as string
+        ) as SubscriptionBeat2ResponseJSON;
+        const message = new MessageEvent<SubscriptionBeat2Response>(
+            event.type,
+            { data: new SubscriptionBeat2Response(json) }
+        );
+        this.listeners.forEach((listener) => {
+            listener.onMessage(message);
+        });
+    }
+
+    onOpen(event: Event): void {
+        this.listeners.forEach((listener) => {
+            listener.onOpen(event);
+        });
+    }
+
+    open(): this {
+        this.wsc
+            .addListener(this)
+            .open({ path: BeatsSubscription.PATH.path + this.query.query });
+        return this;
+    }
+
+    removeListener(
+        listener: WebSocketListener<SubscriptionBeat2Response>
+    ): this {
+        this.listeners.splice(this.listeners.indexOf(listener), 1);
+        return this;
+    }
+}
+
+class BeatsSubscriptionQuery {
+    readonly pos?: BlockId;
+
+    constructor(pos?: BlockId) {
+        this.pos = pos;
+    }
+
+    get query(): string {
+        return this.pos === undefined ? '' : `?pos=${this.pos}`;
+    }
+}
+
+export { BeatsSubscription };
diff --git a/packages/net/src/thor/subscriptions/BlocksSubscription.ts b/packages/net/src/thor/subscriptions/BlocksSubscription.ts
new file mode 100644
index 000000000..5ee594aa6
--- /dev/null
+++ b/packages/net/src/thor/subscriptions/BlocksSubscription.ts
@@ -0,0 +1,110 @@
+import { type HttpPath, type HttpQuery } from '../../http';
+import { type WebSocketClient, type WebSocketListener } from '../../ws';
+import {
+    SubscriptionBlockResponse,
+    type SubscriptionBlockResponseJSON
+} from './SubscriptionBlockResponse';
+import { type BlockId } from '@vechain/sdk-core';
+
+class BlocksSubscription
+    implements WebSocketClient, WebSocketListener<unknown>
+{
+    static readonly PATH: HttpPath = { path: '/subscriptions/block' };
+
+    private readonly listeners: Array<
+        WebSocketListener<SubscriptionBlockResponse>
+    > = [];
+
+    private readonly query: BlockSubscriptionQuery;
+
+    private readonly wsc: WebSocketClient;
+
+    protected constructor(wsc: WebSocketClient, query: BlockSubscriptionQuery) {
+        this.wsc = wsc;
+        this.query = query;
+    }
+
+    addListener(listener: WebSocketListener<SubscriptionBlockResponse>): this {
+        this.listeners.push(listener);
+        return this;
+    }
+
+    static at(wsc: WebSocketClient): BlocksSubscription {
+        return new BlocksSubscription(wsc, new BlockSubscriptionQuery());
+    }
+
+    atPos(pos?: BlockId): BlocksSubscription {
+        return new BlocksSubscription(
+            this.wsc,
+            new BlockSubscriptionQuery(pos ?? this.query.pos)
+        );
+    }
+
+    get baseURL(): string {
+        return this.wsc.baseURL;
+    }
+
+    close(): this {
+        this.wsc.close();
+        return this;
+    }
+
+    onClose(event: Event): void {
+        this.listeners.forEach((listener) => {
+            listener.onClose(event);
+        });
+    }
+
+    onError(event: Event): void {
+        this.listeners.forEach((listener) => {
+            listener.onError(event);
+        });
+    }
+
+    onMessage(event: MessageEvent<unknown>): void {
+        const json = JSON.parse(
+            event.data as string
+        ) as SubscriptionBlockResponseJSON;
+        const message = new MessageEvent<SubscriptionBlockResponse>(
+            event.type,
+            { data: new SubscriptionBlockResponse(json) }
+        );
+        this.listeners.forEach((listener) => {
+            listener.onMessage(message);
+        });
+    }
+
+    onOpen(event: Event): void {
+        this.listeners.forEach((listener) => {
+            listener.onOpen(event);
+        });
+    }
+
+    open(): this {
+        this.wsc
+            .addListener(this)
+            .open({ path: BlocksSubscription.PATH.path + this.query.query });
+        return this;
+    }
+
+    removeListener(
+        listener: WebSocketListener<SubscriptionBlockResponse>
+    ): this {
+        this.listeners.splice(this.listeners.indexOf(listener), 1);
+        return this;
+    }
+}
+
+class BlockSubscriptionQuery implements HttpQuery {
+    readonly pos?: BlockId;
+
+    constructor(pos?: BlockId) {
+        this.pos = pos;
+    }
+
+    get query(): string {
+        return this.pos === undefined ? '' : `?pos=${this.pos}`;
+    }
+}
+
+export { BlocksSubscription };
diff --git a/packages/net/src/thor/subscriptions/EventsSubscription.ts b/packages/net/src/thor/subscriptions/EventsSubscription.ts
new file mode 100644
index 000000000..4e0ff9af9
--- /dev/null
+++ b/packages/net/src/thor/subscriptions/EventsSubscription.ts
@@ -0,0 +1,191 @@
+import { type WebSocketClient, type WebSocketListener } from '../../ws';
+import { type SubscriptionEventResponse } from './SubscriptionEventResponse';
+import { type HttpPath, type HttpQuery } from '../../http';
+import { type Address, type ThorId } from '@vechain/sdk-core';
+
+class EventsSubscription
+    implements WebSocketClient, WebSocketListener<SubscriptionEventResponse>
+{
+    static readonly PATH: HttpPath = { path: '/subscriptions/event' };
+
+    private readonly listeners: Array<
+        WebSocketListener<SubscriptionEventResponse>
+    > = [];
+
+    private readonly query: EventsSubscriptionQuery;
+
+    private readonly wsc: WebSocketClient;
+
+    protected constructor(
+        wsc: WebSocketClient,
+        query: EventsSubscriptionQuery
+    ) {
+        this.wsc = wsc;
+        this.query = query;
+    }
+
+    addListener(listener: WebSocketListener<SubscriptionEventResponse>): this {
+        this.listeners.push(listener);
+        return this;
+    }
+
+    static at(wsc: WebSocketClient): EventsSubscription {
+        return new EventsSubscription(wsc, new EventsSubscriptionQuery());
+    }
+
+    atPos(pos?: ThorId): EventsSubscription {
+        return new EventsSubscription(
+            this.wsc,
+            new EventsSubscriptionQuery(
+                this.query.addr,
+                pos ?? this.query.pos,
+                this.query.t0,
+                this.query.t1,
+                this.query.t2,
+                this.query.t3
+            )
+        );
+    }
+
+    get baseURL(): string {
+        return this.wsc.baseURL;
+    }
+
+    close(): this {
+        this.wsc.close();
+        return this;
+    }
+
+    onClose(event: Event): void {
+        this.listeners.forEach((listener) => {
+            listener.onClose(event);
+        });
+    }
+
+    onError(event: Event): void {
+        this.listeners.forEach((listener) => {
+            listener.onError(event);
+        });
+    }
+
+    onMessage(event: MessageEvent<unknown>): void {
+        const json = JSON.parse(
+            event.data as string
+        ) as SubscriptionEventResponse;
+        const message = new MessageEvent<SubscriptionEventResponse>(
+            event.type,
+            { data: json }
+        );
+        this.listeners.forEach((listener) => {
+            listener.onMessage(message);
+        });
+    }
+
+    onOpen(event: Event): void {
+        this.listeners.forEach((listener) => {
+            listener.onOpen(event);
+        });
+    }
+
+    open(): this {
+        this.wsc
+            .addListener(this)
+            .open({ path: EventsSubscription.PATH.path + this.query.query });
+        return this;
+    }
+
+    removeListener(
+        listener: WebSocketListener<SubscriptionEventResponse>
+    ): this {
+        this.listeners.splice(this.listeners.indexOf(listener), 1);
+        return this;
+    }
+
+    withContractAddress(contractAddress?: Address): EventsSubscription {
+        return new EventsSubscription(
+            this.wsc,
+            new EventsSubscriptionQuery(
+                contractAddress ?? this.query.addr,
+                this.query.pos,
+                this.query.t0,
+                this.query.t1,
+                this.query.t2,
+                this.query.t3
+            )
+        );
+    }
+
+    withFilters(
+        t0?: ThorId,
+        t1?: ThorId,
+        t2?: ThorId,
+        t3?: ThorId
+    ): EventsSubscription {
+        return new EventsSubscription(
+            this.wsc,
+            new EventsSubscriptionQuery(
+                this.query.addr,
+                this.query.pos,
+                t0 ?? this.query.t0,
+                t1 ?? this.query.t1,
+                t2 ?? this.query.t2,
+                t3 ?? this.query.t3
+            )
+        );
+    }
+}
+
+class EventsSubscriptionQuery implements HttpQuery {
+    readonly addr?: Address;
+    readonly pos?: ThorId;
+    readonly t0?: ThorId;
+    readonly t1?: ThorId;
+    readonly t2?: ThorId;
+    readonly t3?: ThorId;
+
+    constructor(
+        addr?: Address,
+        pos?: ThorId,
+        t0?: ThorId,
+        t1?: ThorId,
+        t2?: ThorId,
+        t3?: ThorId
+    ) {
+        this.addr = addr;
+        this.pos = pos;
+        this.t0 = t0;
+        this.t1 = t1;
+        this.t2 = t2;
+        this.t3 = t3;
+    }
+
+    get query(): string {
+        let query = '';
+        if (this.addr !== undefined) {
+            query += `addr=${this.addr}`;
+        }
+        if (this.pos !== undefined) {
+            if (query.length > 0) query += '&';
+            query += `pos=${this.pos}`;
+        }
+        if (this.t0 !== undefined) {
+            if (query.length > 0) query += '&';
+            query += `t0=${this.t0}`;
+        }
+        if (this.t1 !== undefined) {
+            if (query.length > 0) query += '&';
+            query += `t1=${this.t1}`;
+        }
+        if (this.t2 !== undefined) {
+            if (query.length > 0) query += '&';
+            query += `t2=${this.t2}`;
+        }
+        if (this.t3 !== undefined) {
+            if (query.length > 0) query += '&';
+            query += `t3=${this.t3}`;
+        }
+        return query.length > 0 ? `?${query}` : query;
+    }
+}
+
+export { EventsSubscription };
diff --git a/packages/net/src/thor/subscriptions/NewTransactionSubscription.ts b/packages/net/src/thor/subscriptions/NewTransactionSubscription.ts
new file mode 100644
index 000000000..51a58f203
--- /dev/null
+++ b/packages/net/src/thor/subscriptions/NewTransactionSubscription.ts
@@ -0,0 +1,75 @@
+import { type WebSocketClient, type WebSocketListener } from '../../ws';
+import type { HttpPath } from '../../http';
+import { TXID, type TXIDJSON } from '../transactions/TXID';
+
+class NewTransactionSubscription
+    implements WebSocketClient, WebSocketListener<TXID>
+{
+    static readonly PATH: HttpPath = { path: '/subscriptions/txpool' };
+
+    private readonly listeners: Array<WebSocketListener<TXID>> = [];
+
+    private readonly wsc: WebSocketClient;
+
+    protected constructor(wsc: WebSocketClient) {
+        this.wsc = wsc;
+    }
+
+    addListener(listener: WebSocketListener<TXID>): this {
+        this.listeners.push(listener);
+        return this;
+    }
+
+    static at(wsc: WebSocketClient): NewTransactionSubscription {
+        return new NewTransactionSubscription(wsc);
+    }
+
+    get baseURL(): string {
+        return this.wsc.baseURL;
+    }
+
+    close(): this {
+        this.wsc.close();
+        return this;
+    }
+
+    onClose(event: Event): void {
+        this.listeners.forEach((listener) => {
+            listener.onClose(event);
+        });
+    }
+
+    onError(event: Event): void {
+        this.listeners.forEach((listener) => {
+            listener.onError(event);
+        });
+    }
+
+    onMessage(event: MessageEvent<unknown>): void {
+        const json = JSON.parse(event.data as string) as TXIDJSON;
+        const message = new MessageEvent<TXID>(event.type, {
+            data: new TXID(json)
+        });
+        this.listeners.forEach((listener) => {
+            listener.onMessage(message);
+        });
+    }
+
+    onOpen(event: Event): void {
+        this.listeners.forEach((listener) => {
+            listener.onOpen(event);
+        });
+    }
+
+    open(): this {
+        this.wsc.addListener(this).open(NewTransactionSubscription.PATH);
+        return this;
+    }
+
+    removeListener(listener: WebSocketListener<TXID>): this {
+        this.listeners.splice(this.listeners.indexOf(listener), 1);
+        return this;
+    }
+}
+
+export { NewTransactionSubscription };
diff --git a/packages/net/src/thor/subscriptions/SubscriptionBeat2Response.ts b/packages/net/src/thor/subscriptions/SubscriptionBeat2Response.ts
new file mode 100644
index 000000000..417551247
--- /dev/null
+++ b/packages/net/src/thor/subscriptions/SubscriptionBeat2Response.ts
@@ -0,0 +1,54 @@
+import { BlockId, HexUInt, Units, VTHO } from '@vechain/sdk-core';
+import { UInt } from '../../../../core';
+
+class SubscriptionBeat2Response {
+    readonly gasLimit: VTHO;
+    readonly obsolete: boolean;
+    readonly number: UInt;
+    readonly id: BlockId;
+    readonly parentID: BlockId;
+    readonly timestamp: UInt;
+    readonly txsFeatures: UInt;
+    readonly bloom: HexUInt;
+    readonly k: UInt;
+
+    constructor(json: SubscriptionBeat2ResponseJSON) {
+        this.gasLimit = VTHO.of(json.gasLimit, Units.wei);
+        this.obsolete = json.obsolete;
+        this.number = UInt.of(json.number);
+        this.id = BlockId.of(json.id);
+        this.parentID = BlockId.of(json.parentID);
+        this.timestamp = UInt.of(json.timestamp);
+        this.txsFeatures = UInt.of(json.txsFeatures);
+        this.bloom = HexUInt.of(json.bloom);
+        this.k = UInt.of(json.k);
+    }
+
+    toJSON(): SubscriptionBeat2ResponseJSON {
+        return {
+            gasLimit: Number(this.gasLimit.wei),
+            obsolete: this.obsolete,
+            number: this.number.valueOf(),
+            id: this.id.toString(),
+            parentID: this.parentID.toString(),
+            timestamp: this.timestamp.valueOf(),
+            txsFeatures: this.txsFeatures.valueOf(),
+            bloom: this.bloom.toString(),
+            k: this.k.valueOf()
+        } satisfies SubscriptionBeat2ResponseJSON;
+    }
+}
+
+interface SubscriptionBeat2ResponseJSON {
+    gasLimit: number;
+    obsolete: boolean;
+    number: number;
+    id: string;
+    parentID: string;
+    timestamp: number;
+    txsFeatures: number;
+    bloom: string;
+    k: number;
+}
+
+export { SubscriptionBeat2Response, type SubscriptionBeat2ResponseJSON };
diff --git a/packages/net/src/thor/subscriptions/SubscriptionBlockResponse.ts b/packages/net/src/thor/subscriptions/SubscriptionBlockResponse.ts
new file mode 100644
index 000000000..a72bf3278
--- /dev/null
+++ b/packages/net/src/thor/subscriptions/SubscriptionBlockResponse.ts
@@ -0,0 +1,90 @@
+import { UInt, type TxId } from '../../../../core';
+import { Address, BlockId, ThorId, Units, VTHO } from '@vechain/sdk-core';
+
+class SubscriptionBlockResponse {
+    readonly number: UInt;
+    readonly id: BlockId;
+    readonly size: UInt;
+    readonly parentID: BlockId;
+    readonly timestamp: UInt;
+    readonly gasLimit: VTHO;
+    readonly beneficiary: Address;
+    readonly gasUsed: VTHO;
+    readonly totalScore: UInt;
+    readonly txsRoot: ThorId;
+    readonly txsFeatures: UInt;
+    readonly stateRoot: ThorId;
+    readonly receiptsRoot: ThorId;
+    readonly com: boolean;
+    readonly signer: Address;
+    readonly obsolete: boolean;
+    readonly transactions: TxId[];
+
+    constructor(json: SubscriptionBlockResponseJSON) {
+        this.number = UInt.of(json.number);
+        this.id = BlockId.of(json.id);
+        this.size = UInt.of(json.size);
+        this.parentID = BlockId.of(json.parentID);
+        this.timestamp = UInt.of(json.timestamp);
+        this.gasLimit = VTHO.of(json.gasLimit, Units.wei);
+        this.beneficiary = Address.of(json.beneficiary);
+        this.gasUsed = VTHO.of(json.gasUsed, Units.wei);
+        this.totalScore = UInt.of(json.totalScore);
+        this.txsRoot = ThorId.of(json.txsRoot);
+        this.txsFeatures = UInt.of(json.txsFeatures);
+        this.stateRoot = ThorId.of(json.stateRoot);
+        this.receiptsRoot = ThorId.of(json.receiptsRoot);
+        this.com = json.com;
+        this.signer = Address.of(json.signer);
+        this.obsolete = json.obsolete;
+        this.transactions = json.transactions.map(
+            (txId: string): TxId => ThorId.of(txId)
+        );
+    }
+
+    toJSON(): SubscriptionBlockResponseJSON {
+        return {
+            number: this.number.valueOf(),
+            id: this.id.toString(),
+            size: this.size.valueOf(),
+            parentID: this.parentID.toString(),
+            timestamp: this.timestamp.valueOf(),
+            gasLimit: Number(this.gasLimit.wei),
+            beneficiary: this.beneficiary.toString(),
+            gasUsed: Number(this.gasUsed.wei),
+            totalScore: this.totalScore.valueOf(),
+            txsRoot: this.txsRoot.toString(),
+            txsFeatures: this.txsFeatures.valueOf(),
+            stateRoot: this.stateRoot.toString(),
+            receiptsRoot: this.receiptsRoot.toString(),
+            com: this.com,
+            signer: this.signer.toString(),
+            obsolete: this.obsolete,
+            transactions: this.transactions.map((txId: ThorId) =>
+                txId.toString()
+            )
+        } satisfies SubscriptionBlockResponseJSON;
+    }
+}
+
+interface SubscriptionBlockResponseJSON {
+    number: number;
+    id: string;
+    size: number;
+    parentID: string;
+    timestamp: number;
+    gasLimit: number;
+    beneficiary: string;
+    gasUsed: number;
+    totalScore: number;
+    txsRoot: string;
+    txsFeatures: number;
+    stateRoot: string;
+    receiptsRoot: string;
+    com: boolean;
+    signer: string;
+    obsolete: boolean;
+    transactions: string[];
+}
+
+export { SubscriptionBlockResponse, type SubscriptionBlockResponseJSON };
diff --git a/packages/net/src/thor/subscriptions/SubscriptionEventResponse.ts b/packages/net/src/thor/subscriptions/SubscriptionEventResponse.ts
new file mode 100644
index 000000000..ccd0db17b
--- /dev/null
+++ b/packages/net/src/thor/subscriptions/SubscriptionEventResponse.ts
@@ -0,0 +1,42 @@
+import { LogMeta, type LogMetaJSON } from '../logs';
+import { Address, HexUInt, ThorId } from '@vechain/sdk-core';
+
+class SubscriptionEventResponse {
+    readonly address: Address;
+    readonly topics: ThorId[];
+    readonly data: HexUInt;
+    readonly obsolete: boolean;
+    readonly meta: LogMeta;
+
+    constructor(json: SubscriptionEventResponseJSON) {
+        this.address = Address.of(json.address);
+        this.topics = json.topics.map(
+            (topic: string): ThorId => ThorId.of(topic)
+        );
+        this.data = HexUInt.of(json.data);
+        this.obsolete = json.obsolete;
+        this.meta = new LogMeta(json.meta);
+    }
+
+    toJSON(): SubscriptionEventResponseJSON {
+        return {
+            address: this.address.toString(),
+            topics: this.topics.map((topic: ThorId): string =>
+                topic.toString()
+            ),
+            data: this.data.toString(),
+            obsolete: this.obsolete,
+            meta: this.meta.toJSON()
+        };
+    }
+}
+
+interface SubscriptionEventResponseJSON {
+    address: string;
+    topics: string[];
+    data: string;
+    obsolete: boolean;
+    meta: LogMetaJSON;
+}
+
+export { SubscriptionEventResponse, type SubscriptionEventResponseJSON };
diff --git a/packages/net/src/thor/subscriptions/SubscriptionTransferResponse.ts b/packages/net/src/thor/subscriptions/SubscriptionTransferResponse.ts
new file mode 100644
index 000000000..7d1922db6
--- /dev/null
+++ b/packages/net/src/thor/subscriptions/SubscriptionTransferResponse.ts
@@ -0,0 +1,38 @@
+import { LogMeta, type LogMetaJSON } from '../logs';
+import { Address, HexUInt, Units, VET } from '@vechain/sdk-core';
+
+class SubscriptionTransferResponse {
+    readonly sender: Address;
+    readonly recipient: Address;
+    readonly amount: VET;
+    readonly obsolete: boolean;
+    readonly meta: LogMeta;
+
+    constructor(json: SubscriptionTransferJSON) {
+        this.sender = Address.of(json.sender);
+        this.recipient = Address.of(json.recipient);
+        this.amount = VET.of(HexUInt.of(json.amount).bi, Units.wei);
+        this.obsolete = json.obsolete;
+        this.meta = new LogMeta(json.meta);
+    }
+
+    toJSON(): SubscriptionTransferJSON {
+        return {
+            sender: this.sender.toString(),
+            recipient: this.recipient.toString(),
+            amount: HexUInt.of(this.amount.wei).toString(),
+            obsolete: this.obsolete,
+            meta: this.meta.toJSON()
+        } satisfies SubscriptionTransferJSON;
+    }
+}
+
+interface SubscriptionTransferJSON {
+    sender: string;
+    recipient: string;
+    amount: string;
+    obsolete: boolean;
+    meta: LogMetaJSON;
+}
+
+export { SubscriptionTransferResponse, type SubscriptionTransferJSON };
diff --git a/packages/net/src/thor/subscriptions/TransfersSubscription.ts b/packages/net/src/thor/subscriptions/TransfersSubscription.ts
new file mode 100644
index 000000000..8e4d6c009
--- /dev/null
+++ b/packages/net/src/thor/subscriptions/TransfersSubscription.ts
@@ -0,0 +1,128 @@
+import { type HttpPath, type HttpQuery } from '../../http';
+import { type Address, type BlockId } from '@vechain/sdk-core';
+import { type WebSocketClient, type WebSocketListener } from '../../ws';
+import { type SubscriptionTransferResponse } from './SubscriptionTransferResponse';
+
+class TransfersSubscription
+    implements WebSocketClient, WebSocketListener<SubscriptionTransferResponse>
+{
+    static readonly PATH: HttpPath = { path: '/subscriptions/transfer' };
+
+    private readonly listeners: Array<
+        WebSocketListener<SubscriptionTransferResponse>
+    > = [];
+
+    private readonly query: TransfersSubscriptionQuery;
+
+    private readonly wsc: WebSocketClient;
+
+    protected constructor(
+        wsc: WebSocketClient,
+        query: TransfersSubscriptionQuery
+    ) {
+        this.wsc = wsc;
+        this.query = query;
+    }
+
+    addListener(listener: WebSocketListener<unknown>): this {
+        this.listeners.push(listener);
+        return this;
+    }
+
+    close(): this {
+        this.wsc.close();
+        return this;
+    }
+
+    open(): this {
+        this.wsc.addListener(this).open({
+            path: TransfersSubscription.PATH.path + this.query.query
+        });
+        return this;
+    }
+
+    removeListener(listener: WebSocketListener<unknown>): this {
+        this.listeners.splice(this.listeners.indexOf(listener), 1);
+        return this;
+    }
+
+    static at(wsc: WebSocketClient): TransfersSubscription {
+        return new TransfersSubscription(wsc, new TransfersSubscriptionQuery());
+    }
+
+    get baseURL(): string {
+        return this.wsc.baseURL;
+    }
+
+    onClose(event: Event): void {
+        this.listeners.forEach((listener) => {
+            listener.onClose(event);
+        });
+    }
+
+    onError(event: Event): void {
+        this.listeners.forEach((listener) => {
+            listener.onError(event);
+        });
+    }
+
+    onMessage(event: MessageEvent<unknown>): void {
+        const json = JSON.parse(
+            event.data as string
+        ) as SubscriptionTransferResponse;
+        const message = new MessageEvent<SubscriptionTransferResponse>(
+            event.type,
+            { data: json }
+        );
+        this.listeners.forEach((listener) => {
+            listener.onMessage(message);
+        });
+    }
+
+    onOpen(event: Event): void {
+        this.listeners.forEach((listener) => {
+            listener.onOpen(event);
+        });
+    }
+}
+
+class TransfersSubscriptionQuery implements HttpQuery {
+    readonly pos?: BlockId;
+    readonly recipient?: Address;
+    readonly sender?: Address;
+    readonly txOrigin?: Address;
+
+    constructor(
+        pos?: BlockId,
+        recipient?: Address,
+        sender?: Address,
+        txOrigin?: Address
+    ) {
+        this.pos = pos;
+        this.recipient = recipient;
+        this.sender = sender;
+        this.txOrigin = txOrigin;
+    }
+
+    get query(): string {
+        let query = '';
+        if (this.pos !== undefined) {
+            query += `pos=${this.pos}`;
+        }
+        if (this.recipient !== undefined) {
+            if (query.length > 0) query += '&';
+            query += `recipient=${this.recipient}`;
+        }
+        if (this.sender !== undefined) {
+            if (query.length > 0) query += '&';
+            query += `sender=${this.sender}`;
+        }
+        if (this.txOrigin !== undefined) {
+            if (query.length > 0) query += '&';
+            query += `txOrigin=${this.txOrigin}`;
+        }
+        return query.length > 0 ? `?${query}` : query;
+    }
+}
+
+export { TransfersSubscription };
diff --git a/packages/net/src/thor/subscriptions/index.ts b/packages/net/src/thor/subscriptions/index.ts
new file mode 100644
index 000000000..4f8935c99
--- /dev/null
+++ b/packages/net/src/thor/subscriptions/index.ts
@@ -0,0 +1,9 @@
+export * from './BeatsSubscription';
+export * from './BlocksSubscription';
+export * from './EventsSubscription';
+export * from './NewTransactionSubscription';
+export * from './SubscriptionBeat2Response';
+export * from './SubscriptionBlockResponse';
+export * from './SubscriptionEventResponse';
+export * from './SubscriptionTransferResponse';
+export * from './TransfersSubscription';
diff --git a/packages/net/src/thor/transactions/Clause.ts b/packages/net/src/thor/transactions/Clause.ts
new file mode 100644
index 000000000..936115770
--- /dev/null
+++ b/packages/net/src/thor/transactions/Clause.ts
@@ -0,0 +1,35 @@
+import { Address, HexUInt, VET } from '@vechain/sdk-core';
+
+class Clause {
+    readonly to?: Address;
+    readonly value: VET;
+    readonly data: HexUInt;
+
+    constructor(json: ClauseJSON) {
+        this.to =
+            json.to !== undefined && json.to != null
+                ? Address.of(json.to)
+                : undefined;
+        this.value = VET.of(json.value);
+        this.data = HexUInt.of(json.data);
+    }
+
+    toJSON(): ClauseJSON {
+        return {
+            to:
+                this.to !== undefined && this.to !== null
+                    ? this.to.toString()
+                    : undefined,
+            value: HexUInt.of(this.value.wei).toString(),
+            data: this.data.toString()
+        } satisfies ClauseJSON;
+    }
+}
+
+interface ClauseJSON {
+    to?: string | null;
+    value: string;
+    data: string;
+}
+
+export { Clause, type ClauseJSON };
diff --git a/packages/net/src/thor/transactions/Event.ts b/packages/net/src/thor/transactions/Event.ts
new file mode 100644
index 000000000..0336c0cf7
--- /dev/null
+++ b/packages/net/src/thor/transactions/Event.ts
@@ -0,0 +1,33 @@
+import { Address, HexUInt, ThorId } from '@vechain/sdk-core';
+
+class Event {
+    readonly address: Address;
+    readonly topics: ThorId[];
+    readonly data: HexUInt;
+
+    constructor(json: EventJSON) {
+        this.address = Address.of(json.address);
+        this.topics = json.topics.map(
+            (topic: string): ThorId => ThorId.of(topic)
+        );
+        this.data = HexUInt.of(json.data);
+    }
+
+    toJSON(): EventJSON {
+        return {
+            address: this.address.toString(),
+            topics: this.topics.map((topic: ThorId): string =>
+                topic.toString()
+            ),
+            data: this.data.toString()
+        } satisfies EventJSON;
+    }
+}
+
+interface EventJSON {
+    address: string;
+    topics: string[];
+    data: string;
+}
+
+export { Event, type EventJSON };
diff --git a/packages/net/src/thor/transactions/GetRawTxResponse.ts b/packages/net/src/thor/transactions/GetRawTxResponse.ts
new file mode 100644
index 000000000..3546620c9
--- /dev/null
+++ b/packages/net/src/thor/transactions/GetRawTxResponse.ts
@@ -0,0 +1,26 @@
+import { HexUInt } from '@vechain/sdk-core';
+import { TxMeta, type TxMetaJSON } from './TxMeta';
+
+class GetRawTxResponse {
+    readonly raw: HexUInt;
+    readonly meta: TxMeta;
+
+    constructor(json: GetRawTxResponseJSON) {
+        this.raw = HexUInt.of(json.raw);
+        this.meta = new TxMeta(json.meta);
+    }
+
+    toJSON(): GetRawTxResponseJSON {
+        return {
+            raw: this.raw.toString(),
+            meta: this.meta.toJSON()
+        } satisfies GetRawTxResponseJSON;
+    }
+}
+
+interface GetRawTxResponseJSON {
+    raw: string;
+    meta: TxMetaJSON;
+}
+
+export { GetRawTxResponse, type GetRawTxResponseJSON };
diff --git a/packages/net/src/thor/transactions/GetTxReceiptResponse.ts b/packages/net/src/thor/transactions/GetTxReceiptResponse.ts
new file mode 100644
index 000000000..6cd583f98
--- /dev/null
+++ b/packages/net/src/thor/transactions/GetTxReceiptResponse.ts
@@ -0,0 +1,24 @@
+import { Receipt, type ReceiptJSON } from './Receipt';
+import { ReceiptMeta, type ReceiptMetaJSON } from './ReceiptMeta';
+
+class GetTxReceiptResponse extends Receipt {
+    readonly meta: ReceiptMeta;
+
+    constructor(json: GetTxReceiptResponseJSON) {
+        super(json);
+        this.meta = new ReceiptMeta(json.meta);
+    }
+
+    toJSON(): GetTxReceiptResponseJSON {
+        return {
+            ...super.toJSON(),
+            meta: this.meta.toJSON()
+        } satisfies GetTxReceiptResponseJSON;
+    }
+}
+
+interface GetTxReceiptResponseJSON extends ReceiptJSON {
+    meta: ReceiptMetaJSON;
+}
+
+export { GetTxReceiptResponse, type GetTxReceiptResponseJSON };
diff --git a/packages/net/src/thor/transactions/GetTxResponse.ts b/packages/net/src/thor/transactions/GetTxResponse.ts
new file mode 100644
index 000000000..6c2c0d41e
--- /dev/null
+++ b/packages/net/src/thor/transactions/GetTxResponse.ts
@@ -0,0 +1,85 @@
+import { Clause, type ClauseJSON } from './Clause';
+import { TxMeta, type TxMetaJSON } from './TxMeta';
+import { Address, BlockId, VTHO } from '@vechain/sdk-core';
+import { UInt } from '../../../../core/src';
+
+import { Nonce } from '../../../../core/src/vcdm/Nonce';
+import { TxId } from '../../../../core/src/vcdm/BlockId';
+
+class GetTxResponse {
+    readonly id: TxId;
+    readonly origin: Address;
+    readonly delegator: Address | null;
+    readonly size: UInt;
+    readonly chainTag: UInt;
+    readonly blockRef: BlockId;
+    readonly expiration: UInt;
+    readonly clauses: Clause[];
+    readonly gasPriceCoef: UInt;
+    readonly gas: VTHO;
+    readonly dependsOn?: TxId | null;
+    readonly nonce: Nonce;
+    readonly meta: TxMeta;
+
+    constructor(json: GetTxResponseJSON) {
+        this.id = TxId.of(json.id);
+        this.origin = Address.of(json.origin);
+        this.delegator =
+            json.delegator !== null ? Address.of(json.delegator) : null;
+        this.size = UInt.of(json.size);
+        this.chainTag = UInt.of(json.chainTag);
+        this.blockRef = BlockId.of(json.blockRef);
+        this.expiration = UInt.of(json.expiration);
+        this.clauses = json.clauses.map((clauseJSON: ClauseJSON) => {
+            return new Clause(clauseJSON);
+        });
+        this.gasPriceCoef = UInt.of(json.gasPriceCoef);
+        this.gas = VTHO.of(json.gas);
+        this.dependsOn =
+            json.dependsOn !== undefined && json.dependsOn !== null
+                ? TxId.of(json.dependsOn)
+                : undefined;
+        this.nonce = Nonce.of(json.nonce);
+        this.meta = new TxMeta(json.meta);
+    }
+
+    toJSON(): GetTxResponseJSON {
+        return {
+            id: this.id.toString(),
+            origin: this.origin.toString(),
+            delegator:
+                this.delegator != null ? this.delegator.toString() : null,
+            size: this.size.valueOf(),
+            chainTag: this.chainTag.valueOf(),
+            blockRef: this.blockRef.toString(),
+            expiration: this.expiration.valueOf(),
+            clauses: this.clauses?.map((clause) => clause.toJSON()),
+            gasPriceCoef: this.gasPriceCoef.valueOf(),
+            gas: Number(this.gas.wei),
+            dependsOn:
+                this.dependsOn !== undefined && this.dependsOn !== null
+                    ? this.dependsOn.toString()
+                    : undefined,
+            nonce: this.nonce.toString(),
+            meta: this.meta.toJSON()
+        } satisfies GetTxResponseJSON;
+    }
+}
+
+interface GetTxResponseJSON {
+    id: string;
+    origin: string;
+    delegator: string | null; // The end point at https://mainnet.vechain.org/doc/stoplight-ui/#/schemas/GetTxResponse specifically returns `null`.
+    size: number;
+    chainTag: number;
+    blockRef: string;
+    expiration: number;
+    clauses: ClauseJSON[];
+    gasPriceCoef: number;
+    gas: number;
+    dependsOn?: string | null;
+    nonce: string;
+    meta: TxMetaJSON;
+}
+
+export { GetTxResponse, type GetTxResponseJSON };
diff --git a/packages/net/src/thor/transactions/Receipt.ts b/packages/net/src/thor/transactions/Receipt.ts
new file mode 100644
index 000000000..40ac4da90
--- /dev/null
+++ b/packages/net/src/thor/transactions/Receipt.ts
@@ -0,0 +1,42 @@
+import { ReceiptOutput, type ReceiptOutputJSON } from './ReceiptOutput';
+import { Address, Hex, HexUInt, Units, VTHO } from '@vechain/sdk-core';
+
+class Receipt {
+    readonly gasUsed: VTHO;
+    readonly gasPayer: Address;
+    readonly paid: VTHO;
+    readonly reward: VTHO;
+    readonly reverted: boolean;
+    readonly outputs: ReceiptOutput[];
+
+    constructor(json: ReceiptJSON) {
+        this.gasUsed = VTHO.of(json.gasUsed);
+        this.gasPayer = Address.of(json.gasPayer);
+        this.paid = VTHO.of(Hex.of(json.paid).bi, Units.wei);
+        this.reward = VTHO.of(Hex.of(json.reward).bi, Units.wei);
+        this.reverted = json.reverted;
+        this.outputs = json.outputs.map((output) => new ReceiptOutput(output));
+    }
+
+    toJSON(): ReceiptJSON {
+        return {
+            gasUsed: Number(this.gasUsed.wei),
+            gasPayer: this.gasPayer.toString(),
+            paid: HexUInt.of(this.paid.wei).toString(),
+            reward: HexUInt.of(this.reward.wei).toString(),
+            reverted: this.reverted,
+            outputs: this.outputs.map((output) => output.toJSON())
+        };
+    }
+}
+
+interface ReceiptJSON {
+    gasUsed: number;
+    gasPayer: string;
+    paid: string;
+    reward: string;
+    reverted: boolean;
+    outputs: ReceiptOutputJSON[];
+}
+
+export { Receipt, type ReceiptJSON };
diff --git a/packages/net/src/thor/transactions/ReceiptMeta.ts b/packages/net/src/thor/transactions/ReceiptMeta.ts
new file mode 100644
index 000000000..47216cf0d
--- /dev/null
+++ b/packages/net/src/thor/transactions/ReceiptMeta.ts
@@ -0,0 +1,30 @@
+import { TxMeta, type TxMetaJSON } from './TxMeta';
+import { Address } from '@vechain/sdk-core';
+
+import { TxId } from '../../../../core/src/vcdm/BlockId';
+
+class ReceiptMeta extends TxMeta {
+    readonly txID: TxId;
+    readonly txOrigin: Address;
+
+    constructor(json: ReceiptMetaJSON) {
+        super(json);
+        this.txID = TxId.of(json.txID);
+        this.txOrigin = Address.of(json.txOrigin);
+    }
+
+    toJSON(): ReceiptMetaJSON {
+        return {
+            ...super.toJSON(),
+            txID: this.txID.toString(),
+            txOrigin: this.txOrigin.toString()
+        } satisfies ReceiptMetaJSON;
+    }
+}
+
+interface ReceiptMetaJSON extends TxMetaJSON {
+    txID: string;
+    txOrigin: string;
+}
+
+export { ReceiptMeta, type ReceiptMetaJSON };
diff --git a/packages/net/src/thor/transactions/ReceiptOutput.ts b/packages/net/src/thor/transactions/ReceiptOutput.ts
new file mode 100644
index 000000000..01dbde469
--- /dev/null
+++ b/packages/net/src/thor/transactions/ReceiptOutput.ts
@@ -0,0 +1,37 @@
+import { Event, type EventJSON } from './Event';
+import { Transfer, type TransferJSON } from './Transfer';
+import { Address } from '@vechain/sdk-core';
+
+class ReceiptOutput {
+    readonly contractAddress: Address;
+    readonly events: Event[];
+    readonly transfers: Transfer[];
+
+    constructor(json: ReceiptOutputJSON) {
+        this.contractAddress = Address.of(json.contractAddress);
+        this.events = json.events.map(
+            (eventJSON): Event => new Event(eventJSON)
+        );
+        this.transfers = json.transfers.map(
+            (transferJSON): Transfer => new Transfer(transferJSON)
+        );
+    }
+
+    toJSON(): ReceiptOutputJSON {
+        return {
+            contractAddress: this.contractAddress.toString(),
+            events: this.events.map((event): EventJSON => event.toJSON()),
+            transfers: this.transfers.map(
+                (transfer): TransferJSON => transfer.toJSON()
+            )
+        } satisfies ReceiptOutputJSON;
+    }
+}
+
+interface ReceiptOutputJSON {
+    contractAddress: string;
+    events: EventJSON[];
+    transfers: TransferJSON[];
+}
+
+export { ReceiptOutput, type ReceiptOutputJSON };
diff --git a/packages/net/src/thor/transactions/RetrieveRawTransactionByID.ts b/packages/net/src/thor/transactions/RetrieveRawTransactionByID.ts
new file mode 100644
index 000000000..308098d0a
--- /dev/null
+++ b/packages/net/src/thor/transactions/RetrieveRawTransactionByID.ts
@@ -0,0 +1,77 @@
+import {
+    RetrieveTransactionByIDPath,
+    RetrieveTransactionByIDQuery
+} from './RetrieveTransactionByID';
+import {
+    GetRawTxResponse,
+    type GetRawTxResponseJSON
+} from './GetRawTxResponse';
+import { type BlockId } from '@vechain/sdk-core';
+import { type HttpClient } from '../../http';
+import { type ThorRequest } from '../ThorRequest';
+import { type ThorResponse } from '../ThorResponse';
+
+import { type TxId } from '../../../../core/src/vcdm/BlockId';
+
+class RetrieveRawTransactionByID
+    implements ThorRequest<RetrieveRawTransactionByID, GetRawTxResponse>
+{
+    readonly path: RetrieveRawTransactionByIDPath;
+
+    readonly query: RetrieveRawTransactionByIDQuery;
+
+    constructor(
+        path: RetrieveRawTransactionByIDPath,
+        query: RetrieveRawTransactionByIDQuery
+    ) {
+        this.path = path;
+        this.query = query;
+    }
+
+    async askTo(
+        httpClient: HttpClient
+    ): Promise<ThorResponse<RetrieveRawTransactionByID, GetRawTxResponse>> {
+        const response = await httpClient.get(this.path, this.query);
+        const responseBody = (await response.json()) as GetRawTxResponseJSON;
+        return {
+            request: this,
+            response: new GetRawTxResponse(responseBody)
+        };
+    }
+
+    static of(txId: TxId): RetrieveRawTransactionByID {
+        return new RetrieveRawTransactionByID(
+            new RetrieveRawTransactionByIDPath(txId),
+            new RetrieveRawTransactionByIDQuery(undefined, false)
+        );
+    }
+
+    withHead(head?: BlockId): RetrieveRawTransactionByID {
+        return new RetrieveRawTransactionByID(
+            this.path,
+            new RetrieveRawTransactionByIDQuery(head, this.query.pending)
+        );
+    }
+
+    withPending(pending: boolean = true): RetrieveRawTransactionByID {
+        return new RetrieveRawTransactionByID(
+            this.path,
+            new RetrieveRawTransactionByIDQuery(this.query.head, pending)
+        );
+    }
+}
+
+class RetrieveRawTransactionByIDPath extends RetrieveTransactionByIDPath {}
+
+class RetrieveRawTransactionByIDQuery extends RetrieveTransactionByIDQuery {
+    get query(): string {
+        const head = this.head === null ? '' : `${this.head}&`;
+        return `?${head}pending=${this.pending}&raw=true`;
+    }
+}
+
+export {
+    RetrieveRawTransactionByID,
+    RetrieveRawTransactionByIDPath,
+    RetrieveRawTransactionByIDQuery
+};
diff --git a/packages/net/src/thor/transactions/RetrieveTransactionByID.ts b/packages/net/src/thor/transactions/RetrieveTransactionByID.ts
new file mode 100644
index 000000000..1ae1abc32
--- /dev/null
+++ b/packages/net/src/thor/transactions/RetrieveTransactionByID.ts
@@ -0,0 +1,88 @@
+import { type BlockId } from '@vechain/sdk-core';
+import { type HttpClient, type HttpPath, type HttpQuery } from '../../http';
+import { GetTxResponse, type GetTxResponseJSON } from './GetTxResponse';
+import { type ThorRequest } from '../ThorRequest';
+import { type ThorResponse } from '../ThorResponse';
+
+import { type TxId } from '../../../../core/src/vcdm/BlockId';
+
+class RetrieveTransactionByID
+    implements ThorRequest<RetrieveTransactionByID, GetTxResponse>
+{
+    readonly path: RetrieveTransactionByIDPath;
+
+    readonly query: RetrieveTransactionByIDQuery;
+
+    constructor(
+        path: RetrieveTransactionByIDPath,
+        query: RetrieveTransactionByIDQuery
+    ) {
+        this.path = path;
+        this.query = query;
+    }
+
+    async askTo(
+        httpClient: HttpClient
+    ): Promise<ThorResponse<RetrieveTransactionByID, GetTxResponse>> {
+        const response = await httpClient.get(this.path, this.query);
+        const responseBody = (await response.json()) as GetTxResponseJSON;
+        return {
+            request: this,
+            response: new GetTxResponse(responseBody)
+        };
+    }
+
+    static of(txId: TxId): RetrieveTransactionByID {
+        return new RetrieveTransactionByID(
+            new RetrieveTransactionByIDPath(txId),
+            new RetrieveTransactionByIDQuery(undefined, false)
+        );
+    }
+
+    withHead(head?: BlockId): RetrieveTransactionByID {
+        return new RetrieveTransactionByID(
+            this.path,
+            new RetrieveTransactionByIDQuery(head, this.query.pending)
+        );
+    }
+
+    withPending(pending: boolean = true): RetrieveTransactionByID {
+        return new RetrieveTransactionByID(
+            this.path,
+            new RetrieveTransactionByIDQuery(this.query.head, pending)
+        );
+    }
+}
+
+class RetrieveTransactionByIDPath implements HttpPath {
+    readonly txId: TxId;
+
+    constructor(txId: TxId) {
+        this.txId = txId;
+    }
+
+    get path(): string {
+        return `/transactions/${this.txId}`;
+    }
+}
+
+class RetrieveTransactionByIDQuery implements HttpQuery {
+    readonly head?: BlockId;
+    readonly pending: boolean;
+
+    constructor(head: BlockId | undefined, pending: boolean) {
+        this.head = head;
+        this.pending = pending;
+    }
+
+    get query(): string {
+        const head = this.head === undefined ? '' : `${this.head}&`;
+        return `?${head}pending=${this.pending}&raw=false`;
+    }
+}
+
+export {
+    RetrieveTransactionByID,
+    RetrieveTransactionByIDPath,
+    RetrieveTransactionByIDQuery
+};
diff --git a/packages/net/src/thor/transactions/RetrieveTransactionReceipt.ts b/packages/net/src/thor/transactions/RetrieveTransactionReceipt.ts
new file mode 100644
index 000000000..a89792b66
--- /dev/null
+++ b/packages/net/src/thor/transactions/RetrieveTransactionReceipt.ts
@@ -0,0 +1,85 @@
+import { type BlockId } from '@vechain/sdk-core';
+import { type HttpClient, type HttpPath, type HttpQuery } from '../../http';
+import {
+    GetTxReceiptResponse,
+    type GetTxReceiptResponseJSON
+} from './GetTxReceiptResponse';
+import { type ThorRequest } from '../ThorRequest';
+import { type ThorResponse } from '../ThorResponse';
+
+import { type TxId } from '../../../../core/src/vcdm/BlockId';
+
+class RetrieveTransactionReceipt
+    implements ThorRequest<RetrieveTransactionReceipt, GetTxReceiptResponse>
+{
+    readonly path: RetrieveTransactionReceiptPath;
+
+    readonly query: RetrieveTransactionReceiptQuery;
+
+    constructor(
+        path: RetrieveTransactionReceiptPath,
+        query: RetrieveTransactionReceiptQuery
+    ) {
+        this.path = path;
+        this.query = query;
+    }
+
+    async askTo(
+        httpClient: HttpClient
+    ): Promise<ThorResponse<RetrieveTransactionReceipt, GetTxReceiptResponse>> {
+        const response = await httpClient.get(this.path, this.query);
+        const responseBody =
+            (await response.json()) as GetTxReceiptResponseJSON;
+        return {
+            request: this,
+            response: new GetTxReceiptResponse(responseBody)
+        } satisfies ThorResponse<
+            RetrieveTransactionReceipt,
+            GetTxReceiptResponse
+        >;
+    }
+
+    static of(txId: TxId): RetrieveTransactionReceipt {
+        return new RetrieveTransactionReceipt(
+            new RetrieveTransactionReceiptPath(txId),
+            new RetrieveTransactionReceiptQuery(undefined)
+        );
+    }
+
+    withHead(head?: BlockId): RetrieveTransactionReceipt {
+        return new RetrieveTransactionReceipt(
+            this.path,
+            new RetrieveTransactionReceiptQuery(head)
+        );
+    }
+}
+
+class RetrieveTransactionReceiptPath implements HttpPath {
+    readonly txId: TxId;
+
+    constructor(txId: TxId) {
+        this.txId = txId;
+    }
+
+    get path(): string {
+        return `/transactions/${this.txId}/receipt`;
+    }
+}
+
+class RetrieveTransactionReceiptQuery implements HttpQuery {
+    readonly head?: BlockId;
+
+    constructor(head: BlockId | undefined) {
+        this.head = head;
+    }
+
+    get query(): string {
+        return this.head === undefined ? '' : `${this.head}&`;
+    }
+}
+
+export {
+    RetrieveTransactionReceipt,
+    RetrieveTransactionReceiptPath,
+    RetrieveTransactionReceiptQuery
+};
diff --git a/packages/net/src/thor/transactions/SendTransaction.ts b/packages/net/src/thor/transactions/SendTransaction.ts
new file mode 100644
index 000000000..4f4aa6fc3
--- /dev/null
+++ b/packages/net/src/thor/transactions/SendTransaction.ts
@@ -0,0 +1,38 @@
+import { HexUInt } from '@vechain/sdk-core';
+import { type HttpClient, type HttpPath } from '../../http';
+import { type ThorRequest } from '../ThorRequest';
+import { type ThorResponse } from '../ThorResponse';
+import { TXID, type TXIDJSON } from './TXID';
+
+class SendTransaction implements ThorRequest<SendTransaction, TXID> {
+    static readonly PATH: HttpPath = { path: '/transactions' };
+
+    readonly encoded: Uint8Array;
+
+    constructor(encoded: Uint8Array) {
+        this.encoded = encoded;
+    }
+
+    async askTo(
+        httpClient: HttpClient
+    ): Promise<ThorResponse<SendTransaction, TXID>> {
+        const response = await httpClient.post(
+            SendTransaction.PATH,
+            { query: '' },
+            {
+                raw: HexUInt.of(this.encoded).toString()
+            }
+        );
+        const json = (await response.json()) as TXIDJSON;
+        return {
+            request: this,
+            response: new TXID(json)
+        } satisfies ThorResponse<SendTransaction, TXID>;
+    }
+
+    static of(encoded: Uint8Array): SendTransaction {
+        return new SendTransaction(encoded);
+    }
+}
+
+export { SendTransaction };
diff --git a/packages/net/src/thor/transactions/TXID.ts b/packages/net/src/thor/transactions/TXID.ts
new file mode 100644
index 000000000..d30f999d8
--- /dev/null
+++ b/packages/net/src/thor/transactions/TXID.ts
@@ -0,0 +1,21 @@
+import { ThorId } from '@vechain/sdk-core';
+
+class TXID {
+    readonly id: ThorId;
+
+    constructor(json: TXIDJSON) {
+        this.id = ThorId.of(json.id);
+    }
+
+    toJSON(): TXIDJSON {
+        return {
+            id: this.id.toString()
+        } satisfies TXIDJSON;
+    }
+}
+
+interface TXIDJSON {
+    id: string;
+}
+
+export { TXID, type TXIDJSON };
diff --git a/packages/net/src/thor/transactions/Transfer.ts b/packages/net/src/thor/transactions/Transfer.ts
new file mode 100644
index 000000000..ea63164d1
--- /dev/null
+++ b/packages/net/src/thor/transactions/Transfer.ts
@@ -0,0 +1,29 @@
+import { Address, VET } from '@vechain/sdk-core';
+
+class Transfer {
+    readonly sender: Address;
+    readonly recipient: Address;
+    readonly amount: VET;
+
+    constructor(json: TransferJSON) {
+        this.sender = Address.of(json.sender);
+        this.recipient = Address.of(json.recipient);
+        this.amount = VET.of(json.amount);
+    }
+
+    toJSON(): TransferJSON {
+        return {
+            sender: this.sender.toString(),
+            recipient: this.recipient.toString(),
+            amount: this.amount.toString()
+        } satisfies TransferJSON;
+    }
+}
+
+interface TransferJSON {
+    sender: string;
+    recipient: string;
+    amount: string;
+}
+
+export { Transfer, type TransferJSON };
diff --git a/packages/net/src/thor/transactions/TxMeta.ts b/packages/net/src/thor/transactions/TxMeta.ts
new file mode 100644
index 000000000..589a4ec75
--- /dev/null
+++ b/packages/net/src/thor/transactions/TxMeta.ts
@@ -0,0 +1,31 @@
+import { BlockId } from '@vechain/sdk-core';
+
+import { UInt } from '../../../../core/src/vcdm/UInt';
+
+class TxMeta {
+    readonly blockID: BlockId;
+    readonly blockNumber: UInt;
+    readonly blockTimestamp: bigint;
+
+    constructor(json: TxMetaJSON) {
+        this.blockID = BlockId.of(json.blockID);
+        this.blockNumber = UInt.of(json.blockNumber);
+        this.blockTimestamp = json.blockTimestamp;
+    }
+
+    toJSON(): TxMetaJSON {
+        return {
+            blockID: this.blockID.toString(),
+            blockNumber: this.blockNumber.valueOf(),
+            blockTimestamp: this.blockTimestamp
+        } satisfies TxMetaJSON;
+    }
+}
+
+interface TxMetaJSON {
+    blockID: string;
+    blockNumber: number;
+    blockTimestamp: bigint;
+}
+
+export { TxMeta, type TxMetaJSON };
diff --git a/packages/net/src/thor/transactions/index.ts b/packages/net/src/thor/transactions/index.ts
new file mode 100644
index 000000000..65504bc01
--- /dev/null
+++ b/packages/net/src/thor/transactions/index.ts
@@ -0,0 +1,15 @@
+export * from './Clause';
+export * from './Event';
+export * from './GetRawTxResponse';
+export * from './GetTxResponse';
+export * from './GetTxReceiptResponse';
+export * from './Receipt';
+export * from './ReceiptMeta';
+export * from './ReceiptOutput';
+export * from './RetrieveRawTransactionByID';
+export * from './RetrieveTransactionByID';
+export * from './RetrieveTransactionReceipt';
+export * from './SendTransaction';
+export * from './TXID';
+export * from './Transfer';
+export * from './TxMeta';
diff --git a/packages/net/src/ws/MozillaWebSocketClient.ts b/packages/net/src/ws/MozillaWebSocketClient.ts
new file mode 100644
index 000000000..74e2929e1
--- /dev/null
+++ b/packages/net/src/ws/MozillaWebSocketClient.ts
@@ -0,0 +1,60 @@
+import { type WebSocketClient } from './WebSocketClient';
+import { type WebSocketListener } from './WebSocketListener';
+import { type HttpPath } from '../http';
+
+class MozillaWebSocketClient implements WebSocketClient {
+    readonly baseURL: string;
+
+    private ws?: WebSocket;
+
+    private readonly listeners: Array<WebSocketListener<unknown>> = [];
+
+    constructor(baseURL: string) {
+        this.baseURL = baseURL;
+    }
+
+    addListener(listener: WebSocketListener<unknown>): this {
+        this.listeners.push(listener);
+        return this;
+    }
+
+    close(): this {
+        this.ws?.close();
+        this.ws = undefined;
+        return this;
+    }
+
+    open(path: HttpPath): this {
+        this.close();
+        this.ws = new WebSocket(this.baseURL + path.path);
+        this.ws.onopen = (event: Event) => {
+            this.listeners.forEach((listener) => {
+                listener.onOpen?.(event);
+            });
+        };
+        this.ws.onerror = (event: Event) => {
+            this.listeners.forEach((listener) => {
+                listener.onError?.(event);
+            });
+        };
+        this.ws.onmessage = (event: MessageEvent<unknown>) => {
+            this.listeners.forEach((listener) => {
+                listener.onMessage?.(event);
+            });
+        };
+        this.ws.onclose = (event: CloseEvent) => {
+            this.listeners.forEach((listener) => {
+                listener.onClose?.(event);
+            });
+            this.ws = undefined;
+        };
+        return this;
+    }
+
+    removeListener(listener: WebSocketListener<unknown>): this {
+        this.listeners.splice(this.listeners.indexOf(listener), 1);
+        return this;
+    }
+}
+
+export { MozillaWebSocketClient };
diff --git a/packages/net/src/ws/WebSocketClient.ts b/packages/net/src/ws/WebSocketClient.ts
new file mode 100644
index 000000000..5df08ae7e
--- /dev/null
+++ b/packages/net/src/ws/WebSocketClient.ts
@@ -0,0 +1,16 @@
+import type { WebSocketListener } from './WebSocketListener';
+import { type HttpPath } from '../http';
+
+interface WebSocketClient {
+    get baseURL(): string;
+
+    addListener: (listener: WebSocketListener<unknown>) => WebSocketClient;
+
+    close: () => WebSocketClient;
+
+    open: (path: HttpPath) => WebSocketClient;
+
+    removeListener: (listener: WebSocketListener<unknown>) => WebSocketClient;
+}
+
+export { type WebSocketClient };
diff --git a/packages/net/src/ws/WebSocketListener.ts b/packages/net/src/ws/WebSocketListener.ts
new file mode 100644
index 000000000..3ca023fda
--- /dev/null
+++ b/packages/net/src/ws/WebSocketListener.ts
@@ -0,0 +1,8 @@
+interface WebSocketListener<EventType> {
+    onClose: (event: Event) => void;
+    onError: (event: Event) => void;
+    onMessage: (event: MessageEvent<EventType>) => void;
+    onOpen: (event: Event) => void;
+}
+
+export type { WebSocketListener };
diff --git a/packages/net/src/ws/index.ts b/packages/net/src/ws/index.ts
new file mode 100644
index 000000000..2f3415242
--- /dev/null
+++ b/packages/net/src/ws/index.ts
@@ -0,0 +1,3 @@
+export * from './MozillaWebSocketClient';
+export * from './WebSocketClient';
+export * from './WebSocketListener';
diff --git a/packages/net/tests/http/FetchHttpClient.testnet.test.ts b/packages/net/tests/http/FetchHttpClient.testnet.test.ts
new file mode 100644
index 000000000..b9c4d250b
--- /dev/null
+++ b/packages/net/tests/http/FetchHttpClient.testnet.test.ts
@@ -0,0 +1,42 @@
+import { describe, test } from '@jest/globals';
+import { FetchHttpClient, ThorNetworks } from '../../src';
+
+/**
+ * Test FetchHttpClient class.
+ *
+ * @group integration/network/http
+ */
+describe('FetchHttpClient testnet tests', () => {
+    test('ok <- get', async () => {
+        await new FetchHttpClient(
+            ThorNetworks.TESTNET,
+            (request: Request) => {
+                console.log(request);
+                return request;
+            },
+            (response: Response) => {
+                console.log(response);
+                return response;
+            }
+        ).get();
+    });
+
+    test('ok <- post', async () => {
+        const expected = {
+            hello: 'world'
+        };
+        const response = await new FetchHttpClient(
+            'https://httpbin.org',
+            (request: Request) => {
+                console.log(request);
+                return request;
+            },
+            (response: Response) => {
+                console.log(response);
+                return response;
+            }
+        ).post({ path: '/post' }, { query: '' }, expected);
+        const actual: unknown = await response.json();
+        console.log(JSON.stringify(actual, null, 2));
+    });
+});
diff --git a/packages/net/tests/thor/accounts/InspectClauses.testnet.test.ts b/packages/net/tests/thor/accounts/InspectClauses.testnet.test.ts
new file mode 100644
index 000000000..bb0593b2a
--- /dev/null
+++ b/packages/net/tests/thor/accounts/InspectClauses.testnet.test.ts
@@ -0,0 +1,41 @@
+import { describe, test } from '@jest/globals';
+import {
+    type ExecuteCodesRequestJSON,
+    FetchHttpClient,
+    InspectClauses,
+    ThorNetworks
+} from '../../../src';
+
+describe('InspectClauses testnet tests', () => {
+    test('ok <- askTo', async () => {
+        const request = {
+            gas: 50000,
+            gasPrice: '1000000000000000',
+            caller: '0x6d95e6dca01d109882fe1726a2fb9865fa41e7aa',
+            provedWork: '1000',
+            gasPayer: '0xd3ae78222beadb038203be21ed5ce7c9b1bff602',
+            expiration: 1000,
+            blockRef: '0x00000000851caf3c',
+            clauses: [
+                {
+                    to: '0x0000000000000000000000000000456E65726779',
+                    value: '0x0',
+                    data: '0xa9059cbb0000000000000000000000000f872421dc479f3c11edd89512731814d0598db50000000000000000000000000000000000000000000000013f306a2409fc0000'
+                },
+                {
+                    to: '0xf077b491b355E64048cE21E3A6Fc4751eEeA77fa',
+                    value: '0x6124fee993bc00000',
+                    data: '0x'
+                },
+                {
+                    value: '0x0',
+                    data: '0x6080604052348015600f57600080fd5b50609f8061001e6000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680631820cabb146044575b600080fd5b348015604f57600080fd5b506056606c565b6040518082815260200191505060405180910390f35b62015180815600a165627a7a723058200ac7475da248e2fc26c057319e296e90c24d5f8b9bf956fb3b77545642cad3b10029'
+                }
+            ]
+        } satisfies ExecuteCodesRequestJSON;
+        const r = await InspectClauses.of(request).askTo(
+            FetchHttpClient.at(ThorNetworks.TESTNET)
+        );
+        console.log(JSON.stringify(r, null, 2));
+    });
+});
diff --git a/packages/net/tests/thor/accounts/RetrieveAccountDetails.testnet.test.ts b/packages/net/tests/thor/accounts/RetrieveAccountDetails.testnet.test.ts
new file mode 100644
index 000000000..f5d663b4a
--- /dev/null
+++ b/packages/net/tests/thor/accounts/RetrieveAccountDetails.testnet.test.ts
@@ -0,0 +1,16 @@
+import { describe, test } from '@jest/globals';
+import {
+    FetchHttpClient,
+    RetrieveAccountDetails,
+    ThorNetworks
+} from '../../../src';
+import { Address } from '@vechain/sdk-core';
+
+describe('RetrieveAccountDetails testnet tests', () => {
+    test('ok <- askTo', async () => {
+        const r = await RetrieveAccountDetails.of(
+            Address.of('0x0000000000000000000000000000456E65726779')
+        ).askTo(FetchHttpClient.at(ThorNetworks.TESTNET));
+        console.log(JSON.stringify(r, null, 2));
+    });
+});
diff --git a/packages/net/tests/thor/accounts/RetrieveContractBytecode.testnet.test.ts b/packages/net/tests/thor/accounts/RetrieveContractBytecode.testnet.test.ts
new file mode 100644
index 000000000..dc3299953
--- /dev/null
+++ b/packages/net/tests/thor/accounts/RetrieveContractBytecode.testnet.test.ts
@@ -0,0 +1,16 @@
+import { describe, test } from '@jest/globals';
+import { Address } from '@vechain/sdk-core';
+import {
+    RetrieveContractBytecode,
+    FetchHttpClient,
+    ThorNetworks
+} from '../../../src';
+
+describe('RetrieveContractBytecode testnet tests', () => {
+    test('ok <- askTo', async () => {
+        const r = await RetrieveContractBytecode.of(
+            Address.of('0x0000000000000000000000000000456E65726779')
+        ).askTo(FetchHttpClient.at(ThorNetworks.TESTNET));
+        console.log(JSON.stringify(r, null, 2));
+    });
+});
diff --git a/packages/net/tests/thor/accounts/RetrieveStoragePositionValue.testnet.test.ts b/packages/net/tests/thor/accounts/RetrieveStoragePositionValue.testnet.test.ts
new file mode 100644
index 000000000..73f135efe
--- /dev/null
+++ b/packages/net/tests/thor/accounts/RetrieveStoragePositionValue.testnet.test.ts
@@ -0,0 +1,19 @@
+import { describe, test } from '@jest/globals';
+import { Address, BlockId } from '@vechain/sdk-core';
+import {
+    FetchHttpClient,
+    RetrieveStoragePositionValue,
+    ThorNetworks
+} from '../../../src';
+
+describe('RetrieveStoragePositionValue testnet tests', () => {
+    test('ok <- askTo', async () => {
+        const r = await RetrieveStoragePositionValue.of(
+            Address.of('0x93Ae8aab337E58A6978E166f8132F59652cA6C56'),
+            BlockId.of(
+                '0x0000000000000000000000000000000000000000000000000000000000000001'
+            )
+        ).askTo(FetchHttpClient.at(ThorNetworks.TESTNET));
+        console.log(JSON.stringify(r, null, 2));
+    });
+});
diff --git a/packages/net/tests/thor/blocks/RetrieveBlock.testnet.test.ts b/packages/net/tests/thor/blocks/RetrieveBlock.testnet.test.ts
new file mode 100644
index 000000000..320cccc7d
--- /dev/null
+++ b/packages/net/tests/thor/blocks/RetrieveBlock.testnet.test.ts
@@ -0,0 +1,12 @@
+import { describe, test } from '@jest/globals';
+import { FetchHttpClient, RetrieveBlock, ThorNetworks } from '../../../src';
+import { Revision } from '@vechain/sdk-core';
+
+describe('RetrieveBlock testnet tests', () => {
+    test('ok <- askTo', async () => {
+        const r = await RetrieveBlock.of(Revision.BEST).askTo(
+            FetchHttpClient.at(ThorNetworks.TESTNET)
+        );
+        console.log(JSON.stringify(r, null, 2));
+    });
+});
diff --git a/packages/net/tests/thor/debug/RetrieveStorageRange.mainnet.test.ts b/packages/net/tests/thor/debug/RetrieveStorageRange.mainnet.test.ts
new file mode 100644
index 000000000..daba15595
--- /dev/null
+++ b/packages/net/tests/thor/debug/RetrieveStorageRange.mainnet.test.ts
@@ -0,0 +1,22 @@
+import { describe, test } from '@jest/globals';
+import {
+    RetrieveStorageRange,
+    type StorageRangeOptionJSON
+} from '../../../src/thor/debug';
+import { FetchHttpClient, ThorNetworks } from '../../../src';
+
+describe('RetrieveStorageRange mainnet tests', () => {
+    test('ok <- askTo', async () => {
+        const request = {
+            address: '0xd8ccdd85abdbf68dfec95f06c973e87b1b5a9997',
+            keyStart:
+                '0x0000000000000000000000000000000000000000000000000000000000000000',
+            maxResult: 10,
+            target: '0x010709463c1f0c9aa66a31182fb36d1977d99bfb6526bae0564a0eac4006c31a/0/0'
+        } satisfies StorageRangeOptionJSON;
+        const r = await RetrieveStorageRange.of(request).askTo(
+            FetchHttpClient.at(ThorNetworks.MAINNET)
+        );
+        console.log(JSON.stringify(r, null, 2));
+    });
+});
diff --git a/packages/net/tests/thor/debug/TraceCall.testnet.test.ts b/packages/net/tests/thor/debug/TraceCall.testnet.test.ts
new file mode 100644
index 000000000..e06ad9722
--- /dev/null
+++ b/packages/net/tests/thor/debug/TraceCall.testnet.test.ts
@@ -0,0 +1,28 @@
+import { describe, test } from '@jest/globals';
+import {
+    type PostDebugTracerCallRequestJSON,
+    TraceCall
+} from '../../../src/thor/debug';
+import { FetchHttpClient, ThorNetworks } from '../../../src';
+
+describe('TraceCall testnet tests', () => {
+    test('ok <- askTo', async () => {
+        const request = {
+            value: '0x0',
+            to: '0x0000000000000000000000000000456E65726779',
+            data: '0xa9059cbb0000000000000000000000000f872421dc479f3c11edd89512731814d0598db50000000000',
+            gas: 50000,
+            gasPrice: '1000000000000000',
+            caller: '0x7567d83b7b8d80addcb281a71d54fc7b3364ffed',
+            provedWork: '1000',
+            gasPayer: '0xd3ae78222beadb038203be21ed5ce7c9b1bff602',
+            expiration: 1000,
+            blockRef: '0x00000000851caf3c',
+            name: 'call'
+        } satisfies PostDebugTracerCallRequestJSON;
+        const r = await TraceCall.of(request).askTo(
+            FetchHttpClient.at(ThorNetworks.TESTNET)
+        );
+        console.log(JSON.stringify(r, null, 2));
+    });
+});
diff --git a/packages/net/tests/thor/debug/TraceTransactionClause.mainnet.test.ts b/packages/net/tests/thor/debug/TraceTransactionClause.mainnet.test.ts
new file mode 100644
index 000000000..85daa5aa6
--- /dev/null
+++ b/packages/net/tests/thor/debug/TraceTransactionClause.mainnet.test.ts
@@ -0,0 +1,20 @@
+import { describe, test } from '@jest/globals';
+import {
+    type PostDebugTracerRequestJSON,
+    TraceTransactionClause
+} from '../../../src/thor/debug';
+import { FetchHttpClient, ThorNetworks } from '../../../src';
+
+describe('TraceTransactionClause mainnet tests', () => {
+    test('ok <- askTo', async () => {
+        const request = {
+            target: '0x010709463c1f0c9aa66a31182fb36d1977d99bfb6526bae0564a0eac4006c31a/0/0',
+            name: 'call',
+            config: {}
+        } satisfies PostDebugTracerRequestJSON;
+        const r = await TraceTransactionClause.of(request).askTo(
+            FetchHttpClient.at(ThorNetworks.MAINNET)
+        );
+        console.log(JSON.stringify(r, null, 2));
+    });
+});
diff --git a/packages/net/tests/thor/logs/QuerySmartContractEvents.testnet.test.ts b/packages/net/tests/thor/logs/QuerySmartContractEvents.testnet.test.ts
new file mode 100644
index 000000000..00bfa8cf9
--- /dev/null
+++ b/packages/net/tests/thor/logs/QuerySmartContractEvents.testnet.test.ts
@@ -0,0 +1,35 @@
+import { describe, test } from '@jest/globals';
+
+import {
+    type EventLogFilterRequestJSON,
+    QuerySmartContractEvents
+} from '../../../src/thor/logs';
+import { FetchHttpClient, ThorNetworks } from '../../../src';
+
+describe('QuerySmartContractEvents testnet tests', () => {
+    test('ok <- askTo', async () => {
+        const request = {
+            range: {
+                unit: 'block',
+                from: 17240365,
+                to: 17289864
+            },
+            options: {
+                offset: 0,
+                limit: 100
+            },
+            criteriaSet: [
+                {
+                    address: '0x0000000000000000000000000000456E65726779',
+                    topic0: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
+                    topic1: '0x0000000000000000000000006d95e6dca01d109882fe1726a2fb9865fa41e7aa'
+                }
+            ],
+            order: 'asc'
+        } satisfies EventLogFilterRequestJSON;
+        const r = await QuerySmartContractEvents.of(request).askTo(
+            FetchHttpClient.at(ThorNetworks.TESTNET)
+        );
+        console.log(JSON.stringify(r, null, 2));
+    });
+});
diff --git a/packages/net/tests/thor/logs/QueryVETTransferEvents.testnet.test.ts b/packages/net/tests/thor/logs/QueryVETTransferEvents.testnet.test.ts
new file mode 100644
index 000000000..2d82713df
--- /dev/null
+++ b/packages/net/tests/thor/logs/QueryVETTransferEvents.testnet.test.ts
@@ -0,0 +1,32 @@
+import { describe, test } from '@jest/globals';
+import { type TransferLogFilterRequestJSON } from '../../../src/thor/logs/TransferLogFilterRequest';
+import { QueryVETTransferEvents } from '../../../src/thor/logs/QueryVETTransferEvents';
+import { FetchHttpClient, ThorNetworks } from '../../../src';
+
+describe('QueryVETTransferEvents testnet tests', () => {
+    test('ok <- askTo', async () => {
+        const request = {
+            range: {
+                unit: 'block',
+                from: 17240365,
+                to: 17289864
+            },
+            options: {
+                offset: 0,
+                limit: 100
+            },
+            criteriaSet: [
+                {
+                    txOrigin: '0x6d95e6dca01d109882fe1726a2fb9865fa41e7aa',
+                    sender: '0x6d95e6dca01d109882fe1726a2fb9865fa41e7aa',
+                    recipient: '0x45429a2255e7248e57fce99e7239aed3f84b7a53'
+                }
+            ],
+            order: 'asc'
+        } satisfies TransferLogFilterRequestJSON;
+        const r = await QueryVETTransferEvents.of(request).askTo(
+            FetchHttpClient.at(ThorNetworks.TESTNET)
+        );
+        console.log(JSON.stringify(r, null, 2));
+    });
+});
diff --git a/packages/net/tests/thor/node/RetrieveConnectedPeers.testnet.test.ts b/packages/net/tests/thor/node/RetrieveConnectedPeers.testnet.test.ts
new file mode 100644
index 000000000..96a771424
--- /dev/null
+++ b/packages/net/tests/thor/node/RetrieveConnectedPeers.testnet.test.ts
@@ -0,0 +1,15 @@
+import { describe, test } from '@jest/globals';
+import {
+    FetchHttpClient,
+    RetrieveConnectedPeers,
+    ThorNetworks
+} from '../../../src';
+
+describe('RetrieveConnectedPeers testnet tests', () => {
+    test('ok <- askTo', async () => {
+        const r = await new RetrieveConnectedPeers().askTo(
+            FetchHttpClient.at(ThorNetworks.TESTNET)
+        );
+        console.log(JSON.stringify(r, null, 2));
+    });
+});
diff --git a/packages/net/tests/thor/subscriptions/BeatsSubscription.solo.test.ts b/packages/net/tests/thor/subscriptions/BeatsSubscription.solo.test.ts
new file mode 100644
index 000000000..28572b9ad
--- /dev/null
+++ b/packages/net/tests/thor/subscriptions/BeatsSubscription.solo.test.ts
@@ -0,0 +1,43 @@
+import { afterEach, beforeEach, describe } from '@jest/globals';
+import {
+    MozillaWebSocketClient,
+    type WebSocketListener
+} from '../../../src/ws';
+import {
+    BeatsSubscription,
+    type SubscriptionBeat2Response
+} from '../../../src/thor/subscriptions';
+
+describe('BlocksSubscription solo tests', () => {
+    let subscription: BeatsSubscription;
+    beforeEach(() => {
+        subscription = BeatsSubscription.at(
+            new MozillaWebSocketClient('ws://localhost:8669')
+        );
+    });
+
+    test('data <- open', (done) => {
+        subscription
+            .addListener({
+                onMessage: (message) => {
+                    const data = message.data;
+                    console.log(JSON.stringify(data, null, 2));
+                    done();
+                },
+                onOpen: () => {
+                    console.log('WebSocket connection opened');
+                },
+                onClose: () => {
+                    console.log(`WebSocket connection closed`);
+                },
+                onError: (error) => {
+                    console.error('WebSocket encountered an error:', error);
+                }
+            } satisfies WebSocketListener<SubscriptionBeat2Response>)
+            .open();
+    }, 30000);
+
+    afterEach(() => {
+        subscription.close();
+    });
+});
diff --git a/packages/net/tests/thor/subscriptions/BlocksSubscription.solo.test.ts b/packages/net/tests/thor/subscriptions/BlocksSubscription.solo.test.ts
new file mode 100644
index 000000000..da30ba968
--- /dev/null
+++ b/packages/net/tests/thor/subscriptions/BlocksSubscription.solo.test.ts
@@ -0,0 +1,39 @@
+import { afterEach, beforeEach, describe } from '@jest/globals';
+import {
+    MozillaWebSocketClient,
+    type WebSocketListener
+} from '../../../src/ws';
+import {
+    BlocksSubscription,
+    type SubscriptionBlockResponse
+} from '../../../src/thor/subscriptions';
+
+describe('BlocksSubscription solo tests', () => {
+    let subscription: BlocksSubscription;
+    beforeEach(() => {
+        subscription = BlocksSubscription.at(
+            new MozillaWebSocketClient('ws://localhost:8669')
+        );
+    });
+
+    test('data <- open', (done) => {
+        subscription
+            .addListener({
+                onMessage: (message) => {
+                    const data = message.data;
+                    console.log(JSON.stringify(data, null, 2));
+                    done();
+                },
+                onOpen: () => {},
+                onClose: () => {},
+                onError: (error) => {
+                    console.error('WebSocket error:', error);
+                }
+            } satisfies WebSocketListener<SubscriptionBlockResponse>)
+            .open();
+    }, 30000);
+
+    afterEach(() => {
+        subscription.close();
+    });
+});
diff --git a/packages/net/tests/thor/subscriptions/NewTransactionSubscription.solo.test.ts b/packages/net/tests/thor/subscriptions/NewTransactionSubscription.solo.test.ts
new file mode 100644
index 000000000..d70ef1978
--- /dev/null
+++ b/packages/net/tests/thor/subscriptions/NewTransactionSubscription.solo.test.ts
@@ -0,0 +1,35 @@
+import { afterEach, beforeEach, describe } from '@jest/globals';
+import {
+    MozillaWebSocketClient,
+    type WebSocketListener
+} from '../../../src/ws';
+import { NewTransactionSubscription } from '../../../src/thor/subscriptions';
+import { type TXID } from '../../../src';
+
+describe('NewTransactionSubscription solo tests', () => {
+    let subscription: NewTransactionSubscription;
+    beforeEach(() => {
+        subscription = NewTransactionSubscription.at(
+            new MozillaWebSocketClient('ws://localhost:8669')
+        );
+    });
+
+    test('data <- open', (done) => {
+        subscription
+            .addListener({
+                onMessage: (message) => {
+                    const data = message.data;
+                    console.log(JSON.stringify(data, null, 2));
+                    done();
+                },
+                onClose: () => {},
+                onError: () => {},
+                onOpen: () => {}
+            } satisfies WebSocketListener<TXID>)
+            .open();
+    }, 30000);
+
+    afterEach(() => {
+        subscription.close();
+    });
+});
diff --git a/packages/net/tests/thor/transactions/EXP.solo.test.ts b/packages/net/tests/thor/transactions/EXP.solo.test.ts
new file mode 100644
index 000000000..5e53a30d0
--- /dev/null
+++ b/packages/net/tests/thor/transactions/EXP.solo.test.ts
@@ -0,0 +1,205 @@
+import { describe, test } from '@jest/globals';
+import {
+    Address,
+    Clause,
+    HexUInt,
+    networkInfo,
+    Transaction,
+    type TransactionBody,
+    VET
+} from '../../../../core/src';
+import { THOR_SOLO_URL, ThorClient } from '../../../../network/src';
+
+import { secp256k1 as nc_secp256k1 } from '@noble/curves/secp256k1';
+import { Secp256k1 } from '@vechain/sdk-core';
+
+describe('Solo Experiments', () => {
+    const thorClient = ThorClient.at(THOR_SOLO_URL + '/', {
+        isPollingEnabled: false
+    });
+    const sender = {
+        privateKey: HexUInt.of(
+            'ea5383ac1f9e625220039a4afac6a7f868bf1ad4f48ce3a1dd78bd214ee4ace5'
+        ),
+        address: Address.of('0x2669514f9fe96bc7301177ba774d3da8a06cace4')
+    };
+    const receiver = {
+        address: Address.of('0x9e7911de289c3c856ce7f421034f66b6cde49c39')
+    };
+    const gasPayer = {
+        privateKey: HexUInt.of(
+            '432f38bcf338c374523e83fdb2ebe1030aba63c7f1e81f7d76c5f53f4d42e766'
+        ),
+        address: Address.of('0x88b2551c3ed42ca663796c10ce68c88a65f73fe2')
+    };
+    const OneVET = VET.of(1);
+    const clauses = [Clause.transferVET(receiver.address, OneVET)];
+
+    test('Delegated Tx', async () => {
+        const latestBlock = await thorClient.blocks.getBestBlockCompressed();
+        console.log(latestBlock);
+        const gasToPay = await thorClient.gas.estimateGas(
+            clauses,
+            sender.address.toString()
+        );
+        console.log(gasToPay);
+        const body: TransactionBody = {
+            chainTag: networkInfo.solo.chainTag,
+            blockRef: latestBlock?.id.slice(0, 18) ?? '0x0',
+            expiration: 0,
+            clauses,
+            gasPriceCoef: 0,
+            gas: gasToPay.totalGas,
+            dependsOn: null,
+            nonce: 2,
+            reserved: {
+                features: 1 // set the transaction to be delegated
+            }
+        };
+        const tx = Transaction.of(body).signAsSenderAndGasPayer(
+            sender.privateKey.bytes,
+            gasPayer.privateKey.bytes
+        );
+        console.log(tx.signature?.length);
+        const txResult = await thorClient.transactions.sendRawTransaction(
+            HexUInt.of(tx.encoded).toString()
+        );
+        console.log(txResult);
+        const txReceipt = await thorClient.transactions.waitForTransaction(
+            tx.id.toString()
+        );
+        console.log(txReceipt);
+        const txr = await thorClient.transactions.getTransaction(
+            tx.id.toString()
+        );
+        console.log(txr);
+    }, 60000);
+
+    test('NCC Tx', async () => {
+        const latestBlock = await thorClient.blocks.getBestBlockCompressed();
+        const gasToPay = await thorClient.gas.estimateGas(
+            clauses,
+            sender.address.toString()
+        );
+        const body: TransactionBody = {
+            chainTag: networkInfo.solo.chainTag,
+            blockRef: latestBlock?.id.slice(0, 18) ?? '0x0',
+            expiration: 0,
+            clauses,
+            gasPriceCoef: 0,
+            gas: gasToPay.totalGas,
+            dependsOn: null,
+            nonce: 1,
+            reserved: {
+                features: 1 // set the transaction to be delegated
+            }
+        };
+        const tx = Transaction.of(body).signAsSenderAndGasPayer(
+            sender.privateKey.bytes,
+            gasPayer.privateKey.bytes
+        );
+        // KEEP IT
+        // console.log(tx.signature?.length);
+        // const txResult = await thorClient.transactions.sendRawTransaction(
+        //     HexUInt.of(tx.encoded).toString()
+        // );
+        // console.log(txResult);
+        const aBody: TransactionBody = {
+            chainTag: networkInfo.solo.chainTag,
+            blockRef: latestBlock?.id.slice(0, 18) ?? '0x0',
+            expiration: 0,
+            clauses,
+            gasPriceCoef: 0,
+            gas: gasToPay.totalGas,
+            dependsOn: null,
+            nonce: 2,
+            reserved: {
+                features: 1 // set the transaction to be delegated
+            }
+        };
+        const aTx = Transaction.of(aBody).signAsSender(sender.privateKey.bytes);
+        // KEEP IT
+        // const sig = nc_utils.concatBytes(
+        //     aTx.signature as Uint8Array,
+        //     (tx.signature as Uint8Array).slice(65)
+        // );
+        const fTx = Transaction.of(aTx.body, tx.signature);
+        const fTxResult = await thorClient.transactions.sendRawTransaction(
+            HexUInt.of(fTx.encoded).toString()
+        );
+        console.log(fTxResult);
+    }, 60000);
+
+    test('verify', async () => {
+        const latestBlock = await thorClient.blocks.getBestBlockCompressed();
+        const gasToPay = await thorClient.gas.estimateGas(
+            clauses,
+            sender.address.toString()
+        );
+        // KEEP IT
+        // const senderPublicKey = Secp256k1.derivePublicKey(
+        //     sender.privateKey.bytes,
+        //     false
+        // );
+        const gasPayerPublicKey = Secp256k1.derivePublicKey(
+            gasPayer.privateKey.bytes,
+            false
+        );
+        const txA = Transaction.of({
+            chainTag: networkInfo.solo.chainTag,
+            blockRef: latestBlock?.id.slice(0, 18) ?? '0x0',
+            expiration: 0,
+            clauses,
+            gasPriceCoef: 0,
+            gas: gasToPay.totalGas,
+            dependsOn: null,
+            nonce: 1,
+            reserved: {
+                features: 1 // set the transaction to be delegated
+            }
+        });
+        const as = txA.signAsSender(sender.privateKey.bytes);
+        const ap = as.signAsGasPayer(sender.address, gasPayer.privateKey.bytes);
+        const sigmaA = nc_secp256k1.Signature.fromCompact(
+            ap.signature?.slice(-65).slice(0, 64) as Uint8Array
+        );
+        const hashA = ap.getTransactionHash(sender.address).bytes;
+        const isVerifiedA = nc_secp256k1.verify(
+            sigmaA,
+            hashA,
+            gasPayerPublicKey
+        );
+        console.log(isVerifiedA);
+        const txB = Transaction.of({
+            chainTag: networkInfo.solo.chainTag,
+            blockRef: latestBlock?.id.slice(0, 18) ?? '0x0',
+            expiration: 0,
+            clauses,
+            gasPriceCoef: 0,
+            gas: gasToPay.totalGas,
+            dependsOn: null,
+            nonce: 2,
+            reserved: {
+                features: 1 // set the transaction to be delegated
+            }
+        });
+        const bs = txB.signAsSender(sender.privateKey.bytes);
+        const bp = bs.signAsGasPayer(sender.address, gasPayer.privateKey.bytes);
+        const sigmaB = nc_secp256k1.Signature.fromCompact(
+            bp.signature?.slice(-65).slice(0, 64) as Uint8Array
+        );
+        const hashB = bp.getTransactionHash(sender.address).bytes;
+        const isVerifiedB = nc_secp256k1.verify(
+            sigmaB,
+            hashB,
+            gasPayerPublicKey
+        );
+        console.log(isVerifiedB);
+        const isVerifiedForge = nc_secp256k1.verify(
+            sigmaA,
+            hashB,
+            gasPayerPublicKey
+        );
+        console.log(isVerifiedForge);
+    });
+});
diff --git a/packages/net/tests/thor/transactions/EXP.testnet.test.ts b/packages/net/tests/thor/transactions/EXP.testnet.test.ts
new file mode 100644
index 000000000..c24ab6755
--- /dev/null
+++ b/packages/net/tests/thor/transactions/EXP.testnet.test.ts
@@ -0,0 +1,71 @@
+import { describe, test } from '@jest/globals';
+import { TESTNET_URL, ThorClient } from '../../../../network/src';
+import {
+    Address,
+    Clause,
+    HexUInt,
+    networkInfo,
+    Transaction,
+    type TransactionBody,
+    VET
+} from '@vechain/sdk-core';
+
+describe('Testnet Experiments', () => {
+    const thorClient = ThorClient.at(TESTNET_URL + '/', {
+        isPollingEnabled: false
+    });
+    const sender = {
+        privateKey: HexUInt.of(
+            'f9fc826b63a35413541d92d2bfb6661128cd5075fcdca583446d20c59994ba26'
+        ),
+        address: Address.of('0x7a28e7361fd10f4f058f9fefc77544349ecff5d6')
+    };
+    const receiver = {
+        address: Address.of('0xb717b660cd51109334bd10b2c168986055f58c1a')
+    };
+    const gasPayer = {
+        privateKey: HexUInt.of(
+            '521b7793c6eb27d137b617627c6b85d57c0aa303380e9ca4e30a30302fbc6676'
+        ),
+        address: Address.of('0x062F167A905C1484DE7e75B88EDC7439f82117DE')
+    };
+    const OneVET = VET.of(1);
+    const clauses = [Clause.transferVET(receiver.address, OneVET)];
+
+    test('Delegated Tx', async () => {
+        const latestBlock = await thorClient.blocks.getBestBlockCompressed();
+        console.log(latestBlock);
+        const gasToPay = await thorClient.transactions.estimateGas(
+            clauses,
+            gasPayer.address.toString()
+        );
+        console.log(gasToPay);
+        const body: TransactionBody = {
+            chainTag: networkInfo.testnet.chainTag,
+            blockRef: latestBlock?.id.slice(0, 18) ?? '0x0',
+            expiration: 32,
+            clauses,
+            gasPriceCoef: 128,
+            gas: gasToPay.totalGas,
+            dependsOn: null,
+            // eslint-disable-next-line sonarjs/pseudo-random
+            nonce: Math.floor(1000000 * Math.random()),
+            reserved: {
+                features: 1 // set the transaction to be delegated
+            }
+        };
+        const tx = Transaction.of(body).signAsSenderAndGasPayer(
+            sender.privateKey.bytes,
+            gasPayer.privateKey.bytes
+        );
+        console.log('tx', tx);
+        const txResult = await thorClient.transactions.sendRawTransaction(
+            HexUInt.of(tx.encoded).toString()
+        );
+        console.log(txResult);
+        const txReceipt = await thorClient.transactions.waitForTransaction(
+            tx.id.toString()
+        );
+        console.log(txReceipt);
+    }, 60000);
+});
diff --git a/packages/net/tests/thor/transactions/RetrieveRawTransactionByIID.testnet.test.ts b/packages/net/tests/thor/transactions/RetrieveRawTransactionByIID.testnet.test.ts
new file mode 100644
index 000000000..7d60e24f2
--- /dev/null
+++ b/packages/net/tests/thor/transactions/RetrieveRawTransactionByIID.testnet.test.ts
@@ -0,0 +1,18 @@
+import { describe, test } from '@jest/globals';
+import { TxId } from '../../../../core';
+import {
+    FetchHttpClient,
+    RetrieveRawTransactionByID,
+    ThorNetworks
+} from '../../../src';
+
+describe('RetrieveRawTransactionByID testnet tests', () => {
+    test('ok <- askTo', async () => {
+        const txId = TxId.of(
+            '0xb6b5b47a5eee8b14e5222ac1bb957c0bbdc3d489850b033e3e544d9ca0cef934'
+        );
+        const httpClient = FetchHttpClient.at(ThorNetworks.MAINNET);
+        const r = await RetrieveRawTransactionByID.of(txId).askTo(httpClient);
+        console.log(JSON.stringify(r, null, 2));
+    });
+});
diff --git a/packages/net/tests/thor/transactions/RetrieveTransactionByIID.testnet.test.ts b/packages/net/tests/thor/transactions/RetrieveTransactionByIID.testnet.test.ts
new file mode 100644
index 000000000..32f48194e
--- /dev/null
+++ b/packages/net/tests/thor/transactions/RetrieveTransactionByIID.testnet.test.ts
@@ -0,0 +1,18 @@
+import { describe, test } from '@jest/globals';
+import { TxId } from '../../../../core';
+import {
+    FetchHttpClient,
+    RetrieveTransactionByID,
+    ThorNetworks
+} from '../../../src';
+
+describe('RetrieveTransactionByID testnet tests', () => {
+    test('ok <- askTo', async () => {
+        const txId = TxId.of(
+            '0xb6b5b47a5eee8b14e5222ac1bb957c0bbdc3d489850b033e3e544d9ca0cef934'
+        );
+        const httpClient = FetchHttpClient.at(ThorNetworks.MAINNET);
+        const r = await RetrieveTransactionByID.of(txId).askTo(httpClient);
+        console.log(JSON.stringify(r, null, 2));
+    });
+});
diff --git a/packages/net/tests/thor/transactions/RetrieveTransactionReceipt.testnet.test.ts b/packages/net/tests/thor/transactions/RetrieveTransactionReceipt.testnet.test.ts
new file mode 100644
index 000000000..ae543125c
--- /dev/null
+++ b/packages/net/tests/thor/transactions/RetrieveTransactionReceipt.testnet.test.ts
@@ -0,0 +1,18 @@
+import { describe, test } from '@jest/globals';
+import { TxId } from '../../../../core';
+import {
+    FetchHttpClient,
+    RetrieveTransactionReceipt,
+    ThorNetworks
+} from '../../../src';
+
+describe('RetrieveTransactionReceipt testnet tests', () => {
+    test('ok <- askTo', async () => {
+        const txId = TxId.of(
+            '0xb6b5b47a5eee8b14e5222ac1bb957c0bbdc3d489850b033e3e544d9ca0cef934'
+        );
+        const httpClient = FetchHttpClient.at(ThorNetworks.MAINNET);
+        const r = await RetrieveTransactionReceipt.of(txId).askTo(httpClient);
+        console.log(JSON.stringify(r, null, 2));
+    });
+});
diff --git a/packages/net/tests/thor/transactions/SendTransaction.solo.test.ts b/packages/net/tests/thor/transactions/SendTransaction.solo.test.ts
new file mode 100644
index 000000000..da12b7efa
--- /dev/null
+++ b/packages/net/tests/thor/transactions/SendTransaction.solo.test.ts
@@ -0,0 +1,34 @@
+import { describe, test } from '@jest/globals';
+
+import { THOR_SOLO_URL, ThorClient } from '../../../../network/';
+import {
+    transfer1VTHOClause,
+    transferTransactionBody
+} from '../../../../network/tests/thor-client/transactions/fixture';
+import { TEST_ACCOUNTS } from '../../../../network/tests/fixture';
+import { HexUInt, Transaction } from '@vechain/sdk-core';
+import { FetchHttpClient, SendTransaction } from '../../../src';
+
+describe('SendTransaction solo tests', () => {
+    test('ok <- askTo', async () => {
+        const thorSoloClient = ThorClient.at(THOR_SOLO_URL);
+        const gasResult = await thorSoloClient.gas.estimateGas(
+            [transfer1VTHOClause],
+            TEST_ACCOUNTS.TRANSACTION.TRANSACTION_SENDER.address
+        );
+        console.log(gasResult);
+        const tx = Transaction.of({
+            ...transferTransactionBody,
+            gas: gasResult.totalGas,
+            nonce: 10000000
+        }).sign(
+            HexUInt.of(TEST_ACCOUNTS.TRANSACTION.TRANSACTION_SENDER.privateKey)
+                .bytes
+        ).encoded;
+        console.log(tx);
+        const r = await SendTransaction.of(tx).askTo(
+            FetchHttpClient.at(THOR_SOLO_URL)
+        );
+        console.log(r);
+    });
+});
diff --git a/packages/net/tests/ws/MozillaWebSocketClient.solo.test.ts b/packages/net/tests/ws/MozillaWebSocketClient.solo.test.ts
new file mode 100644
index 000000000..820ba56b3
--- /dev/null
+++ b/packages/net/tests/ws/MozillaWebSocketClient.solo.test.ts
@@ -0,0 +1,25 @@
+import { afterEach, beforeEach, describe } from '@jest/globals';
+import { MozillaWebSocketClient } from '../../src/ws/MozillaWebSocketClient';
+import { type WebSocketListener } from '../../src/ws';
+
+describe('MozillaWebSocketClient solo tests', () => {
+    let wsc: MozillaWebSocketClient;
+    beforeEach(() => {
+        wsc = new MozillaWebSocketClient('ws://localhost:8669');
+    });
+
+    test('data <- open', (done) => {
+        wsc.addListener({
+            onMessage: (message) => {
+                console.log(message.data);
+                done();
+            }
+        } satisfies WebSocketListener<unknown>).open({
+            path: '/subscriptions/beat2'
+        });
+    }, 30000);
+
+    afterEach(() => {
+        wsc.close();
+    });
+});
diff --git a/packages/net/tsconfig.json b/packages/net/tsconfig.json
new file mode 100644
index 000000000..b523f6a50
--- /dev/null
+++ b/packages/net/tsconfig.json
@@ -0,0 +1,12 @@
+{
+  "extends": "../../tsconfig.json",
+  "compilerOptions": {
+      "outDir": "./dist",
+      "allowSyntheticDefaultImports": true,
+      "esModuleInterop": true,
+  },
+  "include": [
+      "./src/**/*.ts",
+      "./tests/**/*.ts"
+  ]
+}
\ No newline at end of file
diff --git a/packages/net/typedoc.json b/packages/net/typedoc.json
new file mode 100644
index 000000000..664b688e4
--- /dev/null
+++ b/packages/net/typedoc.json
@@ -0,0 +1,4 @@
+{
+    "extends": ["../../typedoc.base.json"],
+    "entryPoints": ["src/network.ts"]
+}
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index f4b711ca9..f53d2d011 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3427,6 +3427,11 @@
   dependencies:
     eslint-scope "5.1.1"
 
+"@noble/ciphers@^1.1.1":
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-1.2.0.tgz#a7858e18eb620f6b2a327a7f0e647b6a78fd0727"
+  integrity sha512-YGdEUzYEd+82jeaVbSKKVp1jFZb8LwaNMIIzHFkihGvYdd/KKAr7KaJHdEdSYGredE3ssSravXIa0Jxg28Sv5w==
+
 "@noble/ciphers@^1.1.3":
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-1.1.3.tgz#eb27085aa7ce94d8c6eaeb64299bab0589920ec1"
@@ -3446,7 +3451,7 @@
   dependencies:
     "@noble/hashes" "1.4.0"
 
-"@noble/curves@1.7.0", "@noble/curves@^1.4.0", "@noble/curves@^1.6.0", "@noble/curves@^1.7.0", "@noble/curves@~1.7.0":
+"@noble/curves@1.7.0", "@noble/curves@^1.6.0", "@noble/curves@^1.7.0", "@noble/curves@~1.7.0":
   version "1.7.0"
   resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.7.0.tgz#0512360622439256df892f21d25b388f52505e45"
   integrity sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw==
@@ -3850,7 +3855,7 @@
   resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz#427d5549943a9c6fce808e39ea64dbe60d4047f1"
   integrity sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==
 
-"@scure/base@^1.2.1", "@scure/base@~1.2.1":
+"@scure/base@^1.1.9", "@scure/base@^1.2.1", "@scure/base@~1.2.1":
   version "1.2.1"
   resolved "https://registry.npmjs.org/@scure/base/-/base-1.2.1.tgz#dd0b2a533063ca612c17aa9ad26424a2ff5aa865"
   integrity sha512-DGmGtC8Tt63J5GfHgfl5CuAXh96VF/LD8K9Hr/Gv0J2lAoRGlPOMpqMpMbCTOoOJMZCk2Xt+DskdDyn6dEFdzQ==
@@ -5363,6 +5368,37 @@
     uuid "2.0.1"
     xmlhttprequest "1.8.0"
 
+"@vechain/sdk-core@1.0.0-rc.6":
+  version "1.0.0-rc.6"
+  resolved "https://registry.yarnpkg.com/@vechain/sdk-core/-/sdk-core-1.0.0-rc.6.tgz#1c0b3c3297434f63d85bde65d344f8b34ae2a3ea"
+  integrity sha512-71ztrebtgi8u6uDu+HShnZt7OGqXa8tIyzv91GKjwXKDOSJKR6WVXfhWiMSCxEL4EBDAMsiRw5/zM+X4/8wxgg==
+  dependencies:
+    "@ethereumjs/rlp" "^5.0.2"
+    "@noble/ciphers" "^1.1.1"
+    "@noble/curves" "^1.6.0"
+    "@noble/hashes" "^1.5.0"
+    "@scure/base" "^1.1.9"
+    "@scure/bip32" "^1.4.0"
+    "@scure/bip39" "^1.4.0"
+    "@vechain/sdk-errors" "1.0.0-rc.6"
+    "@vechain/sdk-logging" "1.0.0-rc.6"
+    abitype "^1.0.6"
+    ethers "6.13.4"
+    fast-json-stable-stringify "^2.1.0"
+    viem "^2.21.45"
+
+"@vechain/sdk-errors@1.0.0-rc.6":
+  version "1.0.0-rc.6"
+  resolved "https://registry.yarnpkg.com/@vechain/sdk-errors/-/sdk-errors-1.0.0-rc.6.tgz#f0f0cb172ef0556a19c43aedd6cf9a4fa821cfd0"
+  integrity sha512-DzIbaXOis8/yEfRc1KED8Kyo89K3jTJ+2T+gKvAYDyeut0cR4pB8l64ufYagqFDBkU0hJkOQsoz0bF8CRnZX8w==
+
+"@vechain/sdk-logging@1.0.0-rc.6":
+  version "1.0.0-rc.6"
+  resolved "https://registry.yarnpkg.com/@vechain/sdk-logging/-/sdk-logging-1.0.0-rc.6.tgz#f6ca4d37c4eb7aab848c1c973b00d6e6ba103668"
+  integrity sha512-RaPvzZM9ez2jsEowW6/3UXU+fRYiu7KPhiDf3aD19JWpMN92jU9T1NpX4aNsphH+XjhVCj7Ucz4MVwK+TwhGiw==
+  dependencies:
+    "@vechain/sdk-errors" "1.0.0-rc.6"
+
 "@vechain/vebetterdao-contracts@^4.1.0":
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/@vechain/vebetterdao-contracts/-/vebetterdao-contracts-4.1.0.tgz#5a081bf9c548ea777fe16dcab40536d6d2cc1a62"
@@ -8377,6 +8413,19 @@ ethereumjs-util@^7.1.4:
     ethereum-cryptography "^0.1.3"
     rlp "^2.2.4"
 
+ethers@6.13.4:
+  version "6.13.4"
+  resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.13.4.tgz#bd3e1c3dc1e7dc8ce10f9ffb4ee40967a651b53c"
+  integrity sha512-21YtnZVg4/zKkCQPjrDj38B1r4nQvTZLopUGMLQ1ePU2zV/joCfDC3t3iKQjWRzjjjbzR+mdAIoikeBRNkdllA==
+  dependencies:
+    "@adraffy/ens-normalize" "1.10.1"
+    "@noble/curves" "1.2.0"
+    "@noble/hashes" "1.3.2"
+    "@types/node" "22.7.5"
+    aes-js "4.0.0-beta.5"
+    tslib "2.7.0"
+    ws "8.17.1"
+
 ethers@6.13.5, ethers@^6.9.0:
   version "6.13.5"
   resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.13.5.tgz#8c1d6ac988ac08abc3c1d8fabbd4b8b602851ac4"
@@ -11559,10 +11608,10 @@ outvariant@^1.4.0, outvariant@^1.4.3:
   resolved "https://registry.yarnpkg.com/outvariant/-/outvariant-1.4.3.tgz#221c1bfc093e8fec7075497e7799fdbf43d14873"
   integrity sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==
 
-ox@0.6.0:
-  version "0.6.0"
-  resolved "https://registry.npmjs.org/ox/-/ox-0.6.0.tgz#ba8f89d68b5e8afe717c8a947ffacc0669ea155d"
-  integrity sha512-blUzTLidvUlshv0O02CnLFqBLidNzPoAZdIth894avUAotTuWziznv6IENv5idRuOSSP3dH8WzcYw84zVdu0Aw==
+ox@0.6.5:
+  version "0.6.5"
+  resolved "https://registry.yarnpkg.com/ox/-/ox-0.6.5.tgz#e6506a589bd6af9b5fecfcb2c641b63c9882edb6"
+  integrity sha512-vmnH8KvMDwFZDbNY1mq2CBRBWIgSliZB/dFV0xKp+DfF/dJkTENt6nmA+DzHSSAgL/GO2ydjkXWvlndJgSY4KQ==
   dependencies:
     "@adraffy/ens-normalize" "^1.10.1"
     "@noble/curves" "^1.6.0"
@@ -14391,10 +14440,10 @@ vfile@^6.0.0:
     "@types/unist" "^3.0.0"
     vfile-message "^4.0.0"
 
-viem@^2.22.8:
-  version "2.22.8"
-  resolved "https://registry.npmjs.org/viem/-/viem-2.22.8.tgz#fbc51215133066b89730e9a2aa2c7c0cb975a8e8"
-  integrity sha512-iB3PW/a/qzpYbpjo3R662u6a/zo6piZHez/N/bOC25C79FYXBCs8mQDqwiHk3FYErUhS4KVZLabKV9zGMd+EgQ==
+viem@^2.21.45, viem@^2.22.8:
+  version "2.22.9"
+  resolved "https://registry.yarnpkg.com/viem/-/viem-2.22.9.tgz#14ddb7f1ccf900784e347e1aa157e8e58043b0c2"
+  integrity sha512-2yy46qYhcdo8GZggQ3Zoq9QCahI0goddzpVI/vSnTpcClQBSDxYRCuAqRzzLqjvJ7hS0UYgplC7eRkM2sYgflw==
   dependencies:
     "@noble/curves" "1.7.0"
     "@noble/hashes" "1.6.1"
@@ -14402,8 +14451,7 @@ viem@^2.22.8:
     "@scure/bip39" "1.5.0"
     abitype "1.0.7"
     isows "1.0.6"
-    ox "0.6.0"
-    webauthn-p256 "0.0.10"
+    ox "0.6.5"
     ws "8.18.0"
 
 vite-node@2.1.4:
@@ -14547,14 +14595,6 @@ web3-utils@^1.3.6:
     randombytes "^2.1.0"
     utf8 "3.0.0"
 
-webauthn-p256@0.0.10:
-  version "0.0.10"
-  resolved "https://registry.yarnpkg.com/webauthn-p256/-/webauthn-p256-0.0.10.tgz#877e75abe8348d3e14485932968edf3325fd2fdd"
-  integrity sha512-EeYD+gmIT80YkSIDb2iWq0lq2zbHo1CxHlQTeJ+KkCILWpVy3zASH3ByD4bopzfk0uCwXxLqKGLqp2W4O28VFA==
-  dependencies:
-    "@noble/curves" "^1.4.0"
-    "@noble/hashes" "^1.4.0"
-
 webidl-conversions@^3.0.0:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"