@le-space/rates 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/package.json +4 -3
- package/src/aleph-credits.js +61 -0
- package/src/index.js +8 -1
package/README.md
CHANGED
|
@@ -4,6 +4,7 @@ The euro rate of a day, one definition for Le Space's tools – [Belege](https:/
|
|
|
4
4
|
|
|
5
5
|
- **a crypto asset: its price at 00:00 UTC of the day** – CoinGecko's daily snapshot; failing that, Kraken's daily candle, its open; before Kraken's candles, Kraken's first trade at or after midnight. With `prefer: 'kraken'` the order turns.
|
|
6
6
|
- **a currency:** the ECB reference rate of the day or the last before it, inverted; RUB after 2022-03-01 from the Bank of Russia, KZT from the National Bank of Kazakhstan, other currencies the Bank of Russia lists as a cross rate through the ruble (marked `cbr-cross`).
|
|
7
|
+
- **Aleph Cloud credits:** priced in USD by Aleph, so no rate of the day: the per-credit price of the last purchase on or before the day, else Aleph's list price (1 USD per 1,000,000 credits), at that day's ECB rate (`aleph-credits`: `alephCreditPrice`, `alephCreditsInEur`).
|
|
7
8
|
- **a token nobody prices:** its deepest Uniswap pool (V2, V3, V4) at the booking's block, against ETH or USDC, through an Alchemy key (`dex-rate`).
|
|
8
9
|
|
|
9
10
|
Every answer says its source and moment (`{ rate, usdRate, source, at }`), so a document can name them. Rates are decimal strings, never floats.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@le-space/rates",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "The euro rate of a day for Le Space's tools: the price at 00:00 UTC (CoinGecko, Kraken), the ECB reference rate (Bank of Russia, National Bank of Kazakhstan where it has none), a token's DEX pool at a block. Every answer names its source. Browser and Node; fetch and keys handed in.",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"exports": {
|
|
15
15
|
".": "./src/index.js",
|
|
16
16
|
"./rates": "./src/rates.js",
|
|
17
|
-
"./dex-rate": "./src/dex-rate.js"
|
|
17
|
+
"./dex-rate": "./src/dex-rate.js",
|
|
18
|
+
"./aleph-credits": "./src/aleph-credits.js"
|
|
18
19
|
},
|
|
19
20
|
"files": [
|
|
20
21
|
"src"
|
|
@@ -28,7 +29,7 @@
|
|
|
28
29
|
},
|
|
29
30
|
"dependencies": {
|
|
30
31
|
"@noble/hashes": "2.4.0",
|
|
31
|
-
"@le-space/ledger": "^0.
|
|
32
|
+
"@le-space/ledger": "^0.2.0"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
35
|
"prettier": "3.6.2",
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// The euro value of Aleph Cloud credits (Le-Space/belege#113). Credits are
|
|
3
|
+
// no market asset with a rate of the day: Aleph sells them for USD. So a
|
|
4
|
+
// day's consumption is valued at the per-credit price of the last purchase
|
|
5
|
+
// on or before that day, or at Aleph's list price of 1 USD per 1,000,000
|
|
6
|
+
// credits when the account never bought any (credits moved in from another
|
|
7
|
+
// account) – then converted at that day's ECB reference rate
|
|
8
|
+
// (createRateService().rate('USD', day)). A choice for the tax adviser; the
|
|
9
|
+
// statement names it.
|
|
10
|
+
|
|
11
|
+
import { multiply, normalize } from '@le-space/ledger/decimal';
|
|
12
|
+
|
|
13
|
+
/** Aleph's list price, USD per credit. */
|
|
14
|
+
export const ALEPH_LIST_PRICE_USD = '0.000001';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The per-credit price in force on each day, from an account's purchases.
|
|
18
|
+
*
|
|
19
|
+
* @param {{ time: string, price: string | number | null | undefined }[]} purchases any order; a
|
|
20
|
+
* purchase without a price (a transfer in) does not count
|
|
21
|
+
* @returns {(date: string) => { usdPerCredit: string, source: 'purchase' | 'list' }} date YYYY-MM-DD, UTC
|
|
22
|
+
*/
|
|
23
|
+
export function alephCreditPrice(purchases) {
|
|
24
|
+
const priced = purchases
|
|
25
|
+
.filter((p) => p?.price && Number(p.price) > 0)
|
|
26
|
+
.map((p) => ({ time: String(p.time), price: String(p.price) }))
|
|
27
|
+
.sort((a, b) => (a.time < b.time ? -1 : 1));
|
|
28
|
+
return (date) => {
|
|
29
|
+
let found = null;
|
|
30
|
+
for (const p of priced) if (p.time.slice(0, 10) <= date) found = p.price;
|
|
31
|
+
return found
|
|
32
|
+
? { usdPerCredit: found, source: 'purchase' }
|
|
33
|
+
: { usdPerCredit: ALEPH_LIST_PRICE_USD, source: 'list' };
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Credits → euros: credits × USD per credit × EUR per USD, rounded half away
|
|
39
|
+
* from zero to cents. Exact, no float in between.
|
|
40
|
+
*
|
|
41
|
+
* @param {string | number} credits
|
|
42
|
+
* @param {string} usdPerCredit
|
|
43
|
+
* @param {string} eurPerUsd
|
|
44
|
+
* @returns {string} EUR, two decimals at most
|
|
45
|
+
*/
|
|
46
|
+
export function alephCreditsInEur(credits, usdPerCredit, eurPerUsd) {
|
|
47
|
+
const usd = multiply(plain(credits), plain(usdPerCredit), 12);
|
|
48
|
+
return multiply(usd, plain(eurPerUsd), 2);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* A number as a plain decimal string (no exponent), at most 12 decimals.
|
|
53
|
+
*
|
|
54
|
+
* @param {string | number} v
|
|
55
|
+
*/
|
|
56
|
+
export function plain(v) {
|
|
57
|
+
if (typeof v === 'string' && /^-?\d+(\.\d+)?$/.test(v)) return normalize(v);
|
|
58
|
+
const n = Number(v);
|
|
59
|
+
if (!Number.isFinite(n)) throw new Error(`not a number: ${v}`);
|
|
60
|
+
return normalize(n.toFixed(12));
|
|
61
|
+
}
|
package/src/index.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
// trade at or after midnight), the ECB reference rate for currencies (Bank of
|
|
5
5
|
// Russia, National Bank of Kazakhstan where the ECB has none), and a token's
|
|
6
6
|
// DEX pool at the booking's block where nobody prices it. Every answer names
|
|
7
|
-
// its source and moment. `fetch` and the keys are handed in.
|
|
7
|
+
// its source and moment. `fetch` and the keys are handed in. Aleph Cloud
|
|
8
|
+
// credits, priced in USD by Aleph, have their own rule (aleph-credits.js).
|
|
8
9
|
export {
|
|
9
10
|
CBR_CROSS_CURRENCIES,
|
|
10
11
|
COINGECKO_PLATFORMS,
|
|
@@ -26,3 +27,9 @@ export {
|
|
|
26
27
|
v4PoolId,
|
|
27
28
|
virtualReserve
|
|
28
29
|
} from './dex-rate.js';
|
|
30
|
+
export {
|
|
31
|
+
ALEPH_LIST_PRICE_USD,
|
|
32
|
+
alephCreditPrice,
|
|
33
|
+
alephCreditsInEur,
|
|
34
|
+
plain
|
|
35
|
+
} from './aleph-credits.js';
|