Calculating fee rate of tx returned by fundrawtransaction I'm passing an unfunded proposed tx and feeRate param to fundrawtransaction which gives me back the hex of the tx with inputs selected and its fee. I'm not passing a conf_target or estimate_mode. I pass the resulting hex to decoderawtransaction to get the size/vsize of the tx.

I'm trying to check the actual fee rate of the tx by calculating fee * 1000 / vsize but it doesn't seem to match the rate that I passed in as a parameter. 

I've tried with a few different values but the actual rate always seems to come out higher than the requested rate by a factor of about 1.59, give or take a little bit.

So am I calculating the observed rate improperly? Is it even possible to have the resulting fee rate exactly match the one provided by the feeRate parameter?

Here's an example of constructing a tx with amount 0.00073749 and feeRate 0.00001000. Proposed tx from createrawtransaction:

{
  "txid": "de380dec00d86611bab1cfa825a7069ffde31d7fb0c5b91e381ef79b94d927e0",
  "hash": "de380dec00d86611bab1cfa825a7069ffde31d7fb0c5b91e381ef79b94d927e0",
  "version": 2,
  "size": 41,
  "vsize": 41,
  "weight": 164,
  "locktime": 0,
  "vin": [
  ],
  "vout": [
    {
      "value": 0.00073749,
      "n": 0,
      "scriptPubKey": {
        "asm": "0 de525f12f17a495b7903c2c5b306ee07ccad309f",
        "hex": "0014de525f12f17a495b7903c2c5b306ee07ccad309f",
        "reqSigs": 1,
        "type": "witness_v0_keyhash",
        "addresses": [
          "bc1qmef97yh30fy4k7grctzmxphwqlx26vyl5lve86"
        ]
      }
    }
  ]
}


And the full tx with inputs returned by fundrawtransaction:

{
  "txid": "f78b268f9386ab47dd5c0d36caf3aaff5fa719c7a2b5658ff08132159cb6b8cd",
  "hash": "f78b268f9386ab47dd5c0d36caf3aaff5fa719c7a2b5658ff08132159cb6b8cd",
  "version": 2,
  "size": 369,
  "vsize": 369,
  "weight": 1476,
  "locktime": 0,
  "vin": [
    {
      "txid": "ca6f25d4484ab525fdb907cbce81d3dc9d1974695fcc80639fddfff4b6bca268",
      "vout": 1,
      "scriptSig": {
        "asm": "",
        "hex": ""
      },
      "sequence": 4294967294
    },
    {
      "txid": "9b39ee5637d7b0783715be6ce566283024ee2bea33a9d3bd6f415ae5d8f24e5f",
      "vout": 0,
      "scriptSig": {
        "asm": "",
        "hex": ""
      },
      "sequence": 4294967294
    },
    {
      "txid": "2f2b7eaccc06ea3856cd5b4666a15161d0fac9053ab2bcbb94a490593c969246",
      "vout": 0,
      "scriptSig": {
        "asm": "",
        "hex": ""
      },
      "sequence": 4294967294
    },
    {
      "txid": "b5b2e4e1287438fd8193e1409cdf87181061ab4a3d95f50d2bf1bc2534b2d49c",
      "vout": 0,
      "scriptSig": {
        "asm": "",
        "hex": ""
      },
      "sequence": 4294967294
    },
    {
      "txid": "3de9c3690c77f544b0d953c70d97f50bb9790a847bed9972caca960218c6c8f6",
      "vout": 0,
      "scriptSig": {
        "asm": "",
        "hex": ""
      },
      "sequence": 4294967294
    },
    {
      "txid": "5e0021645a37473afdff1720da902f0abe183fb48719169e96b5235bb6b09e25",
      "vout": 0,
      "scriptSig": {
        "asm": "",
        "hex": ""
      },
      "sequence": 4294967294
    },
    {
      "txid": "f92dbcf77c13d887d8aa77404dc78120c90d055a32b4d51196849affc3866ade",
      "vout": 0,
      "scriptSig": {
        "asm": "",
        "hex": ""
      },
      "sequence": 4294967294
    },
    {
      "txid": "e425c6e84bc671bfb3a80bd95d1925007e12483fdcf3a19094bde8a36ae0d9fd",
      "vout": 0,
      "scriptSig": {
        "asm": "",
        "hex": ""
      },
      "sequence": 4294967294
    }
  ],
  "vout": [
    {
      "value": 0.00073749,
      "n": 0,
      "scriptPubKey": {
        "asm": "0 de525f12f17a495b7903c2c5b306ee07ccad309f",
        "hex": "0014de525f12f17a495b7903c2c5b306ee07ccad309f",
        "reqSigs": 1,
        "type": "witness_v0_keyhash",
        "addresses": [
          "bc1qmef97yh30fy4k7grctzmxphwqlx26vyl5lve86"
        ]
      }
    }
  ]
}


In this case the fee is 0.00000668 so the rate I calculate is 0.0000668 * 1000 / 369 = 0.00001810 as opposed to the requested 0.00001000
You're seeing two things here: the exact match coin selection algorithm, and the fact that your vsize is computed before adding signatures.
Exact match
The coin selection algorithm in Bitcoin Core tries to find an &quot;exact match&quot; of UTXOs to spend in order to pay, without adding a change output back to you. If the input amount is a bit too large, it is turned into fee. Surprisingly, under certain circumstances this behavior is actually preferable over turning that excess into change.
The observation is that merely creating a change output costs money right now (in terms of fee needed to pay for it), and more money later (in terms of the marginal fee needed to pay for the additional space used by the transaction input that will spend it). That fee is wasteful: if it's possible to not create the change at all, this becomes a cost you never have to pay.
So the conclusion is: if it is possible to construct a transaction without change that overpays less than what it would cost to create and spend change, it is preferable. This is exactly what Bitcoin Core does, using a short-term fee estimation for the creation of the change, and a long-term fee estimation for spending it.
The concept and algorithm for doing this efficiently were first described by Mark Erhard in his master's thesis.
vsize before signatures
You're looking at the vsize of the unsigned raw transaction coming out of fundrawtransaction. For fee decision purposes, fundrawtransaction tries to predict the size of the transaction after it is signed.