From a94ff83962177f011eaa99772388f4d75fb680c4 Mon Sep 17 00:00:00 2001
From: Rian Hughes <ryanhughes4500@hotmail.com>
Date: Fri, 24 Nov 2023 10:38:15 +0300
Subject: [PATCH] rpcv06 implement v3 transactions (#462)

* rpcv06 implement v3 transactions

* update v3-txs to rpc1

* invokeV3 transaction hash

* Add transaction hash functions for deploy, declare v3

* Add DeclareTxHash test - wip

* wip

* wip - test deployAccount

* test - integration wip

* test - declareTxnHashV3 works

* test - invokeTxnHashV3 works

* test - deployAcntTxnHashV3 works

* Add erroneous test - no test data for v3 testnet

* fix v3 type fields

* Fix minor test

* Update rpc/types_transaction.go

Co-authored-by: Josh Klopfenstein <git@joshklop.com>

* address comments

* use mocks to test TxHash instead of requiring integration endpoint

* update transactionHash test to use mocks and not testnet

---------

Co-authored-by: Josh Klopfenstein <git@joshklop.com>
---
 account/account.go                  | 174 ++++++++++++---
 account/account_test.go             | 333 ++++++++++++++++++++--------
 rpc/contract_test.go                |  48 ++--
 rpc/mock_test.go                    |  23 +-
 rpc/types.go                        |  30 ++-
 rpc/types_block.go                  |  12 +-
 rpc/types_broadcast_transaction.go  |  21 ++
 rpc/types_transaction.go            | 126 ++++++++++-
 rpc/types_transaction_interfaces.go |  67 ++----
 rpc/types_transaction_receipt.go    |   5 +-
 utils/slices.go                     |  41 ++++
 11 files changed, 652 insertions(+), 228 deletions(-)
 create mode 100644 utils/slices.go

diff --git a/account/account.go b/account/account.go
index 2dc00bc2..75c289c9 100644
--- a/account/account.go
+++ b/account/account.go
@@ -5,6 +5,7 @@ import (
 	"errors"
 	"time"
 
+	"github.com/NethermindEth/juno/core/crypto"
 	"github.com/NethermindEth/juno/core/felt"
 	"github.com/NethermindEth/starknet.go/curve"
 	"github.com/NethermindEth/starknet.go/hash"
@@ -30,7 +31,7 @@ var (
 type AccountInterface interface {
 	Sign(ctx context.Context, msg *felt.Felt) ([]*felt.Felt, error)
 	TransactionHashInvoke(invokeTxn rpc.InvokeTxnType) (*felt.Felt, error)
-	TransactionHashDeployAccount(tx rpc.DeployAccountTxn, contractAddress *felt.Felt) (*felt.Felt, error)
+	TransactionHashDeployAccount(tx rpc.DeployAccountType, contractAddress *felt.Felt) (*felt.Felt, error)
 	TransactionHashDeclare(tx rpc.DeclareTxnType) (*felt.Felt, error)
 	SignInvokeTransaction(ctx context.Context, tx *rpc.InvokeTxnV1) error
 	SignDeployAccountTransaction(ctx context.Context, tx *rpc.DeployAccountTxn, precomputeAddress *felt.Felt) error
@@ -171,37 +172,65 @@ func (account *Account) SignDeclareTransaction(ctx context.Context, tx *rpc.Decl
 // Returns:
 // - *felt.Felt: the calculated transaction hash
 // - error: an error if any
-func (account *Account) TransactionHashDeployAccount(tx rpc.DeployAccountTxn, contractAddress *felt.Felt) (*felt.Felt, error) {
+func (account *Account) TransactionHashDeployAccount(tx rpc.DeployAccountType, contractAddress *felt.Felt) (*felt.Felt, error) {
 
 	// https://docs.starknet.io/documentation/architecture_and_concepts/Network_Architecture/transactions/#deploy_account_transaction
+	switch txn := tx.(type) {
+	case rpc.DeployAccountTxn:
+		calldata := []*felt.Felt{txn.ClassHash, txn.ContractAddressSalt}
+		calldata = append(calldata, txn.ConstructorCalldata...)
+		calldataHash, err := hash.ComputeHashOnElementsFelt(calldata)
+		if err != nil {
+			return nil, err
+		}
 
-	// There is only version 1 of deployAccount txn
-	if tx.Version != rpc.TransactionV1 {
-		return nil, ErrTxnTypeUnSupported
-	}
-	calldata := []*felt.Felt{tx.ClassHash, tx.ContractAddressSalt}
-	calldata = append(calldata, tx.ConstructorCalldata...)
-	calldataHash, err := hash.ComputeHashOnElementsFelt(calldata)
-	if err != nil {
-		return nil, err
-	}
+		versionFelt, err := new(felt.Felt).SetString(string(txn.Version))
+		if err != nil {
+			return nil, err
+		}
 
-	versionFelt, err := new(felt.Felt).SetString(string(tx.Version))
-	if err != nil {
-		return nil, err
-	}
+		// https://docs.starknet.io/documentation/architecture_and_concepts/Network_Architecture/transactions/#deploy_account_hash_calculation
+		return hash.CalculateTransactionHashCommon(
+			PREFIX_DEPLOY_ACCOUNT,
+			versionFelt,
+			contractAddress,
+			&felt.Zero,
+			calldataHash,
+			txn.MaxFee,
+			account.ChainId,
+			[]*felt.Felt{txn.Nonce},
+		)
+	case rpc.DeployAccountTxnV3:
+		if txn.Version == "" || txn.ResourceBounds == (rpc.ResourceBoundsMapping{}) || txn.Nonce == nil || txn.PayMasterData == nil {
+			return nil, ErrNotAllParametersSet
+		}
+		calldata := []*felt.Felt{txn.ClassHash, txn.ContractAddressSalt}
+		calldata = append(calldata, txn.ConstructorCalldata...)
 
-	// https://docs.starknet.io/documentation/architecture_and_concepts/Network_Architecture/transactions/#deploy_account_hash_calculation
-	return hash.CalculateTransactionHashCommon(
-		PREFIX_DEPLOY_ACCOUNT,
-		versionFelt,
-		contractAddress,
-		&felt.Zero,
-		calldataHash,
-		tx.MaxFee,
-		account.ChainId,
-		[]*felt.Felt{tx.Nonce},
-	)
+		txnVersionFelt, err := new(felt.Felt).SetString(string(txn.Version))
+		if err != nil {
+			return nil, err
+		}
+		DAUint64, err := dataAvailabilityMode(txn.FeeMode, txn.NonceDataMode)
+		if err != nil {
+			return nil, err
+		}
+		// https://docs.starknet.io/documentation/architecture_and_concepts/Network_Architecture/transactions/#deploy_account_hash_calculation
+		return crypto.PoseidonArray(
+			PREFIX_DEPLOY_ACCOUNT,
+			txnVersionFelt,
+			contractAddress,
+			tipAndResourcesHash(txn.Tip.Impl().Uint64(), txn.ResourceBounds),
+			crypto.PoseidonArray(txn.PayMasterData...),
+			account.ChainId,
+			txn.Nonce,
+			new(felt.Felt).SetUint64(DAUint64),
+			crypto.PoseidonArray(txn.ConstructorCalldata...),
+			txn.ClassHash,
+			txn.ContractAddressSalt,
+		), nil
+	}
+	return nil, ErrTxnTypeUnSupported
 }
 
 // TransactionHashInvoke calculates the transaction hash for the given invoke transaction.
@@ -270,10 +299,56 @@ func (account *Account) TransactionHashInvoke(tx rpc.InvokeTxnType) (*felt.Felt,
 			account.ChainId,
 			[]*felt.Felt{txn.Nonce},
 		)
+	case rpc.InvokeTxnV3:
+		// https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-8.md#protocol-changes
+		if txn.Version == "" || txn.ResourceBounds == (rpc.ResourceBoundsMapping{}) || len(txn.Calldata) == 0 || txn.Nonce == nil || txn.SenderAddress == nil || txn.PayMasterData == nil || txn.AccountDeploymentData == nil {
+			return nil, ErrNotAllParametersSet
+		}
+
+		txnVersionFelt, err := new(felt.Felt).SetString(string(txn.Version))
+		if err != nil {
+			return nil, err
+		}
+		DAUint64, err := dataAvailabilityMode(txn.FeeMode, txn.NonceDataMode)
+		if err != nil {
+			return nil, err
+		}
+
+		return crypto.PoseidonArray(
+			PREFIX_TRANSACTION,
+			txnVersionFelt,
+			txn.SenderAddress,
+			tipAndResourcesHash(txn.Tip.Impl().Uint64(), txn.ResourceBounds),
+			crypto.PoseidonArray(txn.PayMasterData...),
+			account.ChainId,
+			txn.Nonce,
+			new(felt.Felt).SetUint64(DAUint64),
+			crypto.PoseidonArray(txn.AccountDeploymentData...),
+			crypto.PoseidonArray(txn.Calldata...),
+		), nil
 	}
 	return nil, ErrTxnTypeUnSupported
 }
 
+func tipAndResourcesHash(tip uint64, resourceBounds rpc.ResourceBoundsMapping) *felt.Felt {
+	l1Bounds := new(felt.Felt).SetBytes(resourceBounds.L1Gas.Bytes(rpc.ResourceL1Gas))
+	l2Bounds := new(felt.Felt).SetBytes(resourceBounds.L2Gas.Bytes(rpc.ResourceL2Gas))
+	return crypto.PoseidonArray(new(felt.Felt).SetUint64(tip), l1Bounds, l2Bounds)
+}
+
+func dataAvailabilityMode(feeDAMode, nonceDAMode rpc.DataAvailabilityMode) (uint64, error) {
+	const dataAvailabilityModeBits = 32
+	fee64, err := feeDAMode.UInt64()
+	if err != nil {
+		return 0, err
+	}
+	nonce64, err := nonceDAMode.UInt64()
+	if err != nil {
+		return 0, err
+	}
+	return fee64 + nonce64<<dataAvailabilityModeBits, nil
+}
+
 // TransactionHashDeclare calculates the transaction hash for declaring a transaction type.
 //
 // Parameters:
@@ -342,6 +417,35 @@ func (account *Account) TransactionHashDeclare(tx rpc.DeclareTxnType) (*felt.Fel
 			account.ChainId,
 			[]*felt.Felt{txn.Nonce, txn.CompiledClassHash},
 		)
+	case rpc.DeclareTxnV3:
+		// https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-8.md#protocol-changes
+		if txn.Version == "" || txn.ResourceBounds == (rpc.ResourceBoundsMapping{}) || txn.Nonce == nil || txn.SenderAddress == nil || txn.PayMasterData == nil || txn.AccountDeploymentData == nil ||
+			txn.ClassHash == nil || txn.CompiledClassHash == nil {
+			return nil, ErrNotAllParametersSet
+		}
+
+		txnVersionFelt, err := new(felt.Felt).SetString(string(txn.Version))
+		if err != nil {
+			return nil, err
+		}
+		DAUint64, err := dataAvailabilityMode(txn.FeeMode, txn.NonceDataMode)
+		if err != nil {
+			return nil, err
+		}
+
+		return crypto.PoseidonArray(
+			PREFIX_DECLARE,
+			txnVersionFelt,
+			txn.SenderAddress,
+			tipAndResourcesHash(txn.Tip.Impl().Uint64(), txn.ResourceBounds),
+			crypto.PoseidonArray(txn.PayMasterData...),
+			account.ChainId,
+			txn.Nonce,
+			new(felt.Felt).SetUint64(DAUint64),
+			crypto.PoseidonArray(txn.AccountDeploymentData...),
+			txn.ClassHash,
+			txn.CompiledClassHash,
+		), nil
 	}
 
 	return nil, ErrTxnTypeUnSupported
@@ -473,7 +577,7 @@ func (account *Account) BlockNumber(ctx context.Context) (uint64, error) {
 // - blockID: The rpc.BlockID object representing the block.
 // Returns:
 // - uint64: the number of transactions in the block
-//  - error: an error, if any
+//   - error: an error, if any
 func (account *Account) BlockTransactionCount(ctx context.Context, blockID rpc.BlockID) (uint64, error) {
 	return account.provider.BlockTransactionCount(ctx, blockID)
 }
@@ -530,12 +634,12 @@ func (account *Account) ChainID(ctx context.Context) (string, error) {
 //
 // Parameters:
 // - ctx: The context.Context
-// - blockID: The rpc.BlockID 
+// - blockID: The rpc.BlockID
 // - classHash: The `*felt.Felt`
 // Returns:
-// - *rpc.ClassOutput: The rpc.ClassOutput (the class output could be a DeprecatedContractClass
+//   - *rpc.ClassOutput: The rpc.ClassOutput (the class output could be a DeprecatedContractClass
 //     or just a Contract class depending on the contract version)
-// -  error: An error if any occurred.
+//   - error: An error if any occurred.
 func (account *Account) Class(ctx context.Context, blockID rpc.BlockID, classHash *felt.Felt) (rpc.ClassOutput, error) {
 	return account.provider.Class(ctx, blockID, classHash)
 }
@@ -546,9 +650,9 @@ func (account *Account) Class(ctx context.Context, blockID rpc.BlockID, classHas
 // - blockID: The rpc.BlockID object representing the block ID.
 // - contractAddress: The felt.Felt object representing the contract address.
 // Returns:
-// - *rpc.ClassOutput: The rpc.ClassOutput object (the class output could be a DeprecatedContractClass
+//   - *rpc.ClassOutput: The rpc.ClassOutput object (the class output could be a DeprecatedContractClass
 //     or just a Contract class depending on the contract version)
-// - error: An error if any occurred.
+//   - error: An error if any occurred.
 func (account *Account) ClassAt(ctx context.Context, blockID rpc.BlockID, contractAddress *felt.Felt) (rpc.ClassOutput, error) {
 	return account.provider.ClassAt(ctx, blockID, contractAddress)
 }
@@ -618,7 +722,7 @@ func (account *Account) Nonce(ctx context.Context, blockID rpc.BlockID, contract
 }
 
 // SimulateTransactions simulates transactions using the provided context
-// Parameters: 
+// Parameters:
 // - ctx: The context.Context object
 // - blockID: The rpc.BlockID object for the block referencing the state or call the transactions are on
 // - txns: The slice of rpc.Transaction objects representing the transactions to simulate
@@ -707,7 +811,7 @@ func (account *Account) TransactionReceipt(ctx context.Context, transactionHash
 // Parameters:
 // - ctx: The context.Context object for the request.
 // - transactionHash: The transaction hash for which the transaction trace is to be retrieved.
-// Returns: 
+// Returns:
 // - rpc.TxnTrace: The rpc.TxnTrace object representing the transaction trace, and an error if any.
 func (account *Account) TraceTransaction(ctx context.Context, transactionHash *felt.Felt) (rpc.TxnTrace, error) {
 	return account.provider.TraceTransaction(ctx, transactionHash)
diff --git a/account/account_test.go b/account/account_test.go
index 0c80c60e..6eac0e30 100644
--- a/account/account_test.go
+++ b/account/account_test.go
@@ -41,7 +41,8 @@ var (
 // Parameters:
 // - m: is the test main
 // Returns:
-//  none
+//
+//	none
 func TestMain(m *testing.M) {
 	flag.StringVar(&testEnv, "env", "mock", "set the test environment")
 	flag.Parse()
@@ -60,11 +61,13 @@ func TestMain(m *testing.M) {
 // of the transaction hash. Each test case consists of the expected hash, a flag
 // indicating whether the KeyStore should be set, account address, public key,
 // private key, chain ID, function call, and transaction details.
-// 
+//
 // Parameters:
 //   - t: The testing.T object for running the test
+//
 // Returns:
-//   none
+//
+//	none
 func TestTransactionHashInvoke(t *testing.T) {
 	mockCtrl := gomock.NewController(t)
 	t.Cleanup(mockCtrl.Finish)
@@ -184,11 +187,12 @@ func TestTransactionHashInvoke(t *testing.T) {
 //
 // It tests the FmtCallData function by providing different test sets
 // and comparing the output with the expected call data.
-// 
+//
 // Parameters:
 // - t: The testing.T instance for running the test
 // Return:
-//   none
+//
+//	none
 func TestFmtCallData(t *testing.T) {
 	mockCtrl := gomock.NewController(t)
 	t.Cleanup(mockCtrl.Finish)
@@ -253,7 +257,8 @@ func TestFmtCallData(t *testing.T) {
 // Parameters:
 // - t: The testing.T instance for running the test
 // Return:
-//   none
+//
+//	none
 func TestChainIdMOCK(t *testing.T) {
 	mockCtrl := gomock.NewController(t)
 	t.Cleanup(mockCtrl.Finish)
@@ -296,7 +301,8 @@ func TestChainIdMOCK(t *testing.T) {
 // Parameters:
 // - t: The testing.T instance for running the test
 // Return:
-//   none
+//
+//	none
 func TestChainId(t *testing.T) {
 	mockCtrl := gomock.NewController(t)
 	t.Cleanup(mockCtrl.Finish)
@@ -344,7 +350,8 @@ func TestChainId(t *testing.T) {
 // Parameters:
 // - t: The testing.T instance for running the test
 // Returns:
-//  none
+//
+//	none
 func TestSignMOCK(t *testing.T) {
 	mockCtrl := gomock.NewController(t)
 	t.Cleanup(mockCtrl.Finish)
@@ -404,7 +411,8 @@ func TestSignMOCK(t *testing.T) {
 // Parameters:
 // - t: The testing.T instance for running the test
 // Returns:
-//   none
+//
+//	none
 func TestAddInvoke(t *testing.T) {
 
 	type testSetType struct {
@@ -582,9 +590,11 @@ func TestAddInvoke(t *testing.T) {
 // response is not nil.
 //
 // Parameters:
-//  - t: is the testing framework
+//   - t: is the testing framework
+//
 // Returns:
-//  none
+//
+//	none
 func TestAddDeployAccountDevnet(t *testing.T) {
 	if testEnv != "devnet" {
 		t.Skip("Skipping test as it requires a devnet environment")
@@ -633,65 +643,6 @@ func TestAddDeployAccountDevnet(t *testing.T) {
 	require.NotNil(t, resp, "AddDeployAccountTransaction resp not nil")
 }
 
-// TestTransactionHashDeployAccountTestnet tests the TransactionHashDeployAccount function when using the testnet environment.
-//
-// It creates a client and provider, initializes the required addresses and keys, and sets up the transaction parameters.
-// It then precomputes the address and calculates the hash of the transaction.
-// Finally, it verifies that the calculated hash matches the expected hash.
-//
-// Parameters:
-//  - t: is the testing framework
-// Returns:
-//  none
-func TestTransactionHashDeployAccountTestnet(t *testing.T) {
-
-	if testEnv != "testnet" {
-		t.Skip("Skipping test as it requires a testnet environment")
-	}
-
-	client, err := rpc.NewClient(base)
-	require.NoError(t, err, "Error in rpc.NewClient")
-	provider := rpc.NewProvider(client)
-
-	AccountAddress := utils.TestHexToFelt(t, "0x0088d0038623a89bf853c70ea68b1062ccf32b094d1d7e5f924cda8404dc73e1")
-	PubKey := utils.TestHexToFelt(t, "0x7ed3c6482e12c3ef7351214d1195ee7406d814af04a305617599ff27be43883")
-	PrivKey := utils.TestHexToFelt(t, "0x07514c4f0de1f800b0b0c7377ef39294ce218a7abd9a1c9b6aa574779f7cdc6a")
-
-	ExpectedHash := utils.TestHexToFelt(t, "0x5b6b5927cd70ad7a80efdbe898244525871875c76540b239f6730118598b9cb")
-	ExpectedPrecomputeAddr := utils.TestHexToFelt(t, "0x88d0038623a89bf853c70ea68b1062ccf32b094d1d7e5f924cda8404dc73e1")
-	ks := account.NewMemKeystore()
-	fakePrivKeyBI, ok := new(big.Int).SetString(PrivKey.String(), 0)
-	require.True(t, ok)
-	ks.Put(PubKey.String(), fakePrivKeyBI)
-
-	acnt, err := account.NewAccount(provider, AccountAddress, PubKey.String(), ks)
-	require.NoError(t, err)
-
-	classHash := utils.TestHexToFelt(t, "0x3131fa018d520a037686ce3efddeab8f28895662f019ca3ca18a626650f7d1e")
-
-	tx := rpc.DeployAccountTxn{
-		Nonce:               &felt.Zero,
-		MaxFee:              utils.TestHexToFelt(t, "0x105ef39b2000"),
-		Type:                rpc.TransactionType_DeployAccount,
-		Version:             rpc.TransactionV1,
-		Signature:           []*felt.Felt{},
-		ClassHash:           classHash,
-		ContractAddressSalt: utils.TestHexToFelt(t, "0x7ed3c6482e12c3ef7351214d1195ee7406d814af04a305617599ff27be43883"),
-		ConstructorCalldata: []*felt.Felt{
-			utils.TestHexToFelt(t, "0x5aa23d5bb71ddaa783da7ea79d405315bafa7cf0387a74f4593578c3e9e6570"),
-			utils.TestHexToFelt(t, "0x2dd76e7ad84dbed81c314ffe5e7a7cacfb8f4836f01af4e913f275f89a3de1a"),
-			utils.TestHexToFelt(t, "0x1"),
-			utils.TestHexToFelt(t, "0x7ed3c6482e12c3ef7351214d1195ee7406d814af04a305617599ff27be43883"),
-		},
-	}
-	precomputedAddress, err := acnt.PrecomputeAddress(&felt.Zero, tx.ContractAddressSalt, classHash, tx.ConstructorCalldata)
-	require.Equal(t, ExpectedPrecomputeAddr.String(), precomputedAddress.String(), "Error with calulcating PrecomputeAddress")
-
-	hash, err := acnt.TransactionHashDeployAccount(tx, precomputedAddress)
-	require.NoError(t, err, "TransactionHashDeployAccount gave an Error")
-	require.Equal(t, hash.String(), ExpectedHash.String(), "Error with calulcating TransactionHashDeployAccount")
-}
-
 // TestTransactionHashDeclare tests the TransactionHashDeclare function.
 //
 // This function verifies that the TransactionHashDeclare function returns the
@@ -709,35 +660,225 @@ func TestTransactionHashDeployAccountTestnet(t *testing.T) {
 // Parameters:
 // - t: reference to the testing.T object
 // Returns:
-//  none
+//
+//	none
 func TestTransactionHashDeclare(t *testing.T) {
-	// https://goerli.voyager.online/tx/0x4e0519272438a3ae0d0fca776136e2bb6fcd5d3b2af47e53575c5874ccfce92
-	if testEnv != "testnet" {
-		t.Skip("Skipping test as it requires a testnet environment")
+	mockCtrl := gomock.NewController(t)
+	t.Cleanup(mockCtrl.Finish)
+	mockRpcProvider := mocks.NewMockRpcProvider(mockCtrl)
+	mockRpcProvider.EXPECT().ChainID(context.Background()).Return("SN_GOERLI", nil)
+
+	acnt, err := account.NewAccount(mockRpcProvider, &felt.Zero, "", account.NewMemKeystore())
+	require.NoError(t, err)
+
+	type testSetType struct {
+		Txn          rpc.DeclareTxnType
+		ExpectedHash *felt.Felt
+		ExpectedErr  error
+	}
+	testSet := map[string][]testSetType{
+		"mock": {{
+			// Note this is a testnet / goerli transaction
+			Txn: rpc.DeclareTxnV2{
+				Nonce:             utils.TestHexToFelt(t, "0xb"),
+				MaxFee:            utils.TestHexToFelt(t, "0x50c8f3053db"),
+				Type:              rpc.TransactionType_Declare,
+				Version:           rpc.TransactionV2,
+				Signature:         []*felt.Felt{},
+				SenderAddress:     utils.TestHexToFelt(t, "0x36437dffa1b0bf630f04690a3b302adbabb942deb488ea430660c895ff25acf"),
+				CompiledClassHash: utils.TestHexToFelt(t, "0x615a5260d3d47d79fba87898da95cb5394b181c7d5097bc8ced4ed06ac24ac5"),
+				ClassHash:         utils.TestHexToFelt(t, "0x639cdc0c42c8c4d3d805e56294fa0e6bf5a584ad0fcd538b843cc294913b982"),
+			},
+			ExpectedHash: utils.TestHexToFelt(t, "0x4e0519272438a3ae0d0fca776136e2bb6fcd5d3b2af47e53575c5874ccfce92"),
+			ExpectedErr:  nil,
+		},
+			{
+				// https://external.integration.starknet.io/feeder_gateway/get_transaction?transactionHash=0x41d1f5206ef58a443e7d3d1ca073171ec25fa75313394318fc83a074a6631c3
+				Txn: rpc.DeclareTxnV3{
+					Nonce:   utils.TestHexToFelt(t, "0x1"),
+					Type:    rpc.TransactionType_Declare,
+					Version: rpc.TransactionV3,
+					Signature: []*felt.Felt{
+						utils.TestHexToFelt(t, "0x29a49dff154fede73dd7b5ca5a0beadf40b4b069f3a850cd8428e54dc809ccc"),
+						utils.TestHexToFelt(t, "0x429d142a17223b4f2acde0f5ecb9ad453e188b245003c86fab5c109bad58fc3")},
+					SenderAddress:     utils.TestHexToFelt(t, "0x2fab82e4aef1d8664874e1f194951856d48463c3e6bf9a8c68e234a629a6f50"),
+					CompiledClassHash: utils.TestHexToFelt(t, "0x1add56d64bebf8140f3b8a38bdf102b7874437f0c861ab4ca7526ec33b4d0f8"),
+					ClassHash:         utils.TestHexToFelt(t, "0x5ae9d09292a50ed48c5930904c880dab56e85b825022a7d689cfc9e65e01ee7"),
+					ResourceBounds: rpc.ResourceBoundsMapping{
+						L1Gas: rpc.ResourceBounds{
+							MaxAmount:       utils.TestHexToFelt(t, "0x186a0"),
+							MaxPricePerUnit: utils.TestHexToFelt(t, "0x2540be400"),
+						},
+						L2Gas: rpc.ResourceBounds{
+							MaxAmount:       utils.TestHexToFelt(t, "0x0"),
+							MaxPricePerUnit: utils.TestHexToFelt(t, "0x0"),
+						},
+					},
+					Tip:                   utils.TestHexToFelt(t, "0x0"),
+					PayMasterData:         []*felt.Felt{},
+					AccountDeploymentData: []*felt.Felt{},
+					NonceDataMode:         rpc.DAModeL1,
+					FeeMode:               rpc.DAModeL1,
+				},
+				ExpectedHash: utils.TestHexToFelt(t, "0x41d1f5206ef58a443e7d3d1ca073171ec25fa75313394318fc83a074a6631c3"),
+				ExpectedErr:  nil,
+			},
+		}}[testEnv]
+	for _, test := range testSet {
+		hash, err := acnt.TransactionHashDeclare(test.Txn)
+		require.Equal(t, test.ExpectedErr, err)
+		require.Equal(t, test.ExpectedHash.String(), hash.String(), "TransactionHashDeclare not what expected")
 	}
-	expectedHash := utils.TestHexToFelt(t, "0x4e0519272438a3ae0d0fca776136e2bb6fcd5d3b2af47e53575c5874ccfce92")
+}
 
-	client, err := rpc.NewClient(base)
-	require.NoError(t, err, "Error in rpc.NewClient")
-	provider := rpc.NewProvider(client)
+func TestTransactionHashInvokeV3(t *testing.T) {
+
+	mockCtrl := gomock.NewController(t)
+	t.Cleanup(mockCtrl.Finish)
+	mockRpcProvider := mocks.NewMockRpcProvider(mockCtrl)
+	mockRpcProvider.EXPECT().ChainID(context.Background()).Return("SN_GOERLI", nil)
 
-	acnt, err := account.NewAccount(provider, &felt.Zero, "", account.NewMemKeystore())
+	acnt, err := account.NewAccount(mockRpcProvider, &felt.Zero, "", account.NewMemKeystore())
 	require.NoError(t, err)
 
-	tx := rpc.DeclareTxnV2{
-		Nonce:             utils.TestHexToFelt(t, "0xb"),
-		MaxFee:            utils.TestHexToFelt(t, "0x50c8f3053db"),
-		Type:              rpc.TransactionType_Declare,
-		Version:           rpc.TransactionV2,
-		Signature:         []*felt.Felt{},
-		SenderAddress:     utils.TestHexToFelt(t, "0x36437dffa1b0bf630f04690a3b302adbabb942deb488ea430660c895ff25acf"),
-		CompiledClassHash: utils.TestHexToFelt(t, "0x615a5260d3d47d79fba87898da95cb5394b181c7d5097bc8ced4ed06ac24ac5"),
-		ClassHash:         utils.TestHexToFelt(t, "0x639cdc0c42c8c4d3d805e56294fa0e6bf5a584ad0fcd538b843cc294913b982"),
+	type testSetType struct {
+		Txn          rpc.DeclareTxnType
+		ExpectedHash *felt.Felt
+		ExpectedErr  error
+	}
+	testSet := map[string][]testSetType{
+		"mock": {
+			{
+				// https://external.integration.starknet.io/feeder_gateway/get_transaction?transactionHash=0x49728601e0bb2f48ce506b0cbd9c0e2a9e50d95858aa41463f46386dca489fd
+				Txn: rpc.InvokeTxnV3{
+					Nonce:   utils.TestHexToFelt(t, "0xe97"),
+					Type:    rpc.TransactionType_Invoke,
+					Version: rpc.TransactionV3,
+					Signature: []*felt.Felt{
+						utils.TestHexToFelt(t, "0x71a9b2cd8a8a6a4ca284dcddcdefc6c4fd20b92c1b201bd9836e4ce376fad16"),
+						utils.TestHexToFelt(t, "0x6bef4745194c9447fdc8dd3aec4fc738ab0a560b0d2c7bf62fbf58aef3abfc5")},
+					ResourceBounds: rpc.ResourceBoundsMapping{
+						L1Gas: rpc.ResourceBounds{
+							MaxAmount:       utils.TestHexToFelt(t, "0x186a0"),
+							MaxPricePerUnit: utils.TestHexToFelt(t, "0x5af3107a4000"),
+						},
+						L2Gas: rpc.ResourceBounds{
+							MaxAmount:       utils.TestHexToFelt(t, "0x0"),
+							MaxPricePerUnit: utils.TestHexToFelt(t, "0x0"),
+						},
+					},
+					Tip:                   utils.TestHexToFelt(t, "0x0"),
+					PayMasterData:         []*felt.Felt{},
+					AccountDeploymentData: []*felt.Felt{},
+					SenderAddress:         utils.TestHexToFelt(t, "0x3f6f3bc663aedc5285d6013cc3ffcbc4341d86ab488b8b68d297f8258793c41"),
+					Calldata: utils.TestHexArrToFelt(t, []string{
+						"0x2",
+						"0x450703c32370cf7ffff540b9352e7ee4ad583af143a361155f2b485c0c39684",
+						"0x27c3334165536f239cfd400ed956eabff55fc60de4fb56728b6a4f6b87db01c",
+						"0x0",
+						"0x4",
+						"0x4c312760dfd17a954cdd09e76aa9f149f806d88ec3e402ffaf5c4926f568a42",
+						"0x5df99ae77df976b4f0e5cf28c7dcfe09bd6e81aab787b19ac0c08e03d928cf",
+						"0x4",
+						"0x1",
+						"0x5",
+						"0x450703c32370cf7ffff540b9352e7ee4ad583af143a361155f2b485c0c39684",
+						"0x5df99ae77df976b4f0e5cf28c7dcfe09bd6e81aab787b19ac0c08e03d928cf",
+						"0x1",
+						"0x7fe4fd616c7fece1244b3616bb516562e230be8c9f29668b46ce0369d5ca829",
+						"0x287acddb27a2f9ba7f2612d72788dc96a5b30e401fc1e8072250940e024a587",
+					}),
+					NonceDataMode: rpc.DAModeL1,
+					FeeMode:       rpc.DAModeL1,
+				},
+				ExpectedHash: utils.TestHexToFelt(t, "0x49728601e0bb2f48ce506b0cbd9c0e2a9e50d95858aa41463f46386dca489fd"),
+				ExpectedErr:  nil,
+			},
+		}}[testEnv]
+	for _, test := range testSet {
+		hash, err := acnt.TransactionHashInvoke(test.Txn)
+		require.Equal(t, test.ExpectedErr, err)
+		require.Equal(t, test.ExpectedHash.String(), hash.String(), "TransactionHashDeclare not what expected")
 	}
+}
+
+func TestTransactionHashdeployAccount(t *testing.T) {
+
+	mockCtrl := gomock.NewController(t)
+	t.Cleanup(mockCtrl.Finish)
+	mockRpcProvider := mocks.NewMockRpcProvider(mockCtrl)
+	mockRpcProvider.EXPECT().ChainID(context.Background()).Return("SN_GOERLI", nil)
 
-	hash, err := acnt.TransactionHashDeclare(tx)
+	acnt, err := account.NewAccount(mockRpcProvider, &felt.Zero, "", account.NewMemKeystore())
 	require.NoError(t, err)
-	require.Equal(t, expectedHash.String(), hash.String(), "TransactionHashDeclare not what expected")
+
+	type testSetType struct {
+		Txn           rpc.DeployAccountType
+		SenderAddress *felt.Felt
+		ExpectedHash  *felt.Felt
+		ExpectedErr   error
+	}
+	testSet := map[string][]testSetType{
+		"mock": {
+			{
+				Txn: rpc.DeployAccountTxn{
+					Nonce:               &felt.Zero,
+					MaxFee:              utils.TestHexToFelt(t, "0x105ef39b2000"),
+					Type:                rpc.TransactionType_DeployAccount,
+					Version:             rpc.TransactionV1,
+					Signature:           []*felt.Felt{},
+					ClassHash:           utils.TestHexToFelt(t, "0x3131fa018d520a037686ce3efddeab8f28895662f019ca3ca18a626650f7d1e"),
+					ContractAddressSalt: utils.TestHexToFelt(t, "0x7ed3c6482e12c3ef7351214d1195ee7406d814af04a305617599ff27be43883"),
+					ConstructorCalldata: []*felt.Felt{
+						utils.TestHexToFelt(t, "0x5aa23d5bb71ddaa783da7ea79d405315bafa7cf0387a74f4593578c3e9e6570"),
+						utils.TestHexToFelt(t, "0x2dd76e7ad84dbed81c314ffe5e7a7cacfb8f4836f01af4e913f275f89a3de1a"),
+						utils.TestHexToFelt(t, "0x1"),
+						utils.TestHexToFelt(t, "0x7ed3c6482e12c3ef7351214d1195ee7406d814af04a305617599ff27be43883"),
+					},
+				},
+				SenderAddress: utils.TestHexToFelt(t, "0x88d0038623a89bf853c70ea68b1062ccf32b094d1d7e5f924cda8404dc73e1"),
+				ExpectedHash:  utils.TestHexToFelt(t, "0x5b6b5927cd70ad7a80efdbe898244525871875c76540b239f6730118598b9cb"),
+				ExpectedErr:   nil,
+			},
+			{
+				// https://external.integration.starknet.io/feeder_gateway/get_transaction?transactionHash=0x29fd7881f14380842414cdfdd8d6c0b1f2174f8916edcfeb1ede1eb26ac3ef0
+				Txn: rpc.DeployAccountTxnV3{
+					Nonce:   utils.TestHexToFelt(t, "0x0"),
+					Type:    rpc.TransactionType_DeployAccount,
+					Version: rpc.TransactionV3,
+					Signature: []*felt.Felt{
+						utils.TestHexToFelt(t, "0x6d756e754793d828c6c1a89c13f7ec70dbd8837dfeea5028a673b80e0d6b4ec"),
+						utils.TestHexToFelt(t, "0x4daebba599f860daee8f6e100601d98873052e1c61530c630cc4375c6bd48e3")},
+					ResourceBounds: rpc.ResourceBoundsMapping{
+						L1Gas: rpc.ResourceBounds{
+							MaxAmount:       utils.TestHexToFelt(t, "0x186a0"),
+							MaxPricePerUnit: utils.TestHexToFelt(t, "0x5af3107a4000"),
+						},
+						L2Gas: rpc.ResourceBounds{
+							MaxAmount:       utils.TestHexToFelt(t, "0x0"),
+							MaxPricePerUnit: utils.TestHexToFelt(t, "0x0"),
+						},
+					},
+					Tip:           utils.TestHexToFelt(t, "0x0"),
+					PayMasterData: []*felt.Felt{},
+					NonceDataMode: rpc.DAModeL1,
+					FeeMode:       rpc.DAModeL1,
+					ClassHash:     utils.TestHexToFelt(t, "0x2338634f11772ea342365abd5be9d9dc8a6f44f159ad782fdebd3db5d969738"),
+					ConstructorCalldata: utils.TestHexArrToFelt(t, []string{
+						"0x5cd65f3d7daea6c63939d659b8473ea0c5cd81576035a4d34e52fb06840196c",
+					}),
+					ContractAddressSalt: utils.TestHexToFelt(t, "0x0"),
+				},
+				SenderAddress: utils.TestHexToFelt(t, "0x2fab82e4aef1d8664874e1f194951856d48463c3e6bf9a8c68e234a629a6f50"),
+				ExpectedHash:  utils.TestHexToFelt(t, "0x29fd7881f14380842414cdfdd8d6c0b1f2174f8916edcfeb1ede1eb26ac3ef0"),
+				ExpectedErr:   nil,
+			},
+		}}[testEnv]
+	for _, test := range testSet {
+		hash, err := acnt.TransactionHashDeployAccount(test.Txn, test.SenderAddress)
+		require.Equal(t, test.ExpectedErr, err)
+		require.Equal(t, test.ExpectedHash.String(), hash.String(), "TransactionHashDeclare not what expected")
+	}
 }
 
 // TestWaitForTransactionReceiptMOCK is a unit test for the WaitForTransactionReceipt function.
@@ -751,7 +892,8 @@ func TestTransactionHashDeclare(t *testing.T) {
 // Parameters:
 // - t: The testing.T object for test assertions and logging
 // Returns:
-//  none
+//
+//	none
 func TestWaitForTransactionReceiptMOCK(t *testing.T) {
 	mockCtrl := gomock.NewController(t)
 	t.Cleanup(mockCtrl.Finish)
@@ -833,7 +975,8 @@ func TestWaitForTransactionReceiptMOCK(t *testing.T) {
 // Parameters:
 // - t: The testing.T instance for running the test
 // Returns:
-//  none
+//
+//	none
 func TestWaitForTransactionReceipt(t *testing.T) {
 	if testEnv != "devnet" {
 		t.Skip("Skipping test as it requires a devnet environment")
@@ -882,9 +1025,11 @@ func TestWaitForTransactionReceipt(t *testing.T) {
 // It asserts that the expected hash and error values are returned for each test set.
 //
 // Parameters:
-//  - t: The testing.T instance for running the test
+//   - t: The testing.T instance for running the test
+//
 // Returns:
-//  none
+//
+//	none
 func TestAddDeclareTxn(t *testing.T) {
 	// https://goerli.voyager.online/tx/0x76af2faec46130ffad1ab2f615ad16b30afcf49cfbd09f655a26e545b03a21d
 	if testEnv != "testnet" {
diff --git a/rpc/contract_test.go b/rpc/contract_test.go
index 51609006..d8c7274e 100644
--- a/rpc/contract_test.go
+++ b/rpc/contract_test.go
@@ -18,17 +18,18 @@ import (
 //   - Sets the provider of the test configuration to the spy object.
 //   - Calls the ClassAt function with the specified block tag and contract address.
 //   - Checks the response type and performs the following actions based on the type:
-//     - If the response type is DeprecatedContractClass:
-//       - Compares the response object with the spy object and checks for a full match.
-//       - If the objects do not match, compares them again and logs an error if the match is still not achieved.
-//       - Checks if the program code exists in the response object.
-//     - If the response type is ContractClass:
-//       - Throws an error indicating that the case is not covered.
+//   - If the response type is DeprecatedContractClass:
+//   - Compares the response object with the spy object and checks for a full match.
+//   - If the objects do not match, compares them again and logs an error if the match is still not achieved.
+//   - Checks if the program code exists in the response object.
+//   - If the response type is ContractClass:
+//   - Throws an error indicating that the case is not covered.
 //
 // Parameters:
 // - t: the testing object for running the test cases
 // Returns:
-//  none
+//
+//	none
 func TestClassAt(t *testing.T) {
 	testConfig := beforeEach(t)
 
@@ -96,7 +97,8 @@ func TestClassAt(t *testing.T) {
 // Parameters:
 // - t: the testing object for running the test cases
 // Returns:
-//  none
+//
+//	none
 func TestClassHashAt(t *testing.T) {
 	testConfig := beforeEach(t)
 
@@ -162,20 +164,21 @@ func TestClassHashAt(t *testing.T) {
 // - Calls the Class function with the appropriate parameters.
 // - Handles the response based on its type:
 //   - If the response is of type DeprecatedContractClass:
-//     - Compares the response with the spy object to check for any differences.
-//     - If there is a difference, it reports an error and prints the difference.
-//     - Checks if the class program starts with the expected program.
-//     - If not, it reports an error.
+//   - Compares the response with the spy object to check for any differences.
+//   - If there is a difference, it reports an error and prints the difference.
+//   - Checks if the class program starts with the expected program.
+//   - If not, it reports an error.
 //   - If the response is of type ContractClass:
-//     - Compares the constructor entry point with the expected entry point constructor.
-//     - If they are not equal, it reports an error.
+//   - Compares the constructor entry point with the expected entry point constructor.
+//   - If they are not equal, it reports an error.
 //
 // The function is used for testing the behavior of the Class function in different scenarios.
 //
 // Parameters:
 // - t: A *testing.T object used for reporting test failures and logging
 // Returns:
-//  none
+//
+//	none
 func TestClass(t *testing.T) {
 	testConfig := beforeEach(t)
 
@@ -249,7 +252,8 @@ func TestClass(t *testing.T) {
 // Parameters:
 // - t: The testing.T instance used for reporting test failures and logging
 // Returns:
-//  none
+//
+//	none
 func TestStorageAt(t *testing.T) {
 	testConfig := beforeEach(t)
 
@@ -317,7 +321,8 @@ func TestStorageAt(t *testing.T) {
 // Parameters:
 // - t: the testing object for running the test cases
 // Returns:
-//  none
+//
+//	none
 func TestNonce(t *testing.T) {
 	testConfig := beforeEach(t)
 
@@ -369,7 +374,8 @@ func TestNonce(t *testing.T) {
 // Parameters:
 // - t: the testing object for running the test cases
 // Returns:
-//  none
+//
+//	none
 func TestEstimateMessageFee(t *testing.T) {
 	testConfig := beforeEach(t)
 
@@ -384,9 +390,9 @@ func TestEstimateMessageFee(t *testing.T) {
 				MsgFromL1: MsgFromL1{FromAddress: "0x0", ToAddress: &felt.Zero, Selector: &felt.Zero, Payload: []*felt.Felt{&felt.Zero}},
 				BlockID:   BlockID{Tag: "latest"},
 				ExpectedFeeEst: FeeEstimate{
-					GasConsumed: NumAsHex("0x1"),
-					GasPrice:    NumAsHex("0x2"),
-					OverallFee:  NumAsHex("0x3"),
+					GasConsumed: new(felt.Felt).SetUint64(1),
+					GasPrice:    new(felt.Felt).SetUint64(2),
+					OverallFee:  new(felt.Felt).SetUint64(3),
 				},
 			},
 		},
diff --git a/rpc/mock_test.go b/rpc/mock_test.go
index ea749ecd..884e2157 100644
--- a/rpc/mock_test.go
+++ b/rpc/mock_test.go
@@ -582,10 +582,13 @@ func mock_starknet_estimateFee(result interface{}, method string, args ...interf
 		return errWrongArgs
 	}
 
+	gasCons, _ := new(felt.Felt).SetString("0x01a4")
+	gasPrice, _ := new(felt.Felt).SetString("0x45")
+	overallFee, _ := new(felt.Felt).SetString("0x7134")
 	output := FeeEstimate{
-		GasConsumed: NumAsHex("0x01a4"),
-		GasPrice:    NumAsHex("0x45"),
-		OverallFee:  NumAsHex("0x7134"),
+		GasConsumed: gasCons,
+		GasPrice:    gasPrice,
+		OverallFee:  overallFee,
 	}
 	outputContent, _ := json.Marshal(output)
 	json.Unmarshal(outputContent, r)
@@ -621,9 +624,9 @@ func mock_starknet_estimateMessageFee(result interface{}, method string, args ..
 	}
 
 	output := FeeEstimate{
-		GasConsumed: NumAsHex("0x1"),
-		GasPrice:    NumAsHex("0x2"),
-		OverallFee:  NumAsHex("0x3"),
+		GasConsumed: new(felt.Felt).SetUint64(1),
+		GasPrice:    new(felt.Felt).SetUint64(2),
+		OverallFee:  new(felt.Felt).SetUint64(3),
 	}
 	outputContent, _ := json.Marshal(output)
 	json.Unmarshal(outputContent, r)
@@ -789,10 +792,10 @@ func mock_starknet_getStateUpdate(result interface{}, method string, args ...int
 // - args: optional arguments for the method
 // Returns:
 // - error: an error if
-// 		- The result parameter is not of type *json.RawMessage
-// 		- The number of arguments is not equal to 2
-// 		- The first argument is not of type BlockID
-// 		- The second argument is not of type *felt.Felt
+//   - The result parameter is not of type *json.RawMessage
+//   - The number of arguments is not equal to 2
+//   - The first argument is not of type BlockID
+//   - The second argument is not of type *felt.Felt
 func mock_starknet_getNonce(result interface{}, method string, args ...interface{}) error {
 	r, ok := result.(*json.RawMessage)
 	if !ok {
diff --git a/rpc/types.go b/rpc/types.go
index 99474b67..2acf5e1f 100644
--- a/rpc/types.go
+++ b/rpc/types.go
@@ -115,7 +115,9 @@ type SyncStatus struct {
 // occurred during the marshaling process.
 //
 // Parameters:
-//  none
+//
+//	none
+//
 // Returns:
 // - []byte: the JSON encoding of the SyncStatus struct
 // - error: any error that occurred during the marshaling process
@@ -136,7 +138,9 @@ func (s SyncStatus) MarshalJSON() ([]byte, error) {
 // UnmarshalJSON unmarshals the JSON data into the SyncStatus struct.
 //
 // Parameters:
-//  -data: It takes a byte slice as input representing the JSON data to be unmarshaled.
+//
+//	-data: It takes a byte slice as input representing the JSON data to be unmarshaled.
+//
 // Returns:
 // - error: an error if the unmarshaling fails
 func (s *SyncStatus) UnmarshalJSON(data []byte) error {
@@ -185,13 +189,13 @@ type TxDetails struct {
 
 type FeeEstimate struct {
 	// GasConsumed the Ethereum gas cost of the transaction (see https://docs.starknet.io/docs/Fees/fee-mechanism for more info)
-	GasConsumed NumAsHex `json:"gas_consumed"`
+	GasConsumed *felt.Felt `json:"gas_consumed"`
 
 	// GasPrice the gas price (in gwei) that was used in the cost estimation
-	GasPrice NumAsHex `json:"gas_price"`
+	GasPrice *felt.Felt `json:"gas_price"`
 
 	// OverallFee the estimated fee for the transaction (in gwei), product of gas_consumed and gas_price
-	OverallFee NumAsHex `json:"overall_fee"`
+	OverallFee *felt.Felt `json:"overall_fee"`
 }
 
 type TxnExecutionStatus string
@@ -229,7 +233,9 @@ func (ts *TxnExecutionStatus) UnmarshalJSON(data []byte) error {
 // The function returns the marshaled byte slice and a nil error.
 //
 // Parameters:
-//  none
+//
+//	none
+//
 // Returns:
 // - []byte: the JSON encoding of the TxnExecutionStatus
 // - error: the error if there was an issue marshaling
@@ -240,7 +246,9 @@ func (ts TxnExecutionStatus) MarshalJSON() ([]byte, error) {
 // String returns the string representation of the TxnExecutionStatus.
 //
 // Parameters:
-//  none
+//
+//	none
+//
 // Returns:
 // - string: the string representation of the TxnExecutionStatus
 func (s TxnExecutionStatus) String() string {
@@ -279,7 +287,9 @@ func (ts *TxnFinalityStatus) UnmarshalJSON(data []byte) error {
 // MarshalJSON marshals the TxnFinalityStatus into JSON.
 //
 // Parameters:
-//  none
+//
+//	none
+//
 // Returns:
 // - []byte: a byte slice
 // - error: an error if any
@@ -290,7 +300,9 @@ func (ts TxnFinalityStatus) MarshalJSON() ([]byte, error) {
 // String returns the string representation of the TxnFinalityStatus.
 //
 // Parameters:
-//  none
+//
+//	none
+//
 // Returns:
 // - string: the string representation of the TxnFinalityStatus
 func (s TxnFinalityStatus) String() string {
diff --git a/rpc/types_block.go b/rpc/types_block.go
index d2578de9..78a80e04 100644
--- a/rpc/types_block.go
+++ b/rpc/types_block.go
@@ -31,7 +31,9 @@ type BlockID struct {
 // while the error indicates any error that occurred during the marshaling process.
 //
 // Parameters:
-//  none
+//
+//	none
+//
 // Returns:
 // - []byte: the JSON representation of the BlockID
 // - error: any error that occurred during the marshaling process
@@ -99,7 +101,9 @@ func (bs *BlockStatus) UnmarshalJSON(data []byte) error {
 // MarshalJSON returns the JSON encoding of BlockStatus.
 //
 // Parameters:
-//  none
+//
+//	none
+//
 // Returns:
 // - []byte: a byte slice
 // - error: an error if any
@@ -165,7 +169,7 @@ type PendingBlockHeader struct {
 
 type ResourcePrice struct {
 	// The price of one unit of the given resource, denominated in strk
-	PriceInStrk NumAsHex `json:"price_in_strk,omitempty"`
+	PriceInStrk *felt.Felt `json:"price_in_strk,omitempty"`
 	// The price of one unit of the given resource, denominated in wei
-	PriceInWei NumAsHex `json:"price_in_wei"`
+	PriceInWei *felt.Felt `json:"price_in_wei"`
 }
diff --git a/rpc/types_broadcast_transaction.go b/rpc/types_broadcast_transaction.go
index fcfdf50e..69264735 100644
--- a/rpc/types_broadcast_transaction.go
+++ b/rpc/types_broadcast_transaction.go
@@ -9,6 +9,7 @@ var (
 	_ BroadcastTxn = BroadcastInvokev1Txn{}
 	_ BroadcastTxn = BroadcastDeclareV1Txn{}
 	_ BroadcastTxn = BroadcastDeclareV2Txn{}
+	_ BroadcastTxn = BroadcastDeclareTxnV3{}
 	_ BroadcastTxn = BroadcastDeployAccountTxn{}
 )
 
@@ -56,6 +57,26 @@ type BroadcastDeclareV2Txn struct {
 	ContractClass     ContractClass `json:"contract_class"`
 }
 
+type BroadcastDeclareTxnV3 struct {
+	Type              TransactionType       `json:"type"`
+	SenderAddress     *felt.Felt            `json:"sender_address"`
+	CompiledClassHash *felt.Felt            `json:"compiled_class_hash"`
+	Version           NumAsHex              `json:"version"`
+	Signature         []*felt.Felt          `json:"signature"`
+	Nonce             *felt.Felt            `json:"nonce"`
+	ContractClass     *ContractClass        `json:"contract_class"`
+	ResourceBounds    ResourceBoundsMapping `json:"resource_bounds"`
+	Tip               *felt.Felt            `json:"tip"`
+	// The data needed to allow the paymaster to pay for the transaction in native tokens
+	PayMasterData []*felt.Felt `json:"paymaster_data"`
+	// The data needed to deploy the account contract from which this tx will be initiated
+	AccountDeploymentData *felt.Felt `json:"account_deployment_data"`
+	// The storage domain of the account's nonce (an account has a nonce per DA mode)
+	NonceDataMode DataAvailabilityMode `json:"nonce_data_availability_mode"`
+	// The storage domain of the account's balance from which fee will be charged
+	FeeMode DataAvailabilityMode `json:"fee_data_availability_mode"`
+}
+
 type BroadcastDeployAccountTxn struct {
 	DeployAccountTxn
 }
diff --git a/rpc/types_transaction.go b/rpc/types_transaction.go
index 6a1e737e..623be52e 100644
--- a/rpc/types_transaction.go
+++ b/rpc/types_transaction.go
@@ -1,12 +1,14 @@
 package rpc
 
 import (
+	"encoding/binary"
 	"encoding/json"
 	"errors"
 	"fmt"
 	"math/big"
 
 	"github.com/NethermindEth/juno/core/felt"
+	"github.com/NethermindEth/starknet.go/utils"
 )
 
 // https://github.com/starkware-libs/starknet-specs/blob/a789ccc3432c57777beceaa53a34a7ae2f25fda0/api/starknet_api_openrpc.json#L1252
@@ -45,11 +47,29 @@ type InvokeTxnV1 struct {
 	// The data expected by the account's `execute` function (in most usecases, this includes the called contract address and a function selector)
 	Calldata []*felt.Felt `json:"calldata"`
 }
+type InvokeTxnV3 struct {
+	Type           TransactionType       `json:"type"`
+	SenderAddress  *felt.Felt            `json:"sender_address"`
+	Calldata       []*felt.Felt          `json:"calldata"`
+	Version        TransactionVersion    `json:"version"`
+	Signature      []*felt.Felt          `json:"signature"`
+	Nonce          *felt.Felt            `json:"nonce"`
+	ResourceBounds ResourceBoundsMapping `json:"resource_bounds"`
+	Tip            *felt.Felt            `json:"tip"`
+	// The data needed to allow the paymaster to pay for the transaction in native tokens
+	PayMasterData []*felt.Felt `json:"paymaster_data"`
+	// The data needed to deploy the account contract from which this tx will be initiated
+	AccountDeploymentData []*felt.Felt `json:"account_deployment_data"`
+	// The storage domain of the account's nonce (an account has a nonce per DA mode)
+	NonceDataMode DataAvailabilityMode `json:"nonce_data_availability_mode"`
+	// The storage domain of the account's balance from which fee will be charged
+	FeeMode DataAvailabilityMode `json:"fee_data_availability_mode"`
+}
 
 type L1HandlerTxn struct {
 	Type TransactionType `json:"type,omitempty"`
 	// Version of the transaction scheme
-	Version NumAsHex `json:"version"`
+	Version *felt.Felt `json:"version"`
 	// Nonce
 	Nonce string `json:"nonce,omitempty"`
 	FunctionCall
@@ -89,6 +109,77 @@ type DeclareTxnV2 struct {
 	ClassHash         *felt.Felt         `json:"class_hash"`
 }
 
+type DeclareTxnV3 struct {
+	Type              TransactionType       `json:"type"`
+	SenderAddress     *felt.Felt            `json:"sender_address"`
+	CompiledClassHash *felt.Felt            `json:"compiled_class_hash"`
+	Version           TransactionVersion    `json:"version"`
+	Signature         []*felt.Felt          `json:"signature"`
+	Nonce             *felt.Felt            `json:"nonce"`
+	ClassHash         *felt.Felt            `json:"class_hash"`
+	ResourceBounds    ResourceBoundsMapping `json:"resource_bounds"`
+	Tip               *felt.Felt            `json:"tip"`
+	// The data needed to allow the paymaster to pay for the transaction in native tokens
+	PayMasterData []*felt.Felt `json:"paymaster_data"`
+	// The data needed to deploy the account contract from which this tx will be initiated
+	AccountDeploymentData []*felt.Felt `json:"account_deployment_data"`
+	// The storage domain of the account's nonce (an account has a nonce per DA mode)
+	NonceDataMode DataAvailabilityMode `json:"nonce_data_availability_mode"`
+	// The storage domain of the account's balance from which fee will be charged
+	FeeMode DataAvailabilityMode `json:"fee_data_availability_mode"`
+}
+
+type ResourceBoundsMapping struct {
+	// The max amount and max price per unit of L1 gas used in this tx
+	L1Gas ResourceBounds `json:"l1_gas"`
+	// The max amount and max price per unit of L2 gas used in this tx
+	L2Gas ResourceBounds `json:"l2_gas"`
+}
+
+type DataAvailabilityMode string
+
+const (
+	DAModeL1 DataAvailabilityMode = "L1"
+	DAModeL2 DataAvailabilityMode = "L2"
+)
+
+func (da *DataAvailabilityMode) UInt64() (uint64, error) {
+	switch *da {
+	case DAModeL1:
+		return uint64(0), nil
+	case DAModeL2:
+		return uint64(1), nil
+	}
+	return 0, errors.New("Unknown DAMode")
+}
+
+type Resource string
+
+const (
+	ResourceL1Gas Resource = "L1_GAS"
+	ResourceL2Gas Resource = "L2_GAS"
+)
+
+type ResourceBounds struct {
+	// The max amount of the resource that can be used in the tx
+	MaxAmount *felt.Felt `json:"max_amount"`
+	// The max price per unit of this resource for this tx
+	MaxPricePerUnit *felt.Felt `json:"max_price_per_unit"`
+}
+
+func (rb ResourceBounds) Bytes(resource Resource) []byte {
+	const eight = 8
+	maxAmountBytes := make([]byte, eight)
+	binary.BigEndian.PutUint64(maxAmountBytes, rb.MaxAmount.Impl().Uint64())
+	maxPriceBytes := rb.MaxPricePerUnit.Bytes()
+	return utils.Flatten(
+		[]byte{0},
+		[]byte(resource),
+		maxAmountBytes,
+		maxPriceBytes[16:], // uint128.
+	)
+}
+
 // DeployTxn The structure of a deploy transaction. Note that this transaction type is deprecated and will no longer be supported in future versions
 type DeployTxn struct {
 	// ClassHash The hash of the deployed contract's class
@@ -117,6 +208,24 @@ type DeployAccountTxn struct {
 	ConstructorCalldata []*felt.Felt `json:"constructor_calldata"`
 }
 
+type DeployAccountTxnV3 struct {
+	Type                TransactionType       `json:"type"`
+	Version             TransactionVersion    `json:"version"`
+	Signature           []*felt.Felt          `json:"signature"`
+	Nonce               *felt.Felt            `json:"nonce"`
+	ContractAddressSalt *felt.Felt            `json:"contract_address_salt"`
+	ConstructorCalldata []*felt.Felt          `json:"constructor_calldata"`
+	ClassHash           *felt.Felt            `json:"class_hash"`
+	ResourceBounds      ResourceBoundsMapping `json:"resource_bounds"`
+	Tip                 *felt.Felt            `json:"tip"`
+	// The data needed to allow the paymaster to pay for the transaction in native tokens
+	PayMasterData []*felt.Felt `json:"paymaster_data"`
+	// The storage domain of the account's nonce (an account has a nonce per DA mode)
+	NonceDataMode DataAvailabilityMode `json:"nonce_data_availability_mode"`
+	// The storage domain of the account's balance from which fee will be charged
+	FeeMode DataAvailabilityMode `json:"fee_data_availability_mode"`
+}
+
 type UnknownTransaction struct{ Transaction }
 
 // UnmarshalJSON unmarshals the JSON data into an UnknownTransaction object.
@@ -222,15 +331,22 @@ func remarshal(v interface{}, dst interface{}) error {
 type TransactionVersion string
 
 const (
-	TransactionV0 TransactionVersion = "0x0"
-	TransactionV1 TransactionVersion = "0x1"
-	TransactionV2 TransactionVersion = "0x2"
+	TransactionV0             TransactionVersion = "0x0"
+	TransactionV0WithQueryBit TransactionVersion = "0x100000000000000000000000000000000"
+	TransactionV1             TransactionVersion = "0x1"
+	TransactionV1WithQueryBit TransactionVersion = "0x100000000000000000000000000000001"
+	TransactionV2             TransactionVersion = "0x2"
+	TransactionV2WithQueryBit TransactionVersion = "0x100000000000000000000000000000002"
+	TransactionV3             TransactionVersion = "0x3"
+	TransactionV3WithQueryBit TransactionVersion = "0x100000000000000000000000000000003"
 )
 
 // BigInt returns a big integer corresponding to the transaction version.
 //
 // Parameters:
-//  none
+//
+//	none
+//
 // Returns:
 // - *big.Int: a pointer to a big.Int
 // - error: an error if the conversion fails
diff --git a/rpc/types_transaction_interfaces.go b/rpc/types_transaction_interfaces.go
index 15f695e6..1af5a6d9 100644
--- a/rpc/types_transaction_interfaces.go
+++ b/rpc/types_transaction_interfaces.go
@@ -4,6 +4,7 @@ type AddDeclareTxnInput interface{}
 
 var _ AddDeclareTxnInput = DeclareTxnV1{}
 var _ AddDeclareTxnInput = DeclareTxnV2{}
+var _ AddDeclareTxnInput = DeclareTxnV3{}
 
 type Transaction interface {
 	GetType() TransactionType
@@ -11,88 +12,55 @@ type Transaction interface {
 
 var _ Transaction = InvokeTxnV0{}
 var _ Transaction = InvokeTxnV1{}
+var _ Transaction = InvokeTxnV3{}
 var _ Transaction = DeclareTxnV1{}
 var _ Transaction = DeclareTxnV2{}
+var _ Transaction = DeclareTxnV3{}
 var _ Transaction = DeployTxn{}
 var _ Transaction = DeployAccountTxn{}
+var _ Transaction = DeployAccountTxnV3{}
 var _ Transaction = L1HandlerTxn{}
 
-// GetType returns the transaction type of the InvokeTxnV0 struct.
-//
-// Parameters:
-//  none
-// Returns:
-// - TransactionType: the transaction type
 func (tx InvokeTxnV0) GetType() TransactionType {
 	return tx.Type
 }
 
-// GetType returns the type of the InvokeTxnV1 transaction.
-//
-// Parameters:
-//  none
-// Returns:
-// - TransactionType: the transaction type
 func (tx InvokeTxnV1) GetType() TransactionType {
 	return tx.Type
 }
 
-// GetType returns the TransactionType of the DeclareTxnV0 object.
-//
-// Parameters:
-//  none
-// Returns:
-// - TransactionType: the transaction type
+func (tx InvokeTxnV3) GetType() TransactionType {
+	return tx.Type
+}
+
 func (tx DeclareTxnV0) GetType() TransactionType {
 	return tx.Type
 }
 
-// GetType returns the transaction type of DeclareTxnV1.
-//
-// Parameters:
-//  none
-// Returns:
-// - TransactionType: the transaction type
 func (tx DeclareTxnV1) GetType() TransactionType {
 	return tx.Type
 }
 
-// GetType returns the type of the transaction.
-//
-// Parameters:
-//  none
-// Returns:
-// - TransactionType: the transaction type
 func (tx DeclareTxnV2) GetType() TransactionType {
 	return tx.Type
 }
 
-// GetType returns the type of the DeployTxn.
-//
-// Parameters:
-//  none
-// Returns:
-// - TransactionType: the transaction type
+func (tx DeclareTxnV3) GetType() TransactionType {
+	return tx.Type
+}
+
 func (tx DeployTxn) GetType() TransactionType {
 	return tx.Type
 }
 
-// GetType returns the transaction type of the DeployAccountTxn.
-//
-// Parameters:
-//  none
-// Returns:
-// - TransactionType: the transaction type
 func (tx DeployAccountTxn) GetType() TransactionType {
 	return tx.Type
 }
 
-// GetType returns the transaction type of the L1HandlerTxn.
-//
-// Parameters:
-//  none
-// Returns:
-// - TransactionType: the transaction type
+func (tx DeployAccountTxnV3) GetType() TransactionType {
+	return tx.Type
+}
+
 func (tx L1HandlerTxn) GetType() TransactionType {
 	return tx.Type
 }
@@ -104,13 +72,16 @@ type InvokeTxnType interface{}
 
 var _ InvokeTxnType = InvokeTxnV0{}
 var _ InvokeTxnType = InvokeTxnV1{}
+var _ InvokeTxnType = InvokeTxnV3{}
 
 type DeclareTxnType interface{}
 
 var _ DeclareTxnType = DeclareTxnV0{}
 var _ DeclareTxnType = DeclareTxnV1{}
 var _ DeclareTxnType = DeclareTxnV2{}
+var _ DeclareTxnType = DeclareTxnV3{}
 
 type DeployAccountType interface{}
 
 var _ DeployAccountType = DeployAccountTxn{}
+var _ DeployAccountType = DeployAccountTxnV3{}
diff --git a/rpc/types_transaction_receipt.go b/rpc/types_transaction_receipt.go
index b3bf71e5..b0f2ee7a 100644
--- a/rpc/types_transaction_receipt.go
+++ b/rpc/types_transaction_receipt.go
@@ -392,11 +392,12 @@ type ExecutionResources struct {
 	BitwiseApps int `json:"bitwise_builtin_applications,omitempty"`
 	// The number of KECCAK builtin instances
 	KeccakApps int `json:"keccak_builtin_applications,omitempty"`
-	// The  number of accesses to the segment arena
+	// The number of accesses to the segment arena
 	SegmentArenaBuiltin int `json:"segment_arena_builtin,omitempty"`
 }
 
-func (er *ExecutionResources) Validation() bool {
+// Validate checks if the fields are non-zero (to match the starknet-specs)
+func (er *ExecutionResources) Validate() bool {
 	if er.Steps == 0 || er.MemoryHoles == 0 || er.RangeCheckApps == 0 || er.PedersenApps == 0 ||
 		er.PoseidonApps == 0 || er.ECOPApps == 0 || er.ECDSAApps == 0 || er.BitwiseApps == 0 ||
 		er.KeccakApps == 0 || er.SegmentArenaBuiltin == 0 {
diff --git a/utils/slices.go b/utils/slices.go
new file mode 100644
index 00000000..00e7cd6b
--- /dev/null
+++ b/utils/slices.go
@@ -0,0 +1,41 @@
+package utils
+
+import "slices"
+
+func Flatten[T any](sl ...[]T) []T {
+	var result []T
+	for _, slice := range sl {
+		result = append(result, slice...)
+	}
+
+	return result
+}
+
+func Map[T1, T2 any](slice []T1, f func(T1) T2) []T2 {
+	if slice == nil {
+		return nil
+	}
+
+	result := make([]T2, len(slice))
+	for i, e := range slice {
+		result[i] = f(e)
+	}
+
+	return result
+}
+
+func Filter[T any](slice []T, f func(T) bool) []T {
+	var result []T
+	for _, e := range slice {
+		if f(e) {
+			result = append(result, e)
+		}
+	}
+
+	return result
+}
+
+// All returns true if all elements match the given predicate
+func All[T any](slice []T, f func(T) bool) bool {
+	return slices.IndexFunc(slice, func(e T) bool { return !f(e) }) == -1
+}