diff --git a/calc/Cargo.lock b/calc/Cargo.lock index 99d9678ef..1edd3c51f 100644 --- a/calc/Cargo.lock +++ b/calc/Cargo.lock @@ -8,6 +8,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + [[package]] name = "atty" version = "0.2.14" @@ -99,7 +105,7 @@ dependencies = [ "serde", "serde_derive", "sp-arithmetic 2.0.0-dev", - "sp-arithmetic 3.0.0", + "sp-arithmetic 5.0.0", "wasm-bindgen", ] @@ -271,6 +277,17 @@ dependencies = [ "memchr", ] +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "either" version = "1.6.1" @@ -336,6 +353,17 @@ dependencies = [ "parity-scale-codec 1.3.6", ] +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "integer-sqrt" version = "0.1.5" @@ -433,6 +461,12 @@ dependencies = [ "libc", ] +[[package]] +name = "once_cell" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" + [[package]] name = "oorandom" version = "11.1.3" @@ -445,7 +479,7 @@ version = "1.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79602888a81ace83e3d1d4b2873286c1f5f906c84db667594e8db8da3506c383" dependencies = [ - "arrayvec", + "arrayvec 0.5.2", "bitvec 0.17.4", "byte-slice-cast 0.3.5", "serde", @@ -457,20 +491,44 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75c823fdae1bb5ff5708ee61a62697e6296175dc671710876871c853f48592b3" dependencies = [ - "arrayvec", + "arrayvec 0.5.2", "bitvec 0.20.1", "byte-slice-cast 1.0.0", - "parity-scale-codec-derive", + "parity-scale-codec-derive 2.0.0", "serde", ] +[[package]] +name = "parity-scale-codec" +version = "3.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9182e4a71cae089267ab03e67c99368db7cd877baf50f931e5d6d4b71e195ac0" +dependencies = [ + "arrayvec 0.7.2", + "byte-slice-cast 1.0.0", + "impl-trait-for-tuples", + "parity-scale-codec-derive 3.1.3", +] + [[package]] name = "parity-scale-codec-derive" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9029e65297c7fd6d7013f0579e193ec2b34ae78eabca854c9417504ad8a2d214" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 0.1.5", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9299338969a3d2f491d65f140b00ddec470858402f888af98e8642fb5e8965cd" +dependencies = [ + "proc-macro-crate 1.1.3", "proc-macro2", "quote", "syn", @@ -530,20 +588,30 @@ dependencies = [ "toml", ] +[[package]] +name = "proc-macro-crate" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +dependencies = [ + "thiserror", + "toml", +] + [[package]] name = "proc-macro2" -version = "1.0.24" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.9" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" dependencies = [ "proc-macro2", ] @@ -679,6 +747,30 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scale-info" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c46be926081c9f4dd5dd9b6f1d3e3229f2360bc6502dd8836f84a93b7c75e99a" +dependencies = [ + "cfg-if 1.0.0", + "derive_more", + "parity-scale-codec 3.1.5", + "scale-info-derive", +] + +[[package]] +name = "scale-info-derive" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e334bb10a245e28e5fd755cabcafd96cfcd167c99ae63a46924ca8d8703a3c" +dependencies = [ + "proc-macro-crate 1.1.3", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -759,15 +851,17 @@ dependencies = [ [[package]] name = "sp-arithmetic" -version = "3.0.0" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f1c69966c192d1dee8521f0b29ece2b14db07b9b44d801a94e295234761645" +checksum = "31ef21f82cc10f75ed046b65e2f8048080ee76e59f1b8aed55c7150daebfd35b" dependencies = [ "integer-sqrt", "num-traits", - "parity-scale-codec 2.0.0", - "sp-debug-derive 3.0.0", - "sp-std 3.0.0", + "parity-scale-codec 3.1.5", + "scale-info", + "sp-debug-derive 4.0.0", + "sp-std 4.0.0", + "static_assertions", ] [[package]] @@ -783,9 +877,9 @@ dependencies = [ [[package]] name = "sp-debug-derive" -version = "3.0.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e80275f23b4e7ba8f54dec5f90f016530e7307d2ee9445f617ab986cbe97f31e" +checksum = "d676664972e22a0796176e81e7bec41df461d1edf52090955cdab55f2c956ff2" dependencies = [ "proc-macro2", "quote", @@ -800,9 +894,9 @@ checksum = "2585fb8f5f4fde53c2f9ccebac4517da4dc435373a8fcaf5db7f54b798da66c2" [[package]] name = "sp-std" -version = "3.0.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35391ea974fa5ee869cb094d5b437688fbf3d8127d64d1b9fed5822a1ed39b12" +checksum = "14804d6069ee7a388240b665f17908d98386ffb0b5d39f89a4099fc7a2a4c03f" [[package]] name = "static_assertions" @@ -812,13 +906,13 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "syn" -version = "1.0.60" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] @@ -836,6 +930,26 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "thiserror" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tinytemplate" version = "1.2.0" @@ -868,16 +982,16 @@ dependencies = [ ] [[package]] -name = "unicode-width" -version = "0.1.8" +name = "unicode-ident" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" +checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" [[package]] -name = "unicode-xid" -version = "0.2.1" +name = "unicode-width" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" [[package]] name = "walkdir" @@ -898,9 +1012,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wasm-bindgen" -version = "0.2.70" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be" +checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d" dependencies = [ "cfg-if 1.0.0", "serde", @@ -910,13 +1024,13 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.70" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bc45447f0d4573f3d65720f636bbcc3dd6ce920ed704670118650bcd47764c7" +checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", "syn", @@ -925,9 +1039,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.70" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b8853882eef39593ad4174dd26fc9865a64e84026d223f63bb2c42affcbba2c" +checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -935,9 +1049,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.70" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385" +checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" dependencies = [ "proc-macro2", "quote", @@ -948,9 +1062,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.70" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64" +checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a" [[package]] name = "web-sys" diff --git a/calc/Cargo.toml b/calc/Cargo.toml index 88866ffe6..797667295 100644 --- a/calc/Cargo.toml +++ b/calc/Cargo.toml @@ -19,7 +19,7 @@ crate-type = ["cdylib"] debug = ["console_error_panic_hook", "console_log"] [dependencies] -wasm-bindgen = { version = "0.2", default_features = false, features = ["serde-serialize"] } +wasm-bindgen = { version = "0.2.82", default_features = false, features = ["serde-serialize"] } serde_derive = { version = "1", default_features = false } serde = { version = "1", default_features = false } console_error_panic_hook = { version = "0.1", optional = true } @@ -27,7 +27,7 @@ console_log = { version = "0.2", optional = true } log = "0.4" [dependencies.sp-arithmetic] -version = "3" +version = "5.0.0" default_features = false [dependencies.sp-arithmetic-legacy] diff --git a/calc/pkg/calc.d.ts b/calc/pkg/calc.d.ts index 3c1f37400..c9bc477b5 100644 --- a/calc/pkg/calc.d.ts +++ b/calc/pkg/calc.d.ts @@ -1,26 +1,59 @@ /* tslint:disable */ /* eslint-disable */ /** -*/ -export class CalcFee { - free(): void; -/** -* @param {any} polynomial -* @param {string} multiplier -* @param {string} per_byte_fee -* @param {string} spec_name -* @param {number} spec_version -* @returns {CalcFee | undefined} -*/ - static from_params(polynomial: any, multiplier: string, per_byte_fee: string, spec_name: string, spec_version: number): CalcFee | undefined; -/** -* @param {BigInt} weight -* @param {number} len -* @param {BigInt} extrinsic_base_weight +* Uses the following formula to calculate an extrinsics `partial_fee` (ie the total fee +* minus any tip). +* +* ``` +* partial_fee = base_fee + len_fee + ((adjusted_weight_fee/estimated_weight)*actual_weight) +* ``` +* +* Where: +* - `base_fee` is a fixed base fee to include some transaction in a block. It accounts +* for the work needed to verify the signature and such and is constant for any tx. +* - `len_fee` is a fee paid based on the size (length in bytes) of the transaction. Longer +* transactions require more storage. +* - `adjusted_weight_fee` is a fee that is itself `estimated_weight * targeted_fee_adjustment`. +* `targeted_fee_adjustment` is some adjustment made based on the network load and such, and is +* an opaque internal value we have no access to. +* - `estimated_weight` is the "pre-dispatch" weight of the transaction. It's set based on the cost +* of processing the transaction on reference hardware. +* - `actual_weight` is the weight that is found in the `ExtrinsicSuccess` event for the extrinsic in +* a block (it's just called `weight` in the event), and is often the same as `estimated_weight`, +* but the node has the opportunity to change it to whatever it likes, I think. +* +* The RPC endpoint `payment_queryFeeDetails` returns `base_fee`, `len_fee` and `adjusted_weight_fee`/ +* The RPC endpoint `payment_queryInfo` returns `estimated_weight` (called `weight` in the response), and +* a `partialFee` value, which is our best guess at the inclusion fee for the tx without actually submitting +* it and seeing whether the node changes the weight/decides not to take a fee at all. +* +* To get the correct values for some extrinsic from both endpoints, provide the extrinsic bytes, and the +* block number **one before the block it made it into** (eg if the extrinsic was in block 100, you'd use +* block 99 as an argument). This is very important. +* +* Once you've called these endpoints, access the `ExtrinsicSuccess` event to find the `actual_weight`, but +* also a `paysFee` value which signals whether the extrinsic actually incurred a fee at all or not (a node +* has the opportunity to refund the fee entirely if it likes by setting this). +* +* With all of those values to hand, the equation above calculates the correct Fee. Why? Well, the basic +* way to calculate a pre-dispatch fee is: +* +* ``` +* partial_fee = base_fee + len_fee + adjusted_weight_fee +* ``` +* +* We can do this from just the RPC methods. But then once it's in a block, we need to swap out the weight used +* to calculate that `adjusted_weight_fee` with the actual weight that was used from the `ExtrinsicSuccess` event. +* In the end, the maths is simple and gathering the details needed is the main difficulty. We do this all in +* Rust simply to limit any precision loss. +* @param {string} base_fee +* @param {string} len_fee +* @param {string} adjusted_weight_fee +* @param {string} estimated_weight +* @param {string} actual_weight * @returns {string} */ - calc_fee(weight: BigInt, len: number, extrinsic_base_weight: BigInt): string; -} +export function calc_partial_fee(base_fee: string, len_fee: string, adjusted_weight_fee: string, estimated_weight: string, actual_weight: string): string; /** */ export class CalcPayout { diff --git a/calc/pkg/calc.js b/calc/pkg/calc.js index 3f06ca737..d509f11a8 100644 --- a/calc/pkg/calc.js +++ b/calc/pkg/calc.js @@ -1,24 +1,42 @@ let imports = {}; imports['__wbindgen_placeholder__'] = module.exports; let wasm; -const { TextEncoder, TextDecoder } = require(String.raw`util`); +const { TextDecoder, TextEncoder } = require(`util`); + +let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); + +cachedTextDecoder.decode(); + +let cachedUint8Memory0 = new Uint8Array(); + +function getUint8Memory0() { + if (cachedUint8Memory0.byteLength === 0) { + cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8Memory0; +} + +function getStringFromWasm0(ptr, len) { + return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); +} const heap = new Array(32).fill(undefined); heap.push(undefined, null, true, false); -function getObject(idx) { return heap[idx]; } +let heap_next = heap.length; -let WASM_VECTOR_LEN = 0; +function addHeapObject(obj) { + if (heap_next === heap.length) heap.push(heap.length + 1); + const idx = heap_next; + heap_next = heap[idx]; -let cachegetUint8Memory0 = null; -function getUint8Memory0() { - if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) { - cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer); - } - return cachegetUint8Memory0; + heap[idx] = obj; + return idx; } +let WASM_VECTOR_LEN = 0; + let cachedTextEncoder = new TextEncoder('utf-8'); const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' @@ -72,163 +90,112 @@ function passStringToWasm0(arg, malloc, realloc) { return ptr; } -let cachegetInt32Memory0 = null; -function getInt32Memory0() { - if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) { - cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer); - } - return cachegetInt32Memory0; -} +let cachedInt32Memory0 = new Int32Array(); -function debugString(val) { - // primitive types - const type = typeof val; - if (type == 'number' || type == 'boolean' || val == null) { - return `${val}`; - } - if (type == 'string') { - return `"${val}"`; - } - if (type == 'symbol') { - const description = val.description; - if (description == null) { - return 'Symbol'; - } else { - return `Symbol(${description})`; - } - } - if (type == 'function') { - const name = val.name; - if (typeof name == 'string' && name.length > 0) { - return `Function(${name})`; - } else { - return 'Function'; - } - } - // objects - if (Array.isArray(val)) { - const length = val.length; - let debug = '['; - if (length > 0) { - debug += debugString(val[0]); - } - for(let i = 1; i < length; i++) { - debug += ', ' + debugString(val[i]); - } - debug += ']'; - return debug; - } - // Test for built-in - const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val)); - let className; - if (builtInMatches.length > 1) { - className = builtInMatches[1]; - } else { - // Failed to match the standard '[object ClassName]' - return toString.call(val); - } - if (className == 'Object') { - // we're a user defined class or Object - // JSON.stringify avoids problems with cycles, and is generally much - // easier than looping through ownProperties of `val`. - try { - return 'Object(' + JSON.stringify(val) + ')'; - } catch (_) { - return 'Object'; - } - } - // errors - if (val instanceof Error) { - return `${val.name}: ${val.message}\n${val.stack}`; +function getInt32Memory0() { + if (cachedInt32Memory0.byteLength === 0) { + cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); } - // TODO we could test for more things here, like `Set`s and `Map`s. - return className; + return cachedInt32Memory0; } -let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); - -cachedTextDecoder.decode(); +function getObject(idx) { return heap[idx]; } -function getStringFromWasm0(ptr, len) { - return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); +function dropObject(idx) { + if (idx < 36) return; + heap[idx] = heap_next; + heap_next = idx; } -let stack_pointer = 32; - -function addBorrowedObject(obj) { - if (stack_pointer == 1) throw new Error('out of js stack'); - heap[--stack_pointer] = obj; - return stack_pointer; +function takeObject(idx) { + const ret = getObject(idx); + dropObject(idx); + return ret; } - -const u32CvtShim = new Uint32Array(2); - -const uint64CvtShim = new BigUint64Array(u32CvtShim.buffer); /** +* Uses the following formula to calculate an extrinsics `partial_fee` (ie the total fee +* minus any tip). +* +* ``` +* partial_fee = base_fee + len_fee + ((adjusted_weight_fee/estimated_weight)*actual_weight) +* ``` +* +* Where: +* - `base_fee` is a fixed base fee to include some transaction in a block. It accounts +* for the work needed to verify the signature and such and is constant for any tx. +* - `len_fee` is a fee paid based on the size (length in bytes) of the transaction. Longer +* transactions require more storage. +* - `adjusted_weight_fee` is a fee that is itself `estimated_weight * targeted_fee_adjustment`. +* `targeted_fee_adjustment` is some adjustment made based on the network load and such, and is +* an opaque internal value we have no access to. +* - `estimated_weight` is the "pre-dispatch" weight of the transaction. It's set based on the cost +* of processing the transaction on reference hardware. +* - `actual_weight` is the weight that is found in the `ExtrinsicSuccess` event for the extrinsic in +* a block (it's just called `weight` in the event), and is often the same as `estimated_weight`, +* but the node has the opportunity to change it to whatever it likes, I think. +* +* The RPC endpoint `payment_queryFeeDetails` returns `base_fee`, `len_fee` and `adjusted_weight_fee`/ +* The RPC endpoint `payment_queryInfo` returns `estimated_weight` (called `weight` in the response), and +* a `partialFee` value, which is our best guess at the inclusion fee for the tx without actually submitting +* it and seeing whether the node changes the weight/decides not to take a fee at all. +* +* To get the correct values for some extrinsic from both endpoints, provide the extrinsic bytes, and the +* block number **one before the block it made it into** (eg if the extrinsic was in block 100, you'd use +* block 99 as an argument). This is very important. +* +* Once you've called these endpoints, access the `ExtrinsicSuccess` event to find the `actual_weight`, but +* also a `paysFee` value which signals whether the extrinsic actually incurred a fee at all or not (a node +* has the opportunity to refund the fee entirely if it likes by setting this). +* +* With all of those values to hand, the equation above calculates the correct Fee. Why? Well, the basic +* way to calculate a pre-dispatch fee is: +* +* ``` +* partial_fee = base_fee + len_fee + adjusted_weight_fee +* ``` +* +* We can do this from just the RPC methods. But then once it's in a block, we need to swap out the weight used +* to calculate that `adjusted_weight_fee` with the actual weight that was used from the `ExtrinsicSuccess` event. +* In the end, the maths is simple and gathering the details needed is the main difficulty. We do this all in +* Rust simply to limit any precision loss. +* @param {string} base_fee +* @param {string} len_fee +* @param {string} adjusted_weight_fee +* @param {string} estimated_weight +* @param {string} actual_weight +* @returns {string} */ -class CalcFee { - - static __wrap(ptr) { - const obj = Object.create(CalcFee.prototype); - obj.ptr = ptr; - - return obj; - } - - free() { - const ptr = this.ptr; - this.ptr = 0; - - wasm.__wbg_calcfee_free(ptr); - } - /** - * @param {any} polynomial - * @param {string} multiplier - * @param {string} per_byte_fee - * @param {string} spec_name - * @param {number} spec_version - * @returns {CalcFee | undefined} - */ - static from_params(polynomial, multiplier, per_byte_fee, spec_name, spec_version) { - try { - var ptr0 = passStringToWasm0(multiplier, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - var len0 = WASM_VECTOR_LEN; - var ptr1 = passStringToWasm0(per_byte_fee, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - var len1 = WASM_VECTOR_LEN; - var ptr2 = passStringToWasm0(spec_name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - var len2 = WASM_VECTOR_LEN; - var ret = wasm.calcfee_from_params(addBorrowedObject(polynomial), ptr0, len0, ptr1, len1, ptr2, len2, spec_version); - return ret === 0 ? undefined : CalcFee.__wrap(ret); - } finally { - heap[stack_pointer++] = undefined; - } - } - /** - * @param {BigInt} weight - * @param {number} len - * @param {BigInt} extrinsic_base_weight - * @returns {string} - */ - calc_fee(weight, len, extrinsic_base_weight) { - try { - const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - uint64CvtShim[0] = weight; - const low0 = u32CvtShim[0]; - const high0 = u32CvtShim[1]; - uint64CvtShim[0] = extrinsic_base_weight; - const low1 = u32CvtShim[0]; - const high1 = u32CvtShim[1]; - wasm.calcfee_calc_fee(retptr, this.ptr, low0, high0, len, low1, high1); - var r0 = getInt32Memory0()[retptr / 4 + 0]; - var r1 = getInt32Memory0()[retptr / 4 + 1]; - return getStringFromWasm0(r0, r1); - } finally { - wasm.__wbindgen_add_to_stack_pointer(16); - wasm.__wbindgen_free(r0, r1); +module.exports.calc_partial_fee = function(base_fee, len_fee, adjusted_weight_fee, estimated_weight, actual_weight) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0(base_fee, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(len_fee, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + const ptr2 = passStringToWasm0(adjusted_weight_fee, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len2 = WASM_VECTOR_LEN; + const ptr3 = passStringToWasm0(estimated_weight, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len3 = WASM_VECTOR_LEN; + const ptr4 = passStringToWasm0(actual_weight, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len4 = WASM_VECTOR_LEN; + wasm.calc_partial_fee(retptr, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, ptr4, len4); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + var r3 = getInt32Memory0()[retptr / 4 + 3]; + var ptr5 = r0; + var len5 = r1; + if (r3) { + ptr5 = 0; len5 = 0; + throw takeObject(r2); } + return getStringFromWasm0(ptr5, len5); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(ptr5, len5); } -} -module.exports.CalcFee = CalcFee; +}; + /** */ class CalcPayout { @@ -240,10 +207,15 @@ class CalcPayout { return obj; } - free() { + __destroy_into_raw() { const ptr = this.ptr; this.ptr = 0; + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); wasm.__wbg_calcpayout_free(ptr); } /** @@ -252,9 +224,9 @@ class CalcPayout { * @returns {CalcPayout} */ static from_params(total_reward_points, era_payout) { - var ptr0 = passStringToWasm0(era_payout, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - var len0 = WASM_VECTOR_LEN; - var ret = wasm.calcpayout_from_params(total_reward_points, ptr0, len0); + const ptr0 = passStringToWasm0(era_payout, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.calcpayout_from_params(total_reward_points, ptr0, len0); return CalcPayout.__wrap(ret); } /** @@ -268,10 +240,10 @@ class CalcPayout { calc_payout(validator_reward_points, validator_commission, nominator_exposure, total_exposure, is_validator) { try { const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - var ptr0 = passStringToWasm0(nominator_exposure, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - var len0 = WASM_VECTOR_LEN; - var ptr1 = passStringToWasm0(total_exposure, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - var len1 = WASM_VECTOR_LEN; + const ptr0 = passStringToWasm0(nominator_exposure, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(total_exposure, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; wasm.calcpayout_calc_payout(retptr, this.ptr, validator_reward_points, validator_commission, ptr0, len0, ptr1, len1, is_validator); var r0 = getInt32Memory0()[retptr / 4 + 0]; var r1 = getInt32Memory0()[retptr / 4 + 1]; @@ -284,21 +256,9 @@ class CalcPayout { } module.exports.CalcPayout = CalcPayout; -module.exports.__wbindgen_json_serialize = function(arg0, arg1) { - const obj = getObject(arg1); - var ret = JSON.stringify(obj === undefined ? null : obj); - var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - var len0 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len0; - getInt32Memory0()[arg0 / 4 + 0] = ptr0; -}; - -module.exports.__wbindgen_debug_string = function(arg0, arg1) { - var ret = debugString(getObject(arg1)); - var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - var len0 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len0; - getInt32Memory0()[arg0 / 4 + 0] = ptr0; +module.exports.__wbindgen_error_new = function(arg0, arg1) { + const ret = new Error(getStringFromWasm0(arg0, arg1)); + return addHeapObject(ret); }; module.exports.__wbindgen_throw = function(arg0, arg1) { diff --git a/calc/pkg/calc_bg.wasm b/calc/pkg/calc_bg.wasm index dc46491cc..a6539f8ed 100644 Binary files a/calc/pkg/calc_bg.wasm and b/calc/pkg/calc_bg.wasm differ diff --git a/calc/src/calc_fee.rs b/calc/src/calc_fee.rs deleted file mode 100644 index b5e5b1c7b..000000000 --- a/calc/src/calc_fee.rs +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright (C) 2022 Parity Technologies (UK) Ltd. (admin@parity.io) -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use crate::debug; -use core::str::FromStr; -use log::info; -use serde_derive::Deserialize; -use sp_arithmetic::{FixedI128, FixedPointNumber, FixedU128, Perbill}; -use sp_arithmetic_legacy::Fixed128 as Fixed128Legacy; -use wasm_bindgen::prelude::*; - -type Balance = u128; -type Weight = u64; - -#[derive(Deserialize)] -#[allow(non_snake_case)] -pub struct JSCoefficient { - coeffInteger: String, - coeffFrac: u32, - negative: bool, - degree: u8, -} - -#[derive(Debug)] -struct Coefficient { - coeff_integer: Balance, - coeff_frac: Perbill, - negative: bool, - degree: u8, -} - -#[derive(Debug, PartialEq)] -enum Multiplier { - V0(Fixed128Legacy), - V1((FixedI128, bool)), - V2(FixedU128), -} - -impl Multiplier { - fn new(inner: &str, spec_name: &str, spec_version: u32) -> Option { - use Multiplier::{V0, V1, V2}; - let mult = match (spec_name, spec_version) { - ("polkadot", 0) => V1((new_i128(inner), true)), - ("polkadot", v) if v < 11 => V1((new_i128(inner), false)), - ("polkadot", v) if 11 <= v => V2(new_u128(inner)), - - ("kusama", 1062) => V0(new_legacy_128(inner)), - ("kusama", v) if 1062 < v && v < 2011 => V1((new_i128(inner), false)), - ("kusama", v) if 2011 <= v => V2(new_u128(inner)), - - ("westend", 10) => V0(new_legacy_128(inner)), - ("westend", v) if 10 < v && v < 31 => V1((new_i128(inner), false)), - ("westend", v) if 31 <= v => V2(new_u128(inner)), - - ("shiden", _v) => V2(new_u128(inner)), - ("astar", _v) => V2(new_u128(inner)), - - ("statemine", _v) => V2(new_u128(inner)), - ("statemint", _v) => V2(new_u128(inner)), - - ("westmine", _v) => V2(new_u128(inner)), - ("westmint", _v) => V2(new_u128(inner)), - - ("dock-main-runtime", _v) => V2(new_u128(inner)), - ("dock-pos-main-runtime", _v) => V2(new_u128(inner)), - ("dock-pos-test-runtime", _v) => V2(new_u128(inner)), - - ("calamari", _v) => V2(new_u128(inner)), - ("manta", _v) => V2(new_u128(inner)), - - ("karura", _v) => V2(new_u128(inner)), - ("acala", _v) => V2(new_u128(inner)), - ("crust", _v) => V2(new_u128(inner)), - - ("bifrost", _v) => V2(new_u128(inner)), - - _ => { - info!("Unsupported runtime: {}#{}", spec_name, spec_version); - return None; - } - }; - Some(mult) - } - - fn calc(&self, balance: Balance) -> Balance { - match self { - Self::V0(mult) => mult.saturated_multiply_accumulate(balance), - Self::V1((mult, negative_bug)) => { - if *negative_bug && mult.is_negative() { - // replicate the fixed128 bug where negative coefficients are not considered - balance - } else { - mult.saturating_mul_acc_int(balance) - } - } - // V2 changed the range to [0, inf]: we no longer accumulate (only multiply) - Self::V2(mult) => mult.saturating_mul_int(balance), - } - } -} - -#[wasm_bindgen] -#[derive(Debug)] -pub struct CalcFee { - polynomial: Vec, - multiplier: Multiplier, - per_byte_fee: Balance, - adjust_len_fee: bool, -} - -#[wasm_bindgen] -impl CalcFee { - pub fn from_params( - polynomial: &JsValue, - multiplier: &str, - per_byte_fee: &str, - spec_name: &str, - spec_version: u32, - ) -> Option { - debug::setup(); - info!( - "CalcFee::from_params({:#?}, {}, {}, {}, {})", - polynomial, multiplier, per_byte_fee, spec_name, spec_version - ); - - let polynomial: Vec = { - let poly: Option> = polynomial.into_serde().unwrap(); - poly.map(|vec| { - vec.iter() - .map(|c| Coefficient { - coeff_integer: Balance::from_str(&c.coeffInteger).unwrap(), - coeff_frac: Perbill::from_parts(c.coeffFrac), - negative: c.negative, - degree: c.degree, - }) - .collect() - }) - .unwrap_or_else(|| { - vec![Coefficient { - coeff_integer: 8, - coeff_frac: Perbill::from_parts(0), - negative: false, - degree: 1, - }] - }) - }; - let multiplier = Multiplier::new(multiplier, spec_name, spec_version)?; - let per_byte_fee = Balance::from_str(per_byte_fee).unwrap(); - let adjust_len_fee = if let Multiplier::V2(_) = &multiplier { - false - } else { - true - }; - let calc = Self { - polynomial, - multiplier, - per_byte_fee, - adjust_len_fee, - }; - info!( - "CalcFee::withParams({}, {}) -> {:#?}", - spec_name, spec_version, calc - ); - Some(calc) - } - - pub fn calc_fee(&self, weight: Weight, len: u32, extrinsic_base_weight: Weight) -> String { - let unadjusted_len_fee = self.per_byte_fee.saturating_mul(len.into()); - let unadjusted_weight_fee = weight_to_fee(&weight, &self.polynomial); - let base_fee = weight_to_fee(&extrinsic_base_weight, &self.polynomial); - - let (len_fee, adjustable_fee) = if self.adjust_len_fee { - (0, unadjusted_len_fee.saturating_add(unadjusted_weight_fee)) - } else { - (unadjusted_len_fee, unadjusted_weight_fee) - }; - let adjusted_fee = self.multiplier.calc(adjustable_fee); - - let result = base_fee - .saturating_add(len_fee) - .saturating_add(adjusted_fee); - - info!( - "calc_fee: ({}, {}) -> len_fee: {} weight_fee: {} adjustable_fee: {} \ - adjusted_fee: {} base_fee: {} result: {}", - weight, - len, - unadjusted_len_fee, - unadjusted_weight_fee, - adjustable_fee, - adjusted_fee, - base_fee, - result - ); - - result.to_string() - } -} - -fn weight_to_fee(weight: &Weight, polynomial: &[Coefficient]) -> Balance { - polynomial.iter().fold(0, |mut acc: Balance, args| { - let weight: Balance = weight.saturating_pow(args.degree.into()).into(); - - let frac = args.coeff_frac * weight; - let integer = args.coeff_integer.saturating_mul(weight); - - if args.negative { - acc = acc.saturating_sub(frac); - acc = acc.saturating_sub(integer); - } else { - acc = acc.saturating_add(frac); - acc = acc.saturating_add(integer); - } - - acc - }) -} - -fn new_i128(inner: &str) -> FixedI128 { - FixedI128::from_inner(i128::from_str(inner).unwrap()) -} - -fn new_u128(inner: &str) -> FixedU128 { - FixedU128::from_inner(u128::from_str(inner).unwrap()) -} - -fn new_legacy_128(inner: &str) -> Fixed128Legacy { - Fixed128Legacy::from_parts(i128::from_str(inner).unwrap()) -} - -mod test_fees { - use super::Multiplier; - use super::Multiplier::{V2, V1, V0}; - use super::new_u128; - use super::new_i128; - use super::new_legacy_128; - - #[test] - fn multiplier_from_spec_name() { - let inner = "500000000"; - - // Polkadot Multipliers - assert_eq!(Multiplier::new(inner, "polkadot", 11).unwrap(), V2(new_u128(inner))); - assert_eq!(Multiplier::new(inner, "polkadot", 10).unwrap(), V1((new_i128(inner), false))); - assert_eq!(Multiplier::new(inner, "polkadot", 0).unwrap(), V1((new_i128(inner), true))); - - // Kusama Multipliers - assert_eq!(Multiplier::new(inner, "kusama", 2020).unwrap(), V2(new_u128(inner))); - assert_eq!(Multiplier::new(inner, "kusama", 2000).unwrap(), V1((new_i128(inner), false))); - assert_eq!(Multiplier::new(inner, "kusama", 1062).unwrap(), V0(new_legacy_128(inner))); - - // Westend Multipliers - assert_eq!(Multiplier::new(inner, "westend", 31).unwrap(), V2(new_u128(inner))); - assert_eq!(Multiplier::new(inner, "westend", 11).unwrap(), V1((new_i128(inner), false))); - assert_eq!(Multiplier::new(inner, "westend", 10).unwrap(), V0(new_legacy_128(inner))); - - // Statemine Multipliers - assert_eq!(Multiplier::new(inner, "statemine", 1).unwrap(), V2(new_u128(inner))); - - // Statemint Multipliers - assert_eq!(Multiplier::new(inner, "statemint", 1).unwrap(), V2(new_u128(inner))); - } - - #[test] - fn multiplier_calc() { - let inner = "500000000"; - - // Test against V2 calc - assert_eq!(Multiplier::calc(&V2(new_u128(inner)), 1000000000000), 500); - - // Test against V1 calc - assert_eq!(Multiplier::calc(&V1((new_i128(inner), false)), 1000000000), 1000000000); - assert_eq!(Multiplier::calc(&V1((new_i128(inner), true)), 1000000000), 1000000000); - - // Test against V0 calc - assert_eq!(Multiplier::calc(&V0(new_legacy_128(inner)), 1000000000), 1000000000); - } -} diff --git a/calc/src/calc_partial_fee.rs b/calc/src/calc_partial_fee.rs new file mode 100644 index 000000000..cd5a0e420 --- /dev/null +++ b/calc/src/calc_partial_fee.rs @@ -0,0 +1,246 @@ +// Copyright (C) 2022 Parity Technologies (UK) Ltd. (admin@parity.io) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use sp_arithmetic::Perbill; +use wasm_bindgen::prelude::*; + +/// Uses the following formula to calculate an extrinsics `partial_fee` (ie the total fee +/// minus any tip). +/// +/// ``` +/// partial_fee = base_fee + len_fee + ((adjusted_weight_fee/estimated_weight)*actual_weight) +/// ``` +/// +/// Where: +/// - `base_fee` is a fixed base fee to include some transaction in a block. It accounts +/// for the work needed to verify the signature and such and is constant for any tx. +/// - `len_fee` is a fee paid based on the size (length in bytes) of the transaction. Longer +/// transactions require more storage. +/// - `adjusted_weight_fee` is a fee that is itself `estimated_weight * targeted_fee_adjustment`. +/// `targeted_fee_adjustment` is some adjustment made based on the network load and such, and is +/// an opaque internal value we have no access to. +/// - `estimated_weight` is the "pre-dispatch" weight of the transaction. It's set based on the cost +/// of processing the transaction on reference hardware. +/// - `actual_weight` is the weight that is found in the `ExtrinsicSuccess` event for the extrinsic in +/// a block (it's just called `weight` in the event), and is often the same as `estimated_weight`, +/// but the node has the opportunity to change it to whatever it likes, I think. +/// +/// The RPC endpoint `payment_queryFeeDetails` returns `base_fee`, `len_fee` and `adjusted_weight_fee`/ +/// The RPC endpoint `payment_queryInfo` returns `estimated_weight` (called `weight` in the response), and +/// a `partialFee` value, which is our best guess at the inclusion fee for the tx without actually submitting +/// it and seeing whether the node changes the weight/decides not to take a fee at all. +/// +/// To get the correct values for some extrinsic from both endpoints, provide the extrinsic bytes, and the +/// block number **one before the block it made it into** (eg if the extrinsic was in block 100, you'd use +/// block 99 as an argument). This is very important. +/// +/// Once you've called these endpoints, access the `ExtrinsicSuccess` event to find the `actual_weight`, but +/// also a `paysFee` value which signals whether the extrinsic actually incurred a fee at all or not (a node +/// has the opportunity to refund the fee entirely if it likes by setting this). +/// +/// With all of those values to hand, the equation above calculates the correct Fee. Why? Well, the basic +/// way to calculate a pre-dispatch fee is: +/// +/// ``` +/// partial_fee = base_fee + len_fee + adjusted_weight_fee +/// ``` +/// +/// We can do this from just the RPC methods. But then once it's in a block, we need to swap out the weight used +/// to calculate that `adjusted_weight_fee` with the actual weight that was used from the `ExtrinsicSuccess` event. +/// In the end, the maths is simple and gathering the details needed is the main difficulty. We do this all in +/// Rust simply to limit any precision loss. +#[wasm_bindgen] +pub fn calc_partial_fee( + base_fee: &str, + len_fee: &str, + adjusted_weight_fee: &str, + estimated_weight: &str, + actual_weight: &str +) -> Result { + let base_fee: u128 = base_fee.parse()?; + let len_fee: u128 = len_fee.parse()?; + let adjusted_weight_fee: u128 = adjusted_weight_fee.parse()?; + let estimated_weight: u128 = estimated_weight.parse()?; + let actual_weight: u128 = actual_weight.parse()?; + + let partial_fee = calc_raw( + base_fee, + len_fee, + adjusted_weight_fee, + estimated_weight, + actual_weight + ); + + Ok(partial_fee.to_string()) +} + +fn calc_raw( + base_fee: u128, + len_fee: u128, + adjusted_weight_fee: u128, + estimated_weight: u128, + actual_weight: u128 +) -> u128 { + // calculate new adjusted_weight_fee, trying to maintain precision: + let adjusted_weight_fee = Perbill::from_rational(estimated_weight, actual_weight) * adjusted_weight_fee; + + // add the fee components together to get the partial/inclusion fee: + base_fee.saturating_add(len_fee).saturating_add(adjusted_weight_fee) +} + +#[cfg(test)] +mod test_fees { + use super::*; + + fn unwrap(val: Result) -> T { + match val { + Ok(v) => v, + Err(_e) => panic!("Cannot unwrap result") + } + } + + // Fee calculation example 1: + // + // Extrinsic Hex: + // 0x5502840032663236e3cb206fcd0751b43da451089990581becf91cc7bcc48bf5f5a47aaf005baf8efe06ccb5bdd78a1fee70025f4526e6b06419d6acc4555c10d7c164a85bcf61696bdde25a019b2db6e8014ae8d4a12df7d8464f5f512a87d0da01ec10054934a116001f030066470863fba68183688aeeb4f6ed27cbf1085daee54c16cf9caaa7ff0939b27a1700a89123deaa2a8118 + // + // Network WS URL: wss://rpc.shiden.astar.network + // Block number: 1820490 + // Block hash: 0x7b8d40f067cd67191904d43e40225522e491061c170547cf227d791a49e0db62 + // Actual fee: 1611916814889018 + #[test] + fn shiden_block_1820490_tx() { + // NOTE: Whatever block number the tx comes from, we use $blockNumber-1 to get the values. + // It turns out that this is where the values we need should come from. + + // From payment.queryFeeDetails (18 decimal places to 1 SDN): + // baseFee: 100.0000 µSDN + // lenFee: 1.5100 mSDN + // adjustedWeightFee: 1.9168 µSDN + let base_fee: u128 = 100000000000000; + let len_fee: u128 = 1510000000000000; + let adjusted_weight_fee: u128 = 1916814889018; + + // From payment.queryInfo: + // weight: 152822000 + // partialFee: 1611916814889018 + let estimated_weight: u128 = 152822000; + + // From ExtrinsicSuccess event: + let actual_weight: u128 = 152822000; + + // From https://shiden.subscan.io/extrinsic/1820490-2 + // Also seen in Balances.Withdraw event associated with the tx, + // so we know this was the total fee amount taken. Also is the + // actual partialFee, so we are really just testing that our calc + // call returns this, too. + let expected_partial_fee = "1611916814889018"; + + let actual_partial_fee = calc_partial_fee( + &base_fee.to_string(), + &len_fee.to_string(), + &adjusted_weight_fee.to_string(), + &estimated_weight.to_string(), + &actual_weight.to_string() + ); + + assert_eq!(expected_partial_fee, unwrap(actual_partial_fee)); + } + + // Fee calculation example 2: + // + // Extrinsic Hex: + // 0x5502840032663236e3cb206fcd0751b43da451089990581becf91cc7bcc48bf5f5a47aaf00c9e0ce04135a6c150301c53de2f98f9f45cf62025be3e5f28de0769798f855c8bb5cb92aa9cdf4811675122df4e32b21938a3e72be790e1a4fda1c99933c740e192b9d16001f030042d3bfbf83ea4576b85b63b83a58a60ebc5c89e8820ebb92f21926c59be46c1a170040ed0989910d320f + // + // Network WS URL: wss://rpc.shiden.astar.network + // Block number: 1820341 + // Block hash: 0x9f81ab761c40a03ef14e46197a4c00e44c5ee1d225eb1eee37d4ba6a730dc628 + // Actual fee: 1611917528885264 + #[test] + fn shiden_block_1820341_tx() { + // NOTE: Whatever block number the tx comes from, we use $blockNumber-1 to get the values. + // It turns out that this is where the values we need should come from. + + // From payment.queryFeeDetails: + let base_fee: u128 = 100000000000000; + let len_fee: u128 = 1510000000000000; + let adjusted_weight_fee: u128 = 1917528885264; + + // From payment.queryInfo: + // weight: 152822000 + // partialFee: 1611917528885264 + let estimated_weight: u128 = 152822000; + + // From ExtrinsicSuccess event: + let actual_weight: u128 = 152822000; + + // From https://shiden.subscan.io/extrinsic/1820341-2 + // Also seen in Balances.Withdraw event associated with the tx, + // so we know this was the total fee amount taken. + let expected_partial_fee = "1611917528885264"; + + let actual_partial_fee = calc_partial_fee( + &base_fee.to_string(), + &len_fee.to_string(), + &adjusted_weight_fee.to_string(), + &estimated_weight.to_string(), + &actual_weight.to_string() + ); + + assert_eq!(expected_partial_fee, unwrap(actual_partial_fee)); + } + + // Fee Calculation Example 3: + // + // Extrinsic Hex: + // 0x4d028400a9a38c06cfb948f7176027985ec9632a690a1f9e8a64f244f749c117f45aaec50053c5dc9b60c3b73ee49b08d35b79755556280520b2121ad795a0130ff1899d2ccdcc8bffc5ea606cb0e8c05138d336945b1c2d7be22c033b239d4c1dee802700c92ded56000a0300daa641169afddb7e3480071c91348155e9d5543f6dcfd8583667183103cbde0f0f003cc373933e01 + // + // Network WS URL: wss://acala-rpc-0.aca-api.network + // Block Number: 1285857 + // Block Hash: 0x0a7ce4030de0d3d9629ca67381f96ca2936f57fa7a73440bc4a55fe2603e9dc1 + // Actual Fee: 2490128143 + #[test] + fn acala_block_1285857_tx() { + // NOTE: Whatever block number the tx comes from, we use $blockNumber-1 to get the values. + // It turns out that this is where the values we need should come from. + + // From payment.queryFeeDetails: + let adjusted_weight_fee: u128 = 128143; + let base_fee: u128 = 1000000000; + let len_fee: u128 = 1490000000; + + // From payment.queryInfo: + // weight: 152822000 + // partialFee: 2490128142 + let estimated_weight: u128 = 152822000; + + // From ExtrinsicSuccess event: + let actual_weight: u128 = 152822000; + + // From https://acala.subscan.io/extrinsic/1285857-2 + // Also seen in Balances.Withdraw event associated with the tx, + // so we know this was the total fee amount taken. + let expected_partial_fee = "2490128143"; + + let actual_partial_fee = calc_partial_fee( + &base_fee.to_string(), + &len_fee.to_string(), + &adjusted_weight_fee.to_string(), + &estimated_weight.to_string(), + &actual_weight.to_string() + ); + + assert_eq!(expected_partial_fee, unwrap(actual_partial_fee)); + } +} diff --git a/calc/src/calc_payout.rs b/calc/src/calc_payout.rs index 36e0db1e9..002f1836c 100644 --- a/calc/src/calc_payout.rs +++ b/calc/src/calc_payout.rs @@ -67,7 +67,7 @@ impl CalcPayout { // This is the fraction of the total reward that the validator and the // nominators will get. let validator_total_reward_part = - Perbill::from_rational_approximation(validator_reward_points, self.total_reward_points); + Perbill::from_rational(validator_reward_points, self.total_reward_points); // This is how much validator + nominators are entitled to. let validator_total_payout = validator_total_reward_part * self.era_payout; @@ -80,7 +80,7 @@ impl CalcPayout { let own_exposure = Balance::from_str(nominator_exposure).unwrap(); let total_exposure = Balance::from_str(total_exposure).unwrap(); // This is the fraction of the validators leftover payout that the staker is entitled to - let own_exposure_part = Perbill::from_rational_approximation(own_exposure, total_exposure); + let own_exposure_part = Perbill::from_rational(own_exposure, total_exposure); // Now let's calculate how this is split to the account let own_staking_payout = if is_validator { diff --git a/calc/src/lib.rs b/calc/src/lib.rs index e5e84f6ba..187c1954f 100644 --- a/calc/src/lib.rs +++ b/calc/src/lib.rs @@ -12,6 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -pub mod calc_fee; +pub mod calc_partial_fee; pub mod calc_payout; mod debug;