Repository: NethermindEth/juno (go)

Issue:
Add remainder feeder.go Feeder_gateway methods
Features to be added to feeder.go

[ ] estimate_fee
[ ] call_contract

Pull request:
Feat/feeder methods
Fixes | Closes | Resolves #225 

## Changes:
- Fix Keccak250 implementation

## Types of changes
- [x] Bugfix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation Update
- [ ] Code style update (formatting, renaming)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] Other (please describe): 

## Testing
**Requires testing**

- [ ] Yes
- [x] No

Patch:
diff --git a/cmd/juno-cli/cli/estimateFee.go b/cmd/juno-cli/cli/estimateFee.go
new file mode 100644
index 00000000..31e5ed40
--- /dev/null
+++ b/cmd/juno-cli/cli/estimateFee.go
@@ -0,0 +1,52 @@
+package cli
+
+import (
+	"fmt"
+
+	"github.com/NethermindEth/juno/pkg/crypto/keccak"
+	"github.com/NethermindEth/juno/pkg/feeder"
+
+	"github.com/spf13/cobra"
+)
+
+// estimateFeeCmd represents the estimateFee command
+var estimateFeeCmd = &cobra.Command{
+	Use:   "estimate_fee CONTRACT_HASH FUNCTION_NAME INPUTS [SIGNATURE] [flags]",
+	Short: "Calculate transaction fee for calling a function.",
+	Long:  `See https://www.cairo-lang.org/docs/hello_starknet/cli.html#estimate-fee`,
+	Args:  cobra.MinimumNArgs(2),
+	Run: func(cmd *cobra.Command, args []string) {
+		selector_hash, _ := getSelectorFromName(args[1])
+		res, _ := estimateFee(args[0], selector_hash, cmd.Flag("calldata").Value.String(), cmd.Flag("signature").Value.String())
+
+		// Pretty print or not
+		if cmd.Flag("pretty").Value.String() == "true" {
+			prettyPrint(res)
+		} else {
+			normalReturn(res)
+		}
+	},
+}
+
+func getSelectorFromName(func_name string) (string, error) {
+	// Function name (string) > ASCII > Keccak250
+	return fmt.Sprintf("0x%x\n", keccak.Digest250([]byte(func_name))), nil
+}
+
+func estimateFee(contractAddress, entryPointSelector, callData, signature string) (*feeder.EstimateFeeResponse, error) {
+	client := initClient()
+
+	// Call to get estimate transaction fee for given contract and params.
+	res, _ := client.EstimateTransactionFee(contractAddress, entryPointSelector, callData, signature)
+	return res, nil
+}
+
+func init() {
+	rootCmd.AddCommand(estimateFeeCmd)
+
+	// Add calldata flag
+	estimateFeeCmd.Flags().StringP("calldata", "i", "0", "Transaction calldata.")
+
+	// Add signature flag
+	estimateFeeCmd.Flags().StringP("signature", "s", "0", "Account signature.")
+}
diff --git a/docs/docs/features/cli.mdx b/docs/docs/features/cli.mdx
index 484d7fd8..721c37f3 100644
--- a/docs/docs/features/cli.mdx
+++ b/docs/docs/features/cli.mdx
@@ -13,6 +13,7 @@ To query available commands, run `juno-cli --help` from the `build` directory.
 Available Commands:
 
     completion                 Generate the autocompletion script for the specified shell
+    estimate_fee               Calculate transaction fee for calling a function.
     get_block                  Prints out block information.
     get_block_hash_by_id       Get corresponding block hash by block number (ID).
     get_block_id_by_hash       Get block number (ID) by block hash.
diff --git a/pkg/crypto/keccak/keccak.go b/pkg/crypto/keccak/keccak.go
index 97a8c18b..e69d43ae 100644
--- a/pkg/crypto/keccak/keccak.go
+++ b/pkg/crypto/keccak/keccak.go
@@ -17,9 +17,9 @@ import (
 //
 // [hash function]: https://docs.starknet.io/docs/Hashing/hash-functions#starknet-keccak
 func Digest250(data []byte) *big.Int {
-	digest := Digest256(data)
-	digest[0] >>= 6 // Clear the last 6 bits.
-	return new(big.Int).SetBytes(digest)
+	digest := new(big.Int).SetBytes(Digest256(data))
+	mask, _ := new(big.Int).SetString("3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)
+	return digest.And(digest, mask)
 }
 
 // Digest256 returns the 256-bit Keccak hash of the data.
diff --git a/pkg/feeder/feeder.go b/pkg/feeder/feeder.go
index 861a5fc3..7a46358c 100644
--- a/pkg/feeder/feeder.go
+++ b/pkg/feeder/feeder.go
@@ -556,7 +556,6 @@ func (c Client) GetTransactionHashByID(txID string) (*string, error) {
 
 // GetTransactionIDByHash creates a new request to get a transaction ID
 // by hash.
-// notest
 func (c Client) GetTransactionIDByHash(txHash string) (*string, error) {
 	req, err := c.newRequest(
 		"GET", "/get_transaction_id_by_hash",
@@ -580,3 +579,36 @@ func (c Client) GetTransactionIDByHash(txHash string) (*string, error) {
 	metr.IncreaseTxIDReceived()
 	return &resStr, err
 }
+
+// EstimateFee makes a POST request to retrieve expected fee from a given transaction
+func (c Client) EstimateTransactionFee(contractAddress, entryPointSelector, callData, signature string) (*EstimateFeeResponse, error) {
+	// Request needs header with formatted block ID. Even if empty
+	blockIdentifier := formattedBlockIdentifier("", "")
+	if blockIdentifier == nil {
+		// notest
+		blockIdentifier = map[string]string{}
+	}
+
+	callDataList := []string{callData}
+	signatureList := []string{signature}
+
+	req, err := c.newRequest(
+		"POST", "/estimate_fee", blockIdentifier,
+		map[string]interface{}{
+			"contract_address":     contractAddress,
+			"entry_point_selector": entryPointSelector,
+			"calldata":             callDataList,
+			"signature":            signatureList,
+		})
+	var res EstimateFeeResponse
+	_, err = c.do(req, &res)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).
+			Error("Error connecting to gateway.")
+	}
+	return &res, err
+}
+
+func list(callData string) {
+	panic("unimplemented")
+}
diff --git a/pkg/feeder/types.go b/pkg/feeder/types.go
index 634e6f64..24f2b7b8 100644
--- a/pkg/feeder/types.go
+++ b/pkg/feeder/types.go
@@ -190,7 +190,7 @@ type CodeInfo struct {
 type TransactionFailureReason struct {
 	TxID     int64  `json:"tx_id,omitempty"`
 	Code     string `json:"code,omitempty"`
-	ErrorMsg string `json:"error_message,omitempty"`
+	ErrorMsg string `json:"message,omitempty"`
 }
 
 // type TxnStatus string
@@ -279,3 +279,13 @@ type StateUpdateResponse struct {
 	OldRoot   string    `json:"old_root"`
 	StateDiff StateDiff `json:"state_diff"`
 }
+
+type Fee struct {
+	Amount int    `json:"amount,omitempty"`
+	Unit   string `json:"unit,omitempty"`
+}
+
+type EstimateFeeResponse struct {
+	TransactionFailureReason
+	Fee
+}


Test patch:
diff --git a/pkg/crypto/keccak/keccak_test.go b/pkg/crypto/keccak/keccak_test.go
index 5d97ab7a..4611666f 100644
--- a/pkg/crypto/keccak/keccak_test.go
+++ b/pkg/crypto/keccak/keccak_test.go
@@ -28,7 +28,7 @@ func ExampleDigest250() {
 	digest := Digest250([]byte("abc"))
 	fmt.Printf("0x%x\n", digest)
 	// Output:
-	// 0x103657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45
+	// 0x203657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45
 }
 
 // ExampleDigest256 showcases the use of the Digest250 function in the
@@ -46,8 +46,8 @@ func TestDigest250(t *testing.T) {
 	tests := [...]struct {
 		input, want string
 	}{
-		{"", "3d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"},
-		{"abc", "103657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45"},
+		{"", "1d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"},
+		{"abc", "203657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45"},
 	}
 	for _, test := range tests {
 		got := fmt.Sprintf("%x", Digest250([]byte(test.input)))
diff --git a/pkg/feeder/feeder_test.go b/pkg/feeder/feeder_test.go
index 03d37247..dceec5d0 100644
--- a/pkg/feeder/feeder_test.go
+++ b/pkg/feeder/feeder_test.go
@@ -388,3 +388,26 @@ func TestGetStorageAt(t *testing.T) {
 	}
 	assert.Equal(t, &cOrig, transactionId, "GetStorageAt response does not match")
 }
+
+func TestEstimateTransactionFee(t *testing.T) {
+	a := feeder.EstimateFeeResponse{}
+	err := faker.FakeData(&a)
+	if err != nil {
+		t.Fatal()
+	}
+	body, err := json.Marshal(a)
+	if err != nil {
+		t.Fatal()
+	}
+	httpClient.DoReturns(generateResponse(string(body)), nil)
+	var cOrig feeder.EstimateFeeResponse
+	err = json.Unmarshal([]byte(body), &cOrig)
+	if err != nil {
+		t.Fatal()
+	}
+	transactionFee, err := client.EstimateTransactionFee("contract", "func_name", "calldata", "signature")
+	if err != nil {
+		t.Fatal()
+	}
+	assert.Equal(t, &cOrig, transactionFee, "GetTransactionTrace response does not match")
+}