@ledgerhq/coin-module-framework 0.1.9
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/.eslintrc.js +20 -0
- package/.unimportedrc.json +33 -0
- package/CHANGELOG.md +1 -0
- package/jest-global-setup.js +9 -0
- package/jest.config.js +30 -0
- package/package.json +82 -0
- package/src/api/errors.ts +5 -0
- package/src/api/index.ts +7 -0
- package/src/api/types.ts +726 -0
- package/src/config.ts +81 -0
- package/src/currencies/formatCurrencyUnit.ts +6 -0
- package/src/currencies/index.ts +7 -0
- package/src/currencies/parseCurrencyUnit.ts +1 -0
- package/src/errors.ts +4 -0
- package/src/features/types.ts +66 -0
- package/src/index.ts +5 -0
- package/src/setup.ts +4 -0
- package/src/test/utils.ts +26 -0
- package/src/utils.test.ts +23 -0
- package/src/utils.ts +20 -0
- package/tsconfig.build.json +18 -0
- package/tsconfig.json +26 -0
package/.eslintrc.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
env: {
|
|
3
|
+
browser: true,
|
|
4
|
+
es6: true,
|
|
5
|
+
},
|
|
6
|
+
overrides: [
|
|
7
|
+
{
|
|
8
|
+
files: ["src/**/*.test.{ts,tsx}"],
|
|
9
|
+
env: {
|
|
10
|
+
"jest/globals": true,
|
|
11
|
+
},
|
|
12
|
+
plugins: ["jest"],
|
|
13
|
+
},
|
|
14
|
+
],
|
|
15
|
+
rules: {
|
|
16
|
+
"no-console": ["error", { allow: ["warn", "error"] }],
|
|
17
|
+
"@typescript-eslint/no-empty-function": "off",
|
|
18
|
+
"@typescript-eslint/no-explicit-any": "warn",
|
|
19
|
+
},
|
|
20
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"entry": [
|
|
3
|
+
"src/api/index.ts",
|
|
4
|
+
"src/currencies/index.ts",
|
|
5
|
+
"src/index.ts",
|
|
6
|
+
"src/utils.ts"
|
|
7
|
+
],
|
|
8
|
+
"ignoreUnimported": [
|
|
9
|
+
"src/api/errors.ts",
|
|
10
|
+
"src/api/index.ts",
|
|
11
|
+
"src/api/types.ts",
|
|
12
|
+
"src/config.ts",
|
|
13
|
+
"src/currencies/formatCurrencyUnit.ts",
|
|
14
|
+
"src/currencies/index.ts",
|
|
15
|
+
"src/currencies/parseCurrencyUnit.ts",
|
|
16
|
+
"src/errors.ts",
|
|
17
|
+
"src/features/types.ts",
|
|
18
|
+
"src/setup.ts",
|
|
19
|
+
"src/test/utils.ts",
|
|
20
|
+
"src/utils.ts"
|
|
21
|
+
],
|
|
22
|
+
"ignoreUnresolved": [
|
|
23
|
+
"src/features/types.ts",
|
|
24
|
+
"src/setup.ts",
|
|
25
|
+
"src/utils.ts"
|
|
26
|
+
],
|
|
27
|
+
"ignoreUnused": [
|
|
28
|
+
"@ledgerhq/errors",
|
|
29
|
+
"@ledgerhq/live-env",
|
|
30
|
+
"@ledgerhq/types-cryptoassets",
|
|
31
|
+
"expect"
|
|
32
|
+
]
|
|
33
|
+
}
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# @ledgerhq/coin-module-framework
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
module.exports = async () => {
|
|
2
|
+
/**
|
|
3
|
+
* Set the timezone to EST for all tests
|
|
4
|
+
* This allow us to run tests directly through jest without having to manually
|
|
5
|
+
* set the timezone as an env var beforehand, either manually or through a
|
|
6
|
+
* custom script in package.json
|
|
7
|
+
*/
|
|
8
|
+
process.env.TZ = "America/New_York";
|
|
9
|
+
};
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
testEnvironment: "node",
|
|
3
|
+
transform: {
|
|
4
|
+
"^.+\\.(t|j)sx?$": [
|
|
5
|
+
"@swc/jest",
|
|
6
|
+
{
|
|
7
|
+
jsc: {
|
|
8
|
+
target: "esnext",
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
],
|
|
12
|
+
},
|
|
13
|
+
testPathIgnorePatterns: ["lib/", "lib-es/"],
|
|
14
|
+
globalSetup: "<rootDir>/jest-global-setup.js",
|
|
15
|
+
passWithNoTests: true,
|
|
16
|
+
collectCoverageFrom: [
|
|
17
|
+
"src/**/*.{ts,js,tsx}",
|
|
18
|
+
"!src/**/*.test.{ts,tsx}",
|
|
19
|
+
"!src/**/*.spec.{ts,tsx}",
|
|
20
|
+
"!src/**/__integration__/**",
|
|
21
|
+
"!src/**/__integrations__/**",
|
|
22
|
+
"!src/**/__tests__/**",
|
|
23
|
+
],
|
|
24
|
+
coverageReporters: ["json", ["lcov", { file: "lcov.info", projectRoot: "../../" }], "text"],
|
|
25
|
+
reporters: [
|
|
26
|
+
"default",
|
|
27
|
+
["jest-sonar", { outputName: "sonar-executionTests-report.xml", reportedFilePath: "absolute" }],
|
|
28
|
+
],
|
|
29
|
+
setupFilesAfterEnv: ["<rootDir>/src/setup.ts"],
|
|
30
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ledgerhq/coin-module-framework",
|
|
3
|
+
"version": "0.1.9",
|
|
4
|
+
"description": "Ledger framework for Coin integration",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"Ledger",
|
|
7
|
+
"LedgerWallet",
|
|
8
|
+
"Hardware Wallet"
|
|
9
|
+
],
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/LedgerHQ/alpaca-coin-module"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/LedgerHQ/alpaca-coin-module/issues"
|
|
16
|
+
},
|
|
17
|
+
"homepage": "https://github.com/LedgerHQ/alpaca-coin-module/tree/develop/libs/coin-module-framework",
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"typesVersions": {
|
|
22
|
+
"*": {
|
|
23
|
+
"lib/*": [
|
|
24
|
+
"lib/*"
|
|
25
|
+
],
|
|
26
|
+
"lib-es/*": [
|
|
27
|
+
"lib-es/*"
|
|
28
|
+
],
|
|
29
|
+
"currencies": [
|
|
30
|
+
"lib/currencies/index"
|
|
31
|
+
],
|
|
32
|
+
"*": [
|
|
33
|
+
"lib/*"
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"exports": {
|
|
38
|
+
"./lib/*": "./lib/*.js",
|
|
39
|
+
"./lib-es/*": "./lib-es/*.js",
|
|
40
|
+
"./currencies": {
|
|
41
|
+
"@ledgerhq/source": "./src/currencies/index.ts",
|
|
42
|
+
"require": "./lib/currencies/index.js",
|
|
43
|
+
"default": "./lib-es/currencies/index.js"
|
|
44
|
+
},
|
|
45
|
+
"./*": {
|
|
46
|
+
"@ledgerhq/source": "./src/*.ts",
|
|
47
|
+
"require": "./lib/*.js",
|
|
48
|
+
"default": "./lib-es/*.js"
|
|
49
|
+
},
|
|
50
|
+
"./package.json": "./package.json"
|
|
51
|
+
},
|
|
52
|
+
"license": "Apache-2.0",
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@ledgerhq/errors": "6.31.0-nightly.20260317030141",
|
|
55
|
+
"@ledgerhq/live-env": "2.30.0-nightly.20260317030141",
|
|
56
|
+
"@ledgerhq/live-currency-format": "0.6.0-nightly.20260317030141",
|
|
57
|
+
"@ledgerhq/types-cryptoassets": "7.35.0-nightly.20260317030141",
|
|
58
|
+
"bignumber.js": "^9.1.2"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@types/node": "^24.0.0",
|
|
62
|
+
"@types/jest": "^30.0.0",
|
|
63
|
+
"cross-env": "^7.0.3",
|
|
64
|
+
"jest": "^30.2.0",
|
|
65
|
+
"@swc/jest": "0.2.39",
|
|
66
|
+
"@swc/core": "1.15.11"
|
|
67
|
+
},
|
|
68
|
+
"scripts": {
|
|
69
|
+
"clean": "rm -rf lib lib-es",
|
|
70
|
+
"build": "tsc --project tsconfig.build.json && tsc --project tsconfig.build.json -m esnext --moduleResolution bundler --outDir lib-es",
|
|
71
|
+
"coverage": "jest --coverage",
|
|
72
|
+
"prewatch": "pnpm build",
|
|
73
|
+
"watch": "tsc --watch --project tsconfig.build.json",
|
|
74
|
+
"watch:es": "tsc --watch --project tsconfig.build.json -m esnext --moduleResolution bundler --outDir lib-es",
|
|
75
|
+
"lint": "eslint ./src --no-error-on-unmatched-pattern --ext .ts,.tsx --cache",
|
|
76
|
+
"lint:fix": "pnpm lint --fix",
|
|
77
|
+
"test": "cross-env TZ=America/Paris jest",
|
|
78
|
+
"test:debug": "cross-env TZ=America/Paris node --inspect-brk ./node_modules/jest/bin/jest.js --runInBand",
|
|
79
|
+
"unimported": "unimported",
|
|
80
|
+
"update:deps": "../../bin/update-coin-module-deps.sh"
|
|
81
|
+
}
|
|
82
|
+
}
|
package/src/api/index.ts
ADDED
package/src/api/types.ts
ADDED
|
@@ -0,0 +1,726 @@
|
|
|
1
|
+
// NOTE: from types-live
|
|
2
|
+
export type BroadcastConfig = {
|
|
3
|
+
mevProtected: boolean
|
|
4
|
+
sponsored?: boolean
|
|
5
|
+
source?: TransactionSource
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* TransactionSource identifies the origin of a transaction
|
|
10
|
+
*/
|
|
11
|
+
export type TransactionSource = {
|
|
12
|
+
// Type of the transaction source
|
|
13
|
+
type: 'dApp' | 'live-app' | 'coin-module' | 'swap'
|
|
14
|
+
// Name/identifier of the source (e.g., manifestId, provider name)
|
|
15
|
+
name: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type BlockInfo = {
|
|
19
|
+
height: number
|
|
20
|
+
hash: string
|
|
21
|
+
// can be different from tx date
|
|
22
|
+
// transaction could be created at a particular moment, but depending on network conditions
|
|
23
|
+
// mining time, and block intervals, it might not get included in the blockchain until later
|
|
24
|
+
time: Date
|
|
25
|
+
parent?: ParentBlock
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type ParentBlock = Pick<BlockInfo, 'height' | 'hash'>
|
|
29
|
+
|
|
30
|
+
// NOTE: from crypto-asset
|
|
31
|
+
export type Unit = {
|
|
32
|
+
// display name of a given unit (example: satoshi)
|
|
33
|
+
name: string
|
|
34
|
+
// string to use when formatting the unit. like 'BTC' or 'USD'
|
|
35
|
+
code: string
|
|
36
|
+
// number of digits after the '.'
|
|
37
|
+
magnitude: number
|
|
38
|
+
// should it always print all digits even if they are 0 (usually: true for fiats, false for cryptos)
|
|
39
|
+
showAllDigits?: boolean
|
|
40
|
+
// true if the code should prefix amount when formatting
|
|
41
|
+
prefixCode?: boolean
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type AssetInfo =
|
|
45
|
+
| { type: 'native'; name?: string; unit?: Unit }
|
|
46
|
+
| {
|
|
47
|
+
type: string // token, coin, fungible_asset, trc10, trc20, erc20, erc721, erc1155, etc.
|
|
48
|
+
assetReference?: string // contract address (trc20), tokenId (trc10),, etc
|
|
49
|
+
assetOwner?: string // Address of the asset contract deployer/creator
|
|
50
|
+
name?: string // e.g., token name, or asset name
|
|
51
|
+
unit?: Unit
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// NOTE: CoreOperation
|
|
55
|
+
export type Operation<MemoType extends Memo = MemoNotSupported> = {
|
|
56
|
+
id: string
|
|
57
|
+
type: string
|
|
58
|
+
|
|
59
|
+
senders: string[]
|
|
60
|
+
recipients: string[]
|
|
61
|
+
|
|
62
|
+
value: bigint
|
|
63
|
+
asset: AssetInfo
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Optional memo associated with the operation.
|
|
67
|
+
* Use a `Memo` interface like `StringMemo<"text">`, `MapMemo<Kind, Value>`, or `MyMemo`.
|
|
68
|
+
* Defaults to `MemoNotSupported`.
|
|
69
|
+
*/
|
|
70
|
+
memo?: MemoType
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Arbitrary per-blockchain extra fields.
|
|
74
|
+
* This can include things like status, error messages, swap info, etc.
|
|
75
|
+
*/
|
|
76
|
+
details?: Record<string, unknown>
|
|
77
|
+
tx: {
|
|
78
|
+
hash: string // transaction hash
|
|
79
|
+
block: BlockInfo // block metadata, empty string for block hash if not available directly and no reorg possible
|
|
80
|
+
fees: bigint // network fees paid
|
|
81
|
+
// address that paid for this transaction's fees.
|
|
82
|
+
// optional as it may be unknown (e.g. the information is not available in the context of the operation).
|
|
83
|
+
// if it is unknown, it's expected that none of the sender addresses paid for the fees
|
|
84
|
+
feesPayer?: string
|
|
85
|
+
date: Date // tx date (may differ from block time)
|
|
86
|
+
|
|
87
|
+
/** If the transaction has failed, fees have been paid but other balance changes are not effective.*/
|
|
88
|
+
failed: boolean
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export type Transaction = {
|
|
93
|
+
type: string
|
|
94
|
+
recipient: string
|
|
95
|
+
amount: bigint
|
|
96
|
+
fee: bigint
|
|
97
|
+
} & Record<string, unknown> // Field containing dedicated value for each blockchain
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* A block along with its {@link BlockTransaction}, not specific to a particular account/address.
|
|
101
|
+
*/
|
|
102
|
+
export type Block = {
|
|
103
|
+
/** The block metadata. */
|
|
104
|
+
info: BlockInfo
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* The block transactions.
|
|
108
|
+
*
|
|
109
|
+
* It should include at least all transactions where an EOA is involved, however it is OK to ignore other types of
|
|
110
|
+
* transactions that cannot cause balance changes (eg: validator vote transactions on Solana).
|
|
111
|
+
*/
|
|
112
|
+
transactions: BlockTransaction[]
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* A transaction belonging to a {@link Block}, not specific to a particular account/address.
|
|
117
|
+
*/
|
|
118
|
+
export type BlockTransaction = {
|
|
119
|
+
/** The transaction hash/digest (globally unique identifier). */
|
|
120
|
+
hash: string
|
|
121
|
+
|
|
122
|
+
/** If the transaction has been failed, fees have been paid but other balance changes are not effective.*/
|
|
123
|
+
failed: boolean
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The operations/instructions included in this transaction.
|
|
127
|
+
*
|
|
128
|
+
* It should include at least all operations where an EOA is involved, however it is OK to ignore other types of
|
|
129
|
+
* operations that cannot cause balance changes (eg: validator vote instructions on Solana).
|
|
130
|
+
*
|
|
131
|
+
* Note that fees are accounted for separately, so operations must not represent fees.
|
|
132
|
+
*/
|
|
133
|
+
operations: BlockOperation[]
|
|
134
|
+
|
|
135
|
+
/** Network specific details for this transaction. */
|
|
136
|
+
details?: Record<string, unknown>
|
|
137
|
+
|
|
138
|
+
/** The fee amount paid for this transaction, in base unit of the network native coin, always positive or zero. */
|
|
139
|
+
fees: bigint
|
|
140
|
+
|
|
141
|
+
/** The address that paid for this transaction's fees. */
|
|
142
|
+
feesPayer?: string
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** An operation belonging to a {@link BlockTransaction}. */
|
|
146
|
+
export type BlockOperation = TransferBlockOperation | OtherBlockOperation
|
|
147
|
+
|
|
148
|
+
/** An asset transfer that occurred in a {@link BlockTransaction}. */
|
|
149
|
+
export type TransferBlockOperation = {
|
|
150
|
+
/** Operation type discriminator. */
|
|
151
|
+
type: 'transfer'
|
|
152
|
+
|
|
153
|
+
/** The impacted address (can be sender or recipient based on signum of <code>amount</code>). */
|
|
154
|
+
address: string
|
|
155
|
+
|
|
156
|
+
/** The peer participant in the transfer (optional as it may be not known). */
|
|
157
|
+
peer?: string
|
|
158
|
+
|
|
159
|
+
/** The transferred asset. */
|
|
160
|
+
asset: AssetInfo
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* The signed amount of the transfer, i.e. impact of the transfer on <code>address</code> balance (positive for
|
|
164
|
+
* incoming, negative for outgoing).
|
|
165
|
+
*/
|
|
166
|
+
amount: bigint
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* An unclassified type of operation that occurred in a {@link BlockTransaction}.
|
|
171
|
+
*
|
|
172
|
+
* Implementations are free to partially/completely omit this kind of operations.
|
|
173
|
+
*/
|
|
174
|
+
export type OtherBlockOperation = {
|
|
175
|
+
type: 'other'
|
|
176
|
+
} & Record<string, unknown>
|
|
177
|
+
|
|
178
|
+
// Other coins take different parameters What do we want to do ?
|
|
179
|
+
export type Account = {
|
|
180
|
+
currencyName: string
|
|
181
|
+
address: string
|
|
182
|
+
balance: bigint
|
|
183
|
+
currencyUnit: Unit
|
|
184
|
+
spendableBalance: bigint // NOTE:: check if we can get rid of this one
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* A component of an account/address balance, for a single asset.
|
|
189
|
+
*
|
|
190
|
+
* @see AlpacaApi#getBalance
|
|
191
|
+
*/
|
|
192
|
+
export type Balance = {
|
|
193
|
+
/** The balance value, in base unit of {@link asset} (always positive). */
|
|
194
|
+
value: bigint
|
|
195
|
+
|
|
196
|
+
/** The balance asset. */
|
|
197
|
+
asset: AssetInfo
|
|
198
|
+
|
|
199
|
+
/** The non-spendable part of {@link value} (eg: minimum balance requirement, or reserved for rent). */
|
|
200
|
+
locked?: bigint
|
|
201
|
+
|
|
202
|
+
/** The {@link Stake} this balance is part of, if any. */
|
|
203
|
+
stake?: Stake
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** The state of a {@link Stake}. */
|
|
207
|
+
export type StakeState =
|
|
208
|
+
| 'inactive' // stake has been created/funded, but not collecting any rewards for any reason
|
|
209
|
+
| 'activating' // stake has been created/funded, and will start collecting rewards on next "epoch" (protocol specific)
|
|
210
|
+
| 'active' // stake is initialized and collecting rewards
|
|
211
|
+
| 'deactivating' // stake has been deactivated, will be withdrawable/spendable yet on next "epoch" (protocol specific)
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* A staking position, for a single address/asset/state.
|
|
215
|
+
*
|
|
216
|
+
* Note that on blockchains that allow heterogeneous assets/states in a single account, a staking account is represented
|
|
217
|
+
* as several {@link Stake}.
|
|
218
|
+
*
|
|
219
|
+
* @see Reward
|
|
220
|
+
* @see AlpacaApi#getStakes
|
|
221
|
+
*/
|
|
222
|
+
export type Stake = {
|
|
223
|
+
/** An immutable, globally unique id of the stake. Depending on the blockchain, it could simply be the account address,
|
|
224
|
+
* or a synthetic identifier. */
|
|
225
|
+
uid: string
|
|
226
|
+
|
|
227
|
+
/** The owning account address. Depending on the blockchain, it can be the staking account address or directly the
|
|
228
|
+
* main one. */
|
|
229
|
+
address: string
|
|
230
|
+
|
|
231
|
+
/** The validator/staking pool/delegate address. */
|
|
232
|
+
delegate?: string
|
|
233
|
+
|
|
234
|
+
/** The stake status, see {@link StakeState}. */
|
|
235
|
+
state: StakeState
|
|
236
|
+
|
|
237
|
+
/** UTC date of last state change. */
|
|
238
|
+
stateUpdatedAt?: Date
|
|
239
|
+
|
|
240
|
+
/** UTC date of initial stake creation. */
|
|
241
|
+
createdAt?: Date
|
|
242
|
+
|
|
243
|
+
/** The staked asset. */
|
|
244
|
+
asset: AssetInfo
|
|
245
|
+
|
|
246
|
+
/** The amount owned by the stake, in base unit of {@link asset} (deposits + rewards). */
|
|
247
|
+
amount: bigint
|
|
248
|
+
|
|
249
|
+
/** The part of {@link amount} that was deposited (<code>amount = amount_deposited + amount_rewarded</code>). */
|
|
250
|
+
amountDeposited?: bigint
|
|
251
|
+
|
|
252
|
+
/** The part of {@link amount} that was rewarded (<code>amount = amount_deposited + amount_rewarded</code>). */
|
|
253
|
+
amountRewarded?: bigint
|
|
254
|
+
|
|
255
|
+
/** A free form map of network specific fields. */
|
|
256
|
+
details?: Record<string, unknown>
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* A staking reward distribution event.
|
|
261
|
+
*
|
|
262
|
+
* @see Stake
|
|
263
|
+
* @see AlpacaApi#getRewards
|
|
264
|
+
*/
|
|
265
|
+
export type Reward = {
|
|
266
|
+
/** {@link Stake#uid} via which this reward was obtained. */
|
|
267
|
+
stake: string
|
|
268
|
+
|
|
269
|
+
/** The reward asset. */
|
|
270
|
+
asset: AssetInfo
|
|
271
|
+
|
|
272
|
+
/** The reward amount. */
|
|
273
|
+
amount: bigint
|
|
274
|
+
|
|
275
|
+
/** UTC date at which reward was effectively credited to the account (not emitted). */
|
|
276
|
+
receivedAt: Date
|
|
277
|
+
|
|
278
|
+
/** If applicable, the transaction hash that distributed the reward. */
|
|
279
|
+
transactionHash?: string
|
|
280
|
+
|
|
281
|
+
/** A free form map of network specific fields. */
|
|
282
|
+
details?: Record<string, unknown>
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Computational payload processed by Blockchains, such as
|
|
287
|
+
* calldata on EVM or instruction data on Solana
|
|
288
|
+
*/
|
|
289
|
+
export interface TxData {
|
|
290
|
+
type: string
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Default implementation when no computational payload is supported
|
|
295
|
+
* by the underlying Blockchain
|
|
296
|
+
*/
|
|
297
|
+
export interface TxDataNotSupported extends TxData {
|
|
298
|
+
type: 'none'
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Implementation with bufferized computational payload
|
|
303
|
+
*/
|
|
304
|
+
export interface BufferTxData extends TxData {
|
|
305
|
+
type: 'buffer'
|
|
306
|
+
value: Buffer
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export interface Memo {
|
|
310
|
+
type: string
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// generic implementations that cover many coins (in coin-framework)
|
|
314
|
+
export interface MemoNotSupported extends Memo {
|
|
315
|
+
type: 'none'
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Specialized version, not extending the above
|
|
319
|
+
export interface StringMemo<Kind extends string = 'text'> extends Memo {
|
|
320
|
+
type: 'string'
|
|
321
|
+
kind: Kind
|
|
322
|
+
value: string
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export interface MapMemo<Kind extends string, Value> extends Memo {
|
|
326
|
+
type: string
|
|
327
|
+
memos: Map<Kind, Value>
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export interface TypedMapMemo<KindToValueMap extends Record<string, unknown>> extends Memo {
|
|
331
|
+
type: string
|
|
332
|
+
memos: Map<keyof KindToValueMap, KindToValueMap[keyof KindToValueMap]>
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
type MaybeMemo<MemoType extends Memo> = MemoType extends MemoNotSupported
|
|
336
|
+
? object
|
|
337
|
+
: { memo: MemoType }
|
|
338
|
+
|
|
339
|
+
type MaybeTxData<TxDataType extends TxData> = TxDataType extends TxDataNotSupported
|
|
340
|
+
? object
|
|
341
|
+
: { data: TxDataType }
|
|
342
|
+
|
|
343
|
+
export type FeesStrategy = 'slow' | 'medium' | 'fast' | 'custom'
|
|
344
|
+
|
|
345
|
+
export type StakingOperation = 'delegate' | 'undelegate' | 'redelegate'
|
|
346
|
+
|
|
347
|
+
export type TransactionIntent<
|
|
348
|
+
MemoType extends Memo = MemoNotSupported,
|
|
349
|
+
TxDataType extends TxData = TxDataNotSupported,
|
|
350
|
+
> = {
|
|
351
|
+
intentType: 'transaction' | 'staking'
|
|
352
|
+
type: string
|
|
353
|
+
sender: string
|
|
354
|
+
recipient: string
|
|
355
|
+
amount: bigint
|
|
356
|
+
asset: AssetInfo
|
|
357
|
+
useAllAmount?: boolean
|
|
358
|
+
feesStrategy?: FeesStrategy
|
|
359
|
+
senderPublicKey?: string
|
|
360
|
+
sequence?: bigint
|
|
361
|
+
expiration?: number
|
|
362
|
+
sponsored?: boolean
|
|
363
|
+
} & MaybeMemo<MemoType> &
|
|
364
|
+
MaybeTxData<TxDataType>
|
|
365
|
+
|
|
366
|
+
export type StakingTransactionIntent<
|
|
367
|
+
MemoType extends Memo = MemoNotSupported,
|
|
368
|
+
TxDataType extends TxData = TxDataNotSupported,
|
|
369
|
+
> = TransactionIntent & {
|
|
370
|
+
intentType: 'staking'
|
|
371
|
+
mode: StakingOperation
|
|
372
|
+
valAddress: string
|
|
373
|
+
dstValAddress?: string
|
|
374
|
+
} & MaybeMemo<MemoType> &
|
|
375
|
+
MaybeTxData<TxDataType>
|
|
376
|
+
|
|
377
|
+
export type SendTransactionIntent<
|
|
378
|
+
MemoType extends Memo = MemoNotSupported,
|
|
379
|
+
TxDataType extends TxData = TxDataNotSupported,
|
|
380
|
+
> = TransactionIntent & {
|
|
381
|
+
intentType: 'transaction'
|
|
382
|
+
} & MaybeMemo<MemoType> &
|
|
383
|
+
MaybeTxData<TxDataType>
|
|
384
|
+
|
|
385
|
+
export type TransactionValidation = {
|
|
386
|
+
errors: Record<string, Error>
|
|
387
|
+
warnings: Record<string, Error>
|
|
388
|
+
estimatedFees: bigint
|
|
389
|
+
totalFees?: bigint
|
|
390
|
+
amount: bigint
|
|
391
|
+
totalSpent: bigint
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export type FeeEstimation = {
|
|
395
|
+
value: bigint
|
|
396
|
+
parameters?: Record<string, unknown>
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/** Response of {@link AlpacaApi#craftTransaction}. */
|
|
400
|
+
export type CraftedTransaction = {
|
|
401
|
+
/** The serialized transaction (encoding is blockchain dependent). */
|
|
402
|
+
transaction: string
|
|
403
|
+
/** Blockchain specific details (eg: UTXOs referenced in the transaction). */
|
|
404
|
+
details?: Record<string, unknown>
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/** A pagination cursor. */
|
|
408
|
+
export type Cursor = string
|
|
409
|
+
|
|
410
|
+
/** A paginated response. */
|
|
411
|
+
export type Page<T> = {
|
|
412
|
+
items: T[]
|
|
413
|
+
next?: Cursor | undefined
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/** Options for {@link AlpacaApi#listOperations}. */
|
|
417
|
+
export type ListOperationsOptions = {
|
|
418
|
+
/**
|
|
419
|
+
* The minimum block height for which to fetch operations (inclusive).
|
|
420
|
+
*
|
|
421
|
+
* Implementation must raise a "not supported" error if `minHeight` is non-zero and not supported.
|
|
422
|
+
*/
|
|
423
|
+
minHeight: number
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* A pagination cursor to resume listing.
|
|
427
|
+
*
|
|
428
|
+
* Implementation must guarantee the cursor is not volatile, i.e. it can be used long after the last request and still
|
|
429
|
+
* provide consistent results - for instance, a date or transaction hash.
|
|
430
|
+
*/
|
|
431
|
+
cursor?: Cursor
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* The maximum number of operations to fetch (note this is a soft limit, the implementation may return less or more
|
|
435
|
+
* operations to not waste RPC calls).
|
|
436
|
+
*
|
|
437
|
+
* Implementation must raise a "not supported" error if limit is set and not supported.
|
|
438
|
+
*/
|
|
439
|
+
limit?: number
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* The chronological order of the operations (within one page as well as globally when concatenating all pages).
|
|
443
|
+
*
|
|
444
|
+
* Implementation must raise a "not supported" error if order is set and not supported.
|
|
445
|
+
*/
|
|
446
|
+
order?: 'asc' | 'desc'
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/** A network validator */
|
|
450
|
+
export type Validator = {
|
|
451
|
+
/** Address of the validator. */
|
|
452
|
+
address: string
|
|
453
|
+
|
|
454
|
+
/** Human-readable name of the validator. */
|
|
455
|
+
name: string
|
|
456
|
+
|
|
457
|
+
/** Human-readable description of the validator. */
|
|
458
|
+
description?: string | undefined
|
|
459
|
+
|
|
460
|
+
/** URL of the entity running the validator. */
|
|
461
|
+
url?: string | undefined
|
|
462
|
+
|
|
463
|
+
/** URL of the logo for the validator. */
|
|
464
|
+
logo?: string | undefined
|
|
465
|
+
|
|
466
|
+
/** Amount of native asset in the pool (in base unit of chain native currency). */
|
|
467
|
+
balance?: bigint | undefined
|
|
468
|
+
|
|
469
|
+
/** Validator commission (a bigint serialized as a string). */
|
|
470
|
+
commissionRate?: string | undefined
|
|
471
|
+
|
|
472
|
+
/** Validator Annual Percentage Yield (floating point number between 0 and 1). */
|
|
473
|
+
apy?: number | undefined
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export type AccountInfo = {
|
|
477
|
+
isNewAccount: boolean
|
|
478
|
+
balance: string
|
|
479
|
+
ownerCount: number
|
|
480
|
+
sequence: number
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
export type AddressValidationCurrencyParameters = {
|
|
484
|
+
currencyId: string
|
|
485
|
+
networkId: number
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
export type AlpacaApi<
|
|
489
|
+
MemoType extends Memo = MemoNotSupported,
|
|
490
|
+
TxDataType extends TxData = TxDataNotSupported,
|
|
491
|
+
> = {
|
|
492
|
+
// blockchain API
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Get the latest block information from the network.
|
|
496
|
+
*
|
|
497
|
+
* This must return the same result as {@link getBlockInfo} for the latest block height.
|
|
498
|
+
*
|
|
499
|
+
* @returns the latest block metadata (height, hash, time)
|
|
500
|
+
* @see getBlockInfo
|
|
501
|
+
* @see getBlock
|
|
502
|
+
*/
|
|
503
|
+
lastBlock: () => Promise<BlockInfo>
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Get block information for a specific block height.
|
|
507
|
+
*
|
|
508
|
+
* This must return the same result as {@link lastBlock} for the latest block height.
|
|
509
|
+
*
|
|
510
|
+
* This API is optional and may not be supported on all networks: implementation should raise a "not supported" error
|
|
511
|
+
* in such case.
|
|
512
|
+
*
|
|
513
|
+
* @param height the block height to query
|
|
514
|
+
* @returns the block metadata (height, hash, time)
|
|
515
|
+
* @throws "not supported" if the blockchain does not support querying historical blocks
|
|
516
|
+
*/
|
|
517
|
+
getBlockInfo: (height: number) => Promise<BlockInfo>
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Get a full block with all its transactions.
|
|
521
|
+
*
|
|
522
|
+
* This returns the complete block data including all transactions and their operations.
|
|
523
|
+
*
|
|
524
|
+
* This must return the same block info as {@link getBlockInfo} for the same height.
|
|
525
|
+
*
|
|
526
|
+
* This API is optional and may not be supported on all networks: implementation should raise a "not supported" error
|
|
527
|
+
* in such case.
|
|
528
|
+
*
|
|
529
|
+
* @param height the block height to query
|
|
530
|
+
* @returns the complete block with transactions
|
|
531
|
+
* @throws "not supported" if the blockchain does not support querying full blocks
|
|
532
|
+
*/
|
|
533
|
+
getBlock: (height: number) => Promise<Block>
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Get the list of validators available on the network.
|
|
537
|
+
* @param cursor a pagination cursor to resume listing (the implementation must guarantee the cursor is not volatile,
|
|
538
|
+
* i.e. it can be used long after the last request and still provide consistent results - for instance,
|
|
539
|
+
* a date or transaction hash).
|
|
540
|
+
* The concrete implementation may return all validators in a single page when the underlying SDK
|
|
541
|
+
* does not provide cursor-based pagination.
|
|
542
|
+
*/
|
|
543
|
+
getValidators: (cursor?: Cursor) => Promise<Page<Validator>>
|
|
544
|
+
|
|
545
|
+
// account read API
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Get the balance(s) for an address/account.
|
|
549
|
+
*
|
|
550
|
+
* Returns all asset balances associated with the address, including the native asset and any tokens/sub-assets. Each
|
|
551
|
+
* balance includes the total value and optionally locked/staked amounts.
|
|
552
|
+
*
|
|
553
|
+
* If account is not found, implementation must return an empty balance.
|
|
554
|
+
*
|
|
555
|
+
* @param address the account address
|
|
556
|
+
* @returns an array of balances for all assets held by the address
|
|
557
|
+
* @see getStakes
|
|
558
|
+
* @see listOperations
|
|
559
|
+
*/
|
|
560
|
+
getBalance: (address: string) => Promise<Balance[]>
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* List operations for an address/account.
|
|
564
|
+
*
|
|
565
|
+
* If account is not found, implementation must return an empty result.
|
|
566
|
+
*
|
|
567
|
+
* @param address the owner account address
|
|
568
|
+
* @param options see {@link ListOperationsOptions}
|
|
569
|
+
* @returns a page of operations
|
|
570
|
+
* @see getBalance
|
|
571
|
+
*/
|
|
572
|
+
listOperations: (address: string, options: ListOperationsOptions) => Promise<Page<Operation>>
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Get staking positions owned by an address/account.
|
|
576
|
+
*
|
|
577
|
+
* Results are returned in no particular order, in pages that can be of variable size. Page size is controlled by
|
|
578
|
+
* implementation and should minimize number of RPC calls (typically by aligning with SDK/RPC API pages).
|
|
579
|
+
*
|
|
580
|
+
* Results could include closed/deleted staking positions, this is implementation dependent.
|
|
581
|
+
*
|
|
582
|
+
* Since this API can make no sense/be complex to implement/require too many RPC calls on some blockchains, it is
|
|
583
|
+
* optional: implementation should raise a "not supported" error in such case.
|
|
584
|
+
*
|
|
585
|
+
* @param address the owner account address
|
|
586
|
+
* @param cursor a pagination cursor to resume listing (the implementation must guarantee the cursor is not volatile,
|
|
587
|
+
* i.e. it can be used long after the last request and still provide consistent results - for instance,
|
|
588
|
+
* a date or transaction hash)
|
|
589
|
+
* @see getBalance
|
|
590
|
+
* @see getRewards
|
|
591
|
+
*/
|
|
592
|
+
getStakes: (address: string, cursor?: Cursor) => Promise<Page<Stake>>
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Get staking reward distribution events since address/account inception.
|
|
596
|
+
*
|
|
597
|
+
* Results are returned in ascending chronological order, in pages that can be of variable size. Page size is
|
|
598
|
+
* controlled by implementation and should minimize number of RPC calls (typically by aligning with SDK/RPC API pages).
|
|
599
|
+
*
|
|
600
|
+
* Note that since staking implementations vary from one blockchain to another, some points are implementation dependent:
|
|
601
|
+
* - depending on the blockchain account model, the exact meaning of <code>address</code> can be:
|
|
602
|
+
* - a parent account => history will include all staking subaccounts
|
|
603
|
+
* - a single staking position/subaccount address
|
|
604
|
+
* - depending on the reward distribution mechanisms, reward events can be transactions, or blocks/epochs, or
|
|
605
|
+
* generated synthetically (eg: daily)
|
|
606
|
+
*
|
|
607
|
+
* Since this API can make no sense/be complex to implement/require too many RPC calls on some blockchains, it is
|
|
608
|
+
* optional: implementation should raise a "not supported" error in such case.
|
|
609
|
+
*
|
|
610
|
+
* @param address the account address (see doc, exact scope of the request is implementation dependent)
|
|
611
|
+
* @param cursor a pagination cursor to resume listing (the implementation must guarantee the cursor is not volatile,
|
|
612
|
+
* i.e. it can be used long after the last request and still provide consistent results - for instance,
|
|
613
|
+
* a date or transaction hash)
|
|
614
|
+
* @see getBalance
|
|
615
|
+
* @see getStakes
|
|
616
|
+
*/
|
|
617
|
+
getRewards: (address: string, cursor?: Cursor) => Promise<Page<Reward>>
|
|
618
|
+
|
|
619
|
+
// transaction API
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Craft an unsigned transaction from a transaction intent.
|
|
623
|
+
*
|
|
624
|
+
* The crafted transaction is ready to be signed by the hardware wallet and then combined
|
|
625
|
+
* with the signature using {@link combine}.
|
|
626
|
+
*
|
|
627
|
+
* @param transactionIntent the transaction intent describing what the user wants to do
|
|
628
|
+
* @param customFees optional custom fees to use instead of the default estimation
|
|
629
|
+
* @returns the crafted transaction with optional blockchain-specific details
|
|
630
|
+
* @see craftRawTransaction
|
|
631
|
+
*/
|
|
632
|
+
craftTransaction: (
|
|
633
|
+
transactionIntent: TransactionIntent<MemoType, TxDataType>,
|
|
634
|
+
customFees?: FeeEstimation
|
|
635
|
+
) => Promise<CraftedTransaction>
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* Craft an unsigned transaction from a raw/pre-built transaction.
|
|
639
|
+
*
|
|
640
|
+
* This is an alternative to {@link craftTransaction} for cases where the transaction
|
|
641
|
+
* is already built externally (e.g., by a dApp or smart contract interaction).
|
|
642
|
+
*
|
|
643
|
+
* @param transaction the raw transaction to wrap/prepare for signing
|
|
644
|
+
* @param sender the sender address
|
|
645
|
+
* @param publicKey the sender's public key
|
|
646
|
+
* @param sequence the account sequence/nonce (to prevent replay attacks)
|
|
647
|
+
* @returns the crafted transaction ready for signing
|
|
648
|
+
* @throws "not supported" if the blockchain does not support raw transaction crafting
|
|
649
|
+
* @see craftTransaction
|
|
650
|
+
*/
|
|
651
|
+
craftRawTransaction: (
|
|
652
|
+
transaction: string,
|
|
653
|
+
sender: string,
|
|
654
|
+
publicKey: string,
|
|
655
|
+
sequence: bigint
|
|
656
|
+
) => Promise<CraftedTransaction>
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Estimate the fees for a transaction intent.
|
|
660
|
+
*
|
|
661
|
+
* The estimation should be based on current network conditions (e.g., gas price, fee rate).
|
|
662
|
+
*
|
|
663
|
+
* @param transactionIntent the transaction intent describing what the user wants to do
|
|
664
|
+
* @param customFeesParameters optional blockchain-specific parameters to customize the fee estimation
|
|
665
|
+
* (e.g., gas limit, priority fee)
|
|
666
|
+
* @returns the estimated fees and optional parameters that can be passed to {@link craftTransaction}
|
|
667
|
+
*/
|
|
668
|
+
estimateFees: (
|
|
669
|
+
transactionIntent: TransactionIntent<MemoType, TxDataType>,
|
|
670
|
+
customFeesParameters?: FeeEstimation['parameters']
|
|
671
|
+
) => Promise<FeeEstimation>
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Combine a crafted transaction with a signature to produce a signed transaction ready for broadcast.
|
|
675
|
+
*
|
|
676
|
+
* @param tx the unsigned/crafted transaction (as returned by {@link craftTransaction})
|
|
677
|
+
* @param signature the signature produced by the hardware wallet
|
|
678
|
+
* @param pubkey the public key used to sign (required for some blockchains to verify/embed in the transaction)
|
|
679
|
+
* @returns the signed transaction ready for {@link broadcast}
|
|
680
|
+
*/
|
|
681
|
+
combine: (tx: string, signature: string, pubkey?: string) => string | Promise<string>
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Broadcast a signed transaction to the network.
|
|
685
|
+
*
|
|
686
|
+
* @param tx the signed transaction (encoding is blockchain dependent, typically hex-encoded)
|
|
687
|
+
* @param broadcastConfig optional configuration for the broadcast (e.g., retry settings)
|
|
688
|
+
* @returns the transaction hash/digest once successfully submitted to the network
|
|
689
|
+
* @throws if the transaction is rejected by the network (e.g., invalid signature, insufficient funds)
|
|
690
|
+
*/
|
|
691
|
+
broadcast: (tx: string, broadcastConfig?: BroadcastConfig) => Promise<string>
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* Validate a transaction intent.
|
|
695
|
+
*
|
|
696
|
+
* @param transactionIntent the transaction intent describing what the user wants to do
|
|
697
|
+
* @param balances current balances of the intent sender
|
|
698
|
+
* @param customFees optional custom fees to use instead of the default estimation
|
|
699
|
+
* @returns additional values the intent has been validated with, and optional errors/warnings
|
|
700
|
+
*/
|
|
701
|
+
validateIntent: (
|
|
702
|
+
transactionIntent: TransactionIntent<MemoType, TxDataType>,
|
|
703
|
+
balances: Balance[],
|
|
704
|
+
customFees?: FeeEstimation
|
|
705
|
+
) => Promise<TransactionValidation>
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Get the next sequence number (nonce) for an address
|
|
709
|
+
*
|
|
710
|
+
* @param address the account address
|
|
711
|
+
* @returns the next usable sequence number
|
|
712
|
+
*/
|
|
713
|
+
getNextSequence: (address: string) => Promise<bigint>
|
|
714
|
+
|
|
715
|
+
/**
|
|
716
|
+
* Validate whether an address is well-formed for the blockchain.
|
|
717
|
+
*
|
|
718
|
+
* @param address the address to validate
|
|
719
|
+
* @param parameters currency-specific validation parameters
|
|
720
|
+
* @returns `true` if the address is valid, `false` otherwise
|
|
721
|
+
*/
|
|
722
|
+
validateAddress: (
|
|
723
|
+
address: string,
|
|
724
|
+
parameters: Partial<AddressValidationCurrencyParameters>
|
|
725
|
+
) => Promise<boolean>
|
|
726
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { CryptoCurrency, CryptoCurrencyId } from '@ledgerhq/types-cryptoassets'
|
|
2
|
+
import { MissingCoinConfig } from './errors'
|
|
3
|
+
import type { FeatureConfig } from './features/types'
|
|
4
|
+
|
|
5
|
+
type ConfigStatus =
|
|
6
|
+
| {
|
|
7
|
+
type: 'active'
|
|
8
|
+
features?: FeatureConfig[]
|
|
9
|
+
}
|
|
10
|
+
| {
|
|
11
|
+
type: 'under_maintenance'
|
|
12
|
+
message?: string
|
|
13
|
+
}
|
|
14
|
+
| {
|
|
15
|
+
type: 'migration'
|
|
16
|
+
chain: CryptoCurrencyId
|
|
17
|
+
from: string
|
|
18
|
+
to: string
|
|
19
|
+
link: string
|
|
20
|
+
}
|
|
21
|
+
| {
|
|
22
|
+
type: 'feature_unavailable'
|
|
23
|
+
link: string
|
|
24
|
+
feature:
|
|
25
|
+
| 'history'
|
|
26
|
+
| 'swap'
|
|
27
|
+
| 'token_history'
|
|
28
|
+
| 'send_and_receive'
|
|
29
|
+
| 'send'
|
|
30
|
+
| 'receive'
|
|
31
|
+
| 'sending_tokens'
|
|
32
|
+
| 'receiving_tokens'
|
|
33
|
+
| 'staking'
|
|
34
|
+
| 'claiming_staking_rewards'
|
|
35
|
+
}
|
|
36
|
+
| {
|
|
37
|
+
type: 'will_be_deprecated'
|
|
38
|
+
deprecated_date: string
|
|
39
|
+
link: string
|
|
40
|
+
}
|
|
41
|
+
| {
|
|
42
|
+
type: 'deprecated'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
type Banner = {
|
|
46
|
+
isDisplay: boolean
|
|
47
|
+
bannerText: string
|
|
48
|
+
bannerLink?: string
|
|
49
|
+
bannerLinkText?: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type CurrencyConfig = {
|
|
53
|
+
status: ConfigStatus
|
|
54
|
+
customBanner?: Banner
|
|
55
|
+
[key: string]: unknown
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export type CoinConfig<T extends CurrencyConfig> = (currency?: CryptoCurrency) => T
|
|
59
|
+
|
|
60
|
+
function buildCoinConfig<T extends CurrencyConfig>() {
|
|
61
|
+
let coinConfig: CoinConfig<T> | undefined
|
|
62
|
+
|
|
63
|
+
const setCoinConfig = (config: CoinConfig<T>): void => {
|
|
64
|
+
coinConfig = config
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const getCoinConfig = (currency?: CryptoCurrency): T => {
|
|
68
|
+
if (!coinConfig) {
|
|
69
|
+
throw new MissingCoinConfig()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return coinConfig(currency)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
setCoinConfig,
|
|
77
|
+
getCoinConfig,
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export default buildCoinConfig
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { parseCurrencyUnit } from '@ledgerhq/live-currency-format'
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature IDs as defined in https://ledgerhq.atlassian.net/wiki/spaces/CF/pages/6125551933/Coin+modules+-+ADR-003+-+Features+support
|
|
3
|
+
* These represent the capabilities/features a coin module can support
|
|
4
|
+
*/
|
|
5
|
+
export type FeatureId =
|
|
6
|
+
// Blockchain transaction features
|
|
7
|
+
| 'blockchain_txs'
|
|
8
|
+
// Staking features
|
|
9
|
+
| 'staking_txs'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Intent types for blockchain_txs feature
|
|
13
|
+
*/
|
|
14
|
+
export type BlockchainTxsIntent = 'send'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Intent types for staking_txs feature
|
|
18
|
+
*/
|
|
19
|
+
export type StakingTxsIntent = 'delegate' | 'undelegate' | 'redelegate' | 'claimReward' | 'withdraw'
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Mapping from feature ID to its supported intents
|
|
23
|
+
*/
|
|
24
|
+
export type FeatureIntentMap = {
|
|
25
|
+
blockchain_txs: BlockchainTxsIntent
|
|
26
|
+
staking_txs: StakingTxsIntent
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A supported feature declaration in a coin module
|
|
31
|
+
* Maps feature IDs to their supported intents
|
|
32
|
+
* Example: { "blockchain_txs": ["send"], "staking_txs": ["delegate", "claimReward"] }
|
|
33
|
+
*/
|
|
34
|
+
export type SupportedFeatures = Partial<Record<FeatureId, string[]>>
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Feature status in liveconfig
|
|
38
|
+
*/
|
|
39
|
+
export type FeatureStatus = 'active' | 'inactive'
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Feature configuration in liveconfig
|
|
43
|
+
*/
|
|
44
|
+
export type FeatureConfig = {
|
|
45
|
+
id: FeatureId
|
|
46
|
+
status: FeatureStatus
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Helper function to check if a feature is supported
|
|
51
|
+
*/
|
|
52
|
+
export function hasFeature(supportedFeatures: SupportedFeatures, featureId: FeatureId): boolean {
|
|
53
|
+
return featureId in supportedFeatures
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Helper function to check if an intent is supported for a feature
|
|
58
|
+
*/
|
|
59
|
+
export function hasIntent(
|
|
60
|
+
supportedFeatures: SupportedFeatures,
|
|
61
|
+
featureId: FeatureId,
|
|
62
|
+
intent: string
|
|
63
|
+
): boolean {
|
|
64
|
+
const intents = supportedFeatures[featureId]
|
|
65
|
+
return intents?.includes(intent) ?? false
|
|
66
|
+
}
|
package/src/index.ts
ADDED
package/src/setup.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recursively makes all properties optional.
|
|
3
|
+
* Arrays keep their element type (but each element is deeply partial).
|
|
4
|
+
* Primitives and built-in value types (Date, RegExp) are preserved as-is.
|
|
5
|
+
*/
|
|
6
|
+
export type DeepPartial<T> = T extends (infer U)[]
|
|
7
|
+
? DeepPartial<U>[]
|
|
8
|
+
: T extends Date | RegExp
|
|
9
|
+
? T
|
|
10
|
+
: T extends object
|
|
11
|
+
? { [K in keyof T]?: DeepPartial<T[K]> }
|
|
12
|
+
: T
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Re-types a function so its return value is wrapped in `Promise<DeepPartial<…>>`,
|
|
16
|
+
* while keeping the original parameter types intact.
|
|
17
|
+
*
|
|
18
|
+
* Useful for typing Jest mock functions that return partial RPC/API data:
|
|
19
|
+
* ```ts
|
|
20
|
+
* const mock = jest.fn() as jest.MockedFunction<DeepPartialReturn<SomeApi["method"]>>;
|
|
21
|
+
* mock.mockResolvedValue({ onlyTheFieldsWeNeed: true });
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export type DeepPartialReturn<F extends (...args: never[]) => unknown> = (
|
|
25
|
+
...args: Parameters<F>
|
|
26
|
+
) => Promise<DeepPartial<Awaited<ReturnType<F>>>>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import BigNumber from 'bignumber.js'
|
|
2
|
+
import { fromBigNumberToBigInt } from './utils'
|
|
3
|
+
|
|
4
|
+
describe('bigNumberToBigInt', () => {
|
|
5
|
+
it('should convert BigNumber to BigInt', () => {
|
|
6
|
+
const bigNumber = new BigNumber('42')
|
|
7
|
+
const result = fromBigNumberToBigInt(bigNumber)
|
|
8
|
+
expect(result).toEqual(BigInt('42'))
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('should return the default value if input is undefined', () => {
|
|
12
|
+
const value = undefined
|
|
13
|
+
const defaultValue = BigInt(12)
|
|
14
|
+
const result = fromBigNumberToBigInt(value, defaultValue)
|
|
15
|
+
expect(result).toEqual(defaultValue)
|
|
16
|
+
})
|
|
17
|
+
it('should convert to BigInt for very large numbers', () => {
|
|
18
|
+
const valueStr = '1234567890123456789012345678901234567890'
|
|
19
|
+
const valBignumber = new BigNumber(valueStr)
|
|
20
|
+
const valBigint = fromBigNumberToBigInt(valBignumber)
|
|
21
|
+
expect(valBigint).toEqual(BigInt(valueStr))
|
|
22
|
+
})
|
|
23
|
+
})
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import BigNumber from 'bignumber.js'
|
|
2
|
+
import { SendTransactionIntent, StakingTransactionIntent, TransactionIntent } from './api'
|
|
3
|
+
|
|
4
|
+
export function fromBigNumberToBigInt<T>(
|
|
5
|
+
bigNumber: BigNumber | undefined,
|
|
6
|
+
defaultValue?: T
|
|
7
|
+
): bigint | T {
|
|
8
|
+
if (bigNumber != null) {
|
|
9
|
+
return BigInt(bigNumber.toFixed())
|
|
10
|
+
}
|
|
11
|
+
return defaultValue as T
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function isSendTransactionIntent(tx: TransactionIntent): tx is SendTransactionIntent {
|
|
15
|
+
return tx.intentType === 'transaction'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function isStakingTransactionIntent(tx: TransactionIntent): tx is StakingTransactionIntent {
|
|
19
|
+
return tx.intentType === 'staking'
|
|
20
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"customConditions": []
|
|
5
|
+
},
|
|
6
|
+
"exclude": [
|
|
7
|
+
"**/*.test.ts",
|
|
8
|
+
"**/*.test.tsx",
|
|
9
|
+
"**/*.spec.ts",
|
|
10
|
+
"**/*.spec.tsx",
|
|
11
|
+
"**/__tests__/**/*",
|
|
12
|
+
"**/tests/**/*",
|
|
13
|
+
"**/*.test.js",
|
|
14
|
+
"**/*.test.jsx",
|
|
15
|
+
"**/*.spec.js",
|
|
16
|
+
"**/*.spec.jsx"
|
|
17
|
+
]
|
|
18
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../tsconfig.base",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"declaration": true,
|
|
5
|
+
"declarationMap": true,
|
|
6
|
+
"downlevelIteration": true,
|
|
7
|
+
"lib": [
|
|
8
|
+
"es2020",
|
|
9
|
+
"dom"
|
|
10
|
+
],
|
|
11
|
+
"types": [
|
|
12
|
+
"node",
|
|
13
|
+
"jest"
|
|
14
|
+
],
|
|
15
|
+
"outDir": "lib",
|
|
16
|
+
"exactOptionalPropertyTypes": true,
|
|
17
|
+
"typeRoots": [
|
|
18
|
+
"./types",
|
|
19
|
+
"./node_modules/@types"
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
"include": [
|
|
23
|
+
"src/**/*",
|
|
24
|
+
"types/*.d.ts"
|
|
25
|
+
]
|
|
26
|
+
}
|