crypto-aio 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/CHANGELOG.md +151 -1
- package/README.md +32 -12
- package/dist/adapters/evm/builder.js +64 -4
- package/dist/adapters/evm/builder.js.map +1 -1
- package/dist/adapters/evm/decode.js +6 -1
- package/dist/adapters/evm/decode.js.map +1 -1
- package/dist/adapters/evm/ethers-client.js +4 -0
- package/dist/adapters/evm/ethers-client.js.map +1 -1
- package/dist/adapters/evm/fees.d.ts +24 -0
- package/dist/adapters/evm/fees.js +47 -4
- package/dist/adapters/evm/fees.js.map +1 -1
- package/dist/adapters/evm/index.d.ts +1 -1
- package/dist/adapters/evm/index.js +2 -1
- package/dist/adapters/evm/index.js.map +1 -1
- package/dist/adapters/evm/network.d.ts +5 -0
- package/dist/adapters/evm/network.js +21 -2
- package/dist/adapters/evm/network.js.map +1 -1
- package/dist/adapters/evm/plugin.d.ts +5 -6
- package/dist/adapters/evm/plugin.js +50 -2
- package/dist/adapters/evm/plugin.js.map +1 -1
- package/dist/adapters/evm/proofs.js +17 -5
- package/dist/adapters/evm/proofs.js.map +1 -1
- package/dist/adapters/evm/reader.d.ts +18 -0
- package/dist/adapters/evm/reader.js +34 -0
- package/dist/adapters/evm/reader.js.map +1 -1
- package/dist/adapters/evm/tokens.d.ts +3 -0
- package/dist/adapters/evm/tokens.js.map +1 -1
- package/dist/adapters/evm/types.d.ts +10 -0
- package/dist/adapters/evm/types.js.map +1 -1
- package/dist/adapters/evm/web3-client.d.ts +6 -3
- package/dist/adapters/evm/web3-client.js +19 -2
- package/dist/adapters/evm/web3-client.js.map +1 -1
- package/dist/adapters/solana/builder.js +29 -18
- package/dist/adapters/solana/builder.js.map +1 -1
- package/dist/adapters/solana/fees.d.ts +12 -3
- package/dist/adapters/solana/fees.js +13 -4
- package/dist/adapters/solana/fees.js.map +1 -1
- package/dist/adapters/solana/message.d.ts +15 -0
- package/dist/adapters/solana/message.js +120 -0
- package/dist/adapters/solana/message.js.map +1 -0
- package/dist/adapters/solana/plugin.js +4 -1
- package/dist/adapters/solana/plugin.js.map +1 -1
- package/dist/adapters/solana/types.d.ts +8 -5
- package/dist/adapters/solana/types.js.map +1 -1
- package/dist/adapters/solana/web3.js +10 -32
- package/dist/adapters/solana/web3.js.map +1 -1
- package/dist/adapters/ton/proofs.js +18 -76
- package/dist/adapters/ton/proofs.js.map +1 -1
- package/dist/adapters/ton/reader.d.ts +45 -4
- package/dist/adapters/ton/reader.js +200 -5
- package/dist/adapters/ton/reader.js.map +1 -1
- package/dist/adapters/ton/trace.d.ts +26 -0
- package/dist/adapters/ton/trace.js +35 -9
- package/dist/adapters/ton/trace.js.map +1 -1
- package/dist/adapters/tron/abi.d.ts +2 -0
- package/dist/adapters/tron/abi.js +2 -0
- package/dist/adapters/tron/abi.js.map +1 -1
- package/dist/adapters/tron/builder.js +20 -4
- package/dist/adapters/tron/builder.js.map +1 -1
- package/dist/adapters/tron/decode.d.ts +2 -0
- package/dist/adapters/tron/decode.js +1 -1
- package/dist/adapters/tron/decode.js.map +1 -1
- package/dist/adapters/tron/proofs.js +1 -0
- package/dist/adapters/tron/proofs.js.map +1 -1
- package/dist/adapters/tron/reader.d.ts +10 -0
- package/dist/adapters/tron/reader.js +17 -0
- package/dist/adapters/tron/reader.js.map +1 -1
- package/dist/adapters/tron/tokens.d.ts +2 -0
- package/dist/adapters/tron/tokens.js.map +1 -1
- package/dist/adapters/utxo/builder.js +1 -1
- package/dist/adapters/utxo/builder.js.map +1 -1
- package/dist/adapters/utxo/chains.js +2 -1
- package/dist/adapters/utxo/chains.js.map +1 -1
- package/dist/adapters/utxo/context.d.ts +6 -0
- package/dist/adapters/utxo/context.js +8 -1
- package/dist/adapters/utxo/context.js.map +1 -1
- package/dist/adapters/utxo/decode.d.ts +8 -0
- package/dist/adapters/utxo/decode.js +36 -0
- package/dist/adapters/utxo/decode.js.map +1 -1
- package/dist/adapters/utxo/driver.js +5 -0
- package/dist/adapters/utxo/driver.js.map +1 -1
- package/dist/adapters/utxo/esplora.d.ts +23 -2
- package/dist/adapters/utxo/esplora.js +49 -2
- package/dist/adapters/utxo/esplora.js.map +1 -1
- package/dist/adapters/utxo/proofs.d.ts +15 -6
- package/dist/adapters/utxo/proofs.js +185 -47
- package/dist/adapters/utxo/proofs.js.map +1 -1
- package/dist/adapters/utxo/rawblock.d.ts +46 -0
- package/dist/adapters/utxo/rawblock.js +159 -0
- package/dist/adapters/utxo/rawblock.js.map +1 -0
- package/dist/adapters/utxo/rawtx.d.ts +14 -0
- package/dist/adapters/utxo/rawtx.js +44 -14
- package/dist/adapters/utxo/rawtx.js.map +1 -1
- package/dist/core/blockchain/handle.d.ts +6 -27
- package/dist/core/blockchain/handle.js +112 -112
- package/dist/core/blockchain/handle.js.map +1 -1
- package/dist/core/config/types.d.ts +15 -0
- package/dist/core/config/types.js.map +1 -1
- package/dist/core/container/container.d.ts +1 -8
- package/dist/core/container/container.js +42 -8
- package/dist/core/container/container.js.map +1 -1
- package/dist/core/driver/types.d.ts +34 -1
- package/dist/core/driver/types.js.map +1 -1
- package/dist/core/errors/codes.d.ts +10 -0
- package/dist/core/errors/codes.js +4 -0
- package/dist/core/errors/codes.js.map +1 -1
- package/dist/core/lifecycle/engine-rules.d.ts +3 -4
- package/dist/core/lifecycle/engine-rules.js +16 -6
- package/dist/core/lifecycle/engine-rules.js.map +1 -1
- package/dist/core/lifecycle/engine.d.ts +2 -0
- package/dist/core/lifecycle/engine.js +19 -1
- package/dist/core/lifecycle/engine.js.map +1 -1
- package/dist/core/lifecycle/evaluate.js +9 -4
- package/dist/core/lifecycle/evaluate.js.map +1 -1
- package/dist/core/lifecycle/monitor.js +16 -6
- package/dist/core/lifecycle/monitor.js.map +1 -1
- package/dist/core/lifecycle/reconcile.d.ts +1 -1
- package/dist/core/lifecycle/reconcile.js +1 -1
- package/dist/core/lifecycle/reconcile.js.map +1 -1
- package/dist/core/lifecycle/workers.d.ts +9 -3
- package/dist/core/lifecycle/workers.js +32 -14
- package/dist/core/lifecycle/workers.js.map +1 -1
- package/dist/core/model/ids.d.ts +1 -0
- package/dist/core/model/ids.js +1 -0
- package/dist/core/model/ids.js.map +1 -1
- package/dist/core/model/ordering.d.ts +8 -0
- package/dist/core/model/ordering.js.map +1 -1
- package/dist/core/model/transaction.d.ts +7 -0
- package/dist/core/model/transaction.js.map +1 -1
- package/dist/core/observe/scanner.js +3 -0
- package/dist/core/observe/scanner.js.map +1 -1
- package/dist/core/signing/wallet.js +8 -0
- package/dist/core/signing/wallet.js.map +1 -1
- package/dist/core/store/format.d.ts +20 -0
- package/dist/core/store/format.js +86 -0
- package/dist/core/store/format.js.map +1 -0
- package/dist/core/store/memory.js +2 -2
- package/dist/core/store/memory.js.map +1 -1
- package/dist/core/store/types.d.ts +15 -3
- package/dist/core/store/types.js +2 -0
- package/dist/core/store/types.js.map +1 -1
- package/dist/core/transport/http-transport.js +21 -13
- package/dist/core/transport/http-transport.js.map +1 -1
- package/dist/core/transport/types.d.ts +16 -1
- package/dist/core/transport/types.js.map +1 -1
- package/dist/core/util/json.d.ts +7 -1
- package/dist/core/util/json.js +13 -4
- package/dist/core/util/json.js.map +1 -1
- package/dist/core/util/names.d.ts +6 -0
- package/dist/core/util/names.js +10 -0
- package/dist/core/util/names.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/testing/contracts/operations.js +20 -1
- package/dist/testing/contracts/operations.js.map +1 -1
- package/dist/testing/env.d.ts +5 -0
- package/dist/testing/env.js +1 -1
- package/dist/testing/env.js.map +1 -1
- package/dist/testing/faulty-store.d.ts +2 -0
- package/dist/testing/faulty-store.js +5 -0
- package/dist/testing/faulty-store.js.map +1 -1
- package/package.json +17 -7
|
@@ -23,6 +23,8 @@ exports.ERROR_CODES = deepFreezeCodes({
|
|
|
23
23
|
TX_REFUSED: { category: 'chain', retryable: false },
|
|
24
24
|
TX_REJECTED: { category: 'chain', retryable: false },
|
|
25
25
|
TX_REVERTED: { category: 'chain', retryable: false },
|
|
26
|
+
/** Included and executed, but no transfer to the recipient shows: value may have moved. */
|
|
27
|
+
TX_UNEVIDENCED: { category: 'chain', retryable: false },
|
|
26
28
|
TX_EXPIRED: { category: 'chain', retryable: false },
|
|
27
29
|
TX_REPLACED: { category: 'chain', retryable: false },
|
|
28
30
|
SIGNER_UNAVAILABLE: { category: 'signing', retryable: false },
|
|
@@ -39,6 +41,8 @@ exports.ERROR_CODES = deepFreezeCodes({
|
|
|
39
41
|
/** The outcome may have happened (e.g. a delivered broadcast) but was not recorded. */
|
|
40
42
|
STATE_UNRECORDED: { category: 'state', retryable: true },
|
|
41
43
|
SCANNER_REORG_TOO_DEEP: { category: 'state', retryable: false },
|
|
44
|
+
/** A stored Operation's record format is newer than this version reads. */
|
|
45
|
+
RECORD_FORMAT_UNSUPPORTED: { category: 'state', retryable: false },
|
|
42
46
|
TIMEOUT: { category: 'timeout', retryable: true },
|
|
43
47
|
});
|
|
44
48
|
function deepFreezeCodes(codes) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codes.js","sourceRoot":"","sources":["../../../src/core/errors/codes.ts"],"names":[],"mappings":"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","sourcesContent":["export type ErrorCategory =\n | 'config'\n | 'unsupported'\n | 'validation'\n | 'provider'\n | 'chain'\n | 'signing'\n | 'state'\n | 'timeout';\n\ninterface CodeDefinition {\n readonly category: ErrorCategory;\n readonly retryable: boolean;\n}\n\n/** Frozen, with every entry: the table is shared by every error and every caller. */\nexport const ERROR_CODES = deepFreezeCodes({\n CONFIG_INVALID: { category: 'config', retryable: false },\n DEPENDENCY_MISSING: { category: 'config', retryable: false },\n INCOMPATIBLE_SELECTION: { category: 'config', retryable: false },\n UNSUPPORTED_CAPABILITY: { category: 'unsupported', retryable: false },\n INVALID_ADDRESS: { category: 'validation', retryable: false },\n INVALID_AMOUNT: { category: 'validation', retryable: false },\n ASSET_RESOLUTION: { category: 'validation', retryable: false },\n INVALID_INTENT: { category: 'validation', retryable: false },\n PROVIDER_UNAVAILABLE: { category: 'provider', retryable: true },\n RATE_LIMITED: { category: 'provider', retryable: true },\n PROVIDER_MISCONFIGURED: { category: 'provider', retryable: false },\n PROVIDER_INCONSISTENT: { category: 'provider', retryable: true },\n RPC_ERROR: { category: 'provider', retryable: false },\n INSUFFICIENT_FUNDS: { category: 'chain', retryable: false },\n NONCE_CONFLICT: { category: 'chain', retryable: false },\n NONCE_TOO_HIGH: { category: 'chain', retryable: false },\n FEE_TOO_LOW: { category: 'chain', retryable: false },\n TX_REFUSED: { category: 'chain', retryable: false },\n TX_REJECTED: { category: 'chain', retryable: false },\n TX_REVERTED: { category: 'chain', retryable: false },\n TX_EXPIRED: { category: 'chain', retryable: false },\n TX_REPLACED: { category: 'chain', retryable: false },\n SIGNER_UNAVAILABLE: { category: 'signing', retryable: false },\n SIGNING_FAILED: { category: 'signing', retryable: false },\n SIGNATURE_MISMATCH: { category: 'signing', retryable: false },\n POLICY_REJECTED: { category: 'signing', retryable: false },\n KEY_NOT_EXPORTABLE: { category: 'signing', retryable: false },\n IDEMPOTENCY_CONFLICT: { category: 'state', retryable: false },\n FENCING: { category: 'state', retryable: false },\n VERSION_CONFLICT: { category: 'state', retryable: true },\n INVALID_TRANSITION: { category: 'state', retryable: false },\n NOT_FOUND: { category: 'state', retryable: false },\n SEQUENCE_BUSY: { category: 'state', retryable: true },\n /** The outcome may have happened (e.g. a delivered broadcast) but was not recorded. */\n STATE_UNRECORDED: { category: 'state', retryable: true },\n SCANNER_REORG_TOO_DEEP: { category: 'state', retryable: false },\n TIMEOUT: { category: 'timeout', retryable: true },\n} as const satisfies Record<string, CodeDefinition>);\n\nfunction deepFreezeCodes<T extends Record<string, CodeDefinition>>(codes: T): T {\n for (const definition of Object.values(codes)) Object.freeze(definition);\n return Object.freeze(codes);\n}\n\nexport type ErrorCode = keyof typeof ERROR_CODES;\n\nexport type CodesOf<C extends ErrorCategory> = {\n [K in ErrorCode]: (typeof ERROR_CODES)[K]['category'] extends C ? K : never;\n}[ErrorCode];\n"]}
|
|
1
|
+
{"version":3,"file":"codes.js","sourceRoot":"","sources":["../../../src/core/errors/codes.ts"],"names":[],"mappings":"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","sourcesContent":["export type ErrorCategory =\n | 'config'\n | 'unsupported'\n | 'validation'\n | 'provider'\n | 'chain'\n | 'signing'\n | 'state'\n | 'timeout';\n\ninterface CodeDefinition {\n readonly category: ErrorCategory;\n readonly retryable: boolean;\n}\n\n/** Frozen, with every entry: the table is shared by every error and every caller. */\nexport const ERROR_CODES = deepFreezeCodes({\n CONFIG_INVALID: { category: 'config', retryable: false },\n DEPENDENCY_MISSING: { category: 'config', retryable: false },\n INCOMPATIBLE_SELECTION: { category: 'config', retryable: false },\n UNSUPPORTED_CAPABILITY: { category: 'unsupported', retryable: false },\n INVALID_ADDRESS: { category: 'validation', retryable: false },\n INVALID_AMOUNT: { category: 'validation', retryable: false },\n ASSET_RESOLUTION: { category: 'validation', retryable: false },\n INVALID_INTENT: { category: 'validation', retryable: false },\n PROVIDER_UNAVAILABLE: { category: 'provider', retryable: true },\n RATE_LIMITED: { category: 'provider', retryable: true },\n PROVIDER_MISCONFIGURED: { category: 'provider', retryable: false },\n PROVIDER_INCONSISTENT: { category: 'provider', retryable: true },\n RPC_ERROR: { category: 'provider', retryable: false },\n INSUFFICIENT_FUNDS: { category: 'chain', retryable: false },\n NONCE_CONFLICT: { category: 'chain', retryable: false },\n NONCE_TOO_HIGH: { category: 'chain', retryable: false },\n FEE_TOO_LOW: { category: 'chain', retryable: false },\n TX_REFUSED: { category: 'chain', retryable: false },\n TX_REJECTED: { category: 'chain', retryable: false },\n TX_REVERTED: { category: 'chain', retryable: false },\n /** Included and executed, but no transfer to the recipient shows: value may have moved. */\n TX_UNEVIDENCED: { category: 'chain', retryable: false },\n TX_EXPIRED: { category: 'chain', retryable: false },\n TX_REPLACED: { category: 'chain', retryable: false },\n SIGNER_UNAVAILABLE: { category: 'signing', retryable: false },\n SIGNING_FAILED: { category: 'signing', retryable: false },\n SIGNATURE_MISMATCH: { category: 'signing', retryable: false },\n POLICY_REJECTED: { category: 'signing', retryable: false },\n KEY_NOT_EXPORTABLE: { category: 'signing', retryable: false },\n IDEMPOTENCY_CONFLICT: { category: 'state', retryable: false },\n FENCING: { category: 'state', retryable: false },\n VERSION_CONFLICT: { category: 'state', retryable: true },\n INVALID_TRANSITION: { category: 'state', retryable: false },\n NOT_FOUND: { category: 'state', retryable: false },\n SEQUENCE_BUSY: { category: 'state', retryable: true },\n /** The outcome may have happened (e.g. a delivered broadcast) but was not recorded. */\n STATE_UNRECORDED: { category: 'state', retryable: true },\n SCANNER_REORG_TOO_DEEP: { category: 'state', retryable: false },\n /** A stored Operation's record format is newer than this version reads. */\n RECORD_FORMAT_UNSUPPORTED: { category: 'state', retryable: false },\n TIMEOUT: { category: 'timeout', retryable: true },\n} as const satisfies Record<string, CodeDefinition>);\n\nfunction deepFreezeCodes<T extends Record<string, CodeDefinition>>(codes: T): T {\n for (const definition of Object.values(codes)) Object.freeze(definition);\n return Object.freeze(codes);\n}\n\nexport type ErrorCode = keyof typeof ERROR_CODES;\n\nexport type CodesOf<C extends ErrorCategory> = {\n [K in ErrorCode]: (typeof ERROR_CODES)[K]['category'] extends C ? K : never;\n}[ErrorCode];\n"]}
|
|
@@ -72,11 +72,10 @@ export declare function feeOf(attempt: AttemptRecord): bigint;
|
|
|
72
72
|
* The cancel a new cancel is bumped from: among the earlier cancels that superseded
|
|
73
73
|
* the active Attempt `previous` (refused ones gave it the active role back), the one paying
|
|
74
74
|
* the most, so repeated bumps climb; otherwise `previous` itself, so a cancel never starts
|
|
75
|
-
* below a newer, higher replacement. Fees are compared only between cancels
|
|
76
|
-
*
|
|
77
|
-
* repeat cancel may need an explicit `fee`.
|
|
75
|
+
* below a newer, higher replacement. Fees are compared only between cancels: by the
|
|
76
|
+
* driver's `comparePrice` when it has one, otherwise by total charges.
|
|
78
77
|
*/
|
|
79
|
-
export declare function cancelBase(op: OperationRecord, previous: AttemptRecord): AttemptRecord;
|
|
78
|
+
export declare function cancelBase(op: OperationRecord, previous: AttemptRecord, comparePrice?: (a: UnsignedTx, b: UnsignedTx) => number): AttemptRecord;
|
|
80
79
|
/** The same `FeeSpeed` name, or a canonically equal `FeeOverride`. */
|
|
81
80
|
export declare function sameFeeSpec(attempt: AttemptRecord, fee: FeeSpeed | FeeOverride | undefined): boolean;
|
|
82
81
|
/**
|
|
@@ -25,6 +25,7 @@ exports.LIFECYCLE_DEFAULTS = {
|
|
|
25
25
|
droppedGracePeriodMs: 120_000,
|
|
26
26
|
rebroadcastIntervalMs: 60_000,
|
|
27
27
|
leaseMs: 30_000,
|
|
28
|
+
leaseWaitMs: 30_000,
|
|
28
29
|
claimLeaseMs: 60_000,
|
|
29
30
|
waitTimeoutMs: 600_000,
|
|
30
31
|
requireIdempotencyKey: false,
|
|
@@ -36,7 +37,15 @@ function withLifecycleDefaults(options) {
|
|
|
36
37
|
for (const [key, value] of Object.entries(options))
|
|
37
38
|
if (value !== undefined)
|
|
38
39
|
out[key] = value;
|
|
39
|
-
|
|
40
|
+
// Waiting for the lease follows its length unless set on its own.
|
|
41
|
+
if (options.leaseWaitMs === undefined)
|
|
42
|
+
out.leaseWaitMs = out.leaseMs;
|
|
43
|
+
const { leaseWaitMs, signTimeoutMs } = out;
|
|
44
|
+
if (typeof leaseWaitMs !== 'number' ||
|
|
45
|
+
!Number.isFinite(leaseWaitMs) ||
|
|
46
|
+
leaseWaitMs < 0) {
|
|
47
|
+
throw new error_1.ConfigError('CONFIG_INVALID', 'lifecycle.leaseWaitMs must be a finite number of 0 or more');
|
|
48
|
+
}
|
|
40
49
|
if (typeof signTimeoutMs !== 'number' ||
|
|
41
50
|
!Number.isFinite(signTimeoutMs) ||
|
|
42
51
|
signTimeoutMs <= 0) {
|
|
@@ -138,6 +147,7 @@ function heldNonces(op) {
|
|
|
138
147
|
/** Failures proven on chain at finality: their nonce was consumed. */
|
|
139
148
|
exports.CONSUMED_FAILURES = new Set([
|
|
140
149
|
'TX_REVERTED',
|
|
150
|
+
'TX_UNEVIDENCED',
|
|
141
151
|
'TX_REPLACED',
|
|
142
152
|
]);
|
|
143
153
|
/** Whether some value in [from, next) is neither released nor held. */
|
|
@@ -178,16 +188,16 @@ function feeOf(attempt) {
|
|
|
178
188
|
* The cancel a new cancel is bumped from: among the earlier cancels that superseded
|
|
179
189
|
* the active Attempt `previous` (refused ones gave it the active role back), the one paying
|
|
180
190
|
* the most, so repeated bumps climb; otherwise `previous` itself, so a cancel never starts
|
|
181
|
-
* below a newer, higher replacement. Fees are compared only between cancels
|
|
182
|
-
*
|
|
183
|
-
* repeat cancel may need an explicit `fee`.
|
|
191
|
+
* below a newer, higher replacement. Fees are compared only between cancels: by the
|
|
192
|
+
* driver's `comparePrice` when it has one, otherwise by total charges.
|
|
184
193
|
*/
|
|
185
|
-
function cancelBase(op, previous) {
|
|
194
|
+
function cancelBase(op, previous, comparePrice) {
|
|
195
|
+
const higher = (a, b) => comparePrice ? comparePrice(a.unsigned, b.unsigned) > 0 : feeOf(a) > feeOf(b);
|
|
186
196
|
let best;
|
|
187
197
|
for (const attempt of op.attempts) {
|
|
188
198
|
if (attempt.purpose !== 'cancel' || attempt.supersedes !== previous.id)
|
|
189
199
|
continue;
|
|
190
|
-
if (!best ||
|
|
200
|
+
if (!best || higher(attempt, best))
|
|
191
201
|
best = attempt;
|
|
192
202
|
}
|
|
193
203
|
return best ?? previous;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine-rules.js","sourceRoot":"","sources":["../../../src/core/lifecycle/engine-rules.ts"],"names":[],"mappings":"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","sourcesContent":["import type { LifecycleOptions } from '../config/types';\nimport {\n ChainError,\n ConfigError,\n createError,\n isCryptoAioError,\n type CryptoAioError,\n type ErrorCode,\n type SerializedError,\n} from '../errors/error';\nimport type { FeeOverride, FeeSpeed } from '../model/fee';\nimport type { OrderingData, OrderingKind } from '../model/ordering';\nimport type { TxState, UnsignedTx } from '../model/transaction';\nimport { sanitizeError } from '../secret/redact';\nimport type {\n AttemptObservation,\n AttemptRecord,\n OperationRecord,\n OperationState,\n SequenceState,\n} from '../store/types';\nimport { canonicalJson } from '../util/json';\nimport type { ObservationPatch } from './observations';\n\n/*\n * The engine's pure rules: lifecycle defaults, error serialization, and the state\n * sets and predicates `OperationEngine` decides with. No I/O and no engine state.\n */\n\nexport type ResolvedLifecycle = Required<LifecycleOptions>;\n\nexport const LIFECYCLE_DEFAULTS: ResolvedLifecycle = {\n pollIntervalMs: 5_000,\n droppedGracePeriodMs: 120_000,\n rebroadcastIntervalMs: 60_000,\n leaseMs: 30_000,\n claimLeaseMs: 60_000,\n waitTimeoutMs: 600_000,\n requireIdempotencyKey: false,\n broadcastFanout: 1,\n signTimeoutMs: 120_000,\n};\n\nexport function withLifecycleDefaults(options: LifecycleOptions): ResolvedLifecycle {\n const out: Record<string, unknown> = { ...LIFECYCLE_DEFAULTS };\n for (const [key, value] of Object.entries(options))\n if (value !== undefined) out[key] = value;\n const { signTimeoutMs } = out as ResolvedLifecycle;\n if (\n typeof signTimeoutMs !== 'number' ||\n !Number.isFinite(signTimeoutMs) ||\n signTimeoutMs <= 0\n ) {\n throw new ConfigError(\n 'CONFIG_INVALID',\n 'lifecycle.signTimeoutMs must be a finite number greater than 0',\n );\n }\n return out as ResolvedLifecycle;\n}\n\nexport const PRE_SIGNING_STATES: ReadonlySet<OperationState> = new Set<OperationState>([\n 'created',\n 'prepared',\n 'awaiting-signature',\n]);\n\nexport const LEASED_ORDERINGS = new Set(['nonce', 'seqno', 'inputs']);\nexport const DEFINITIVE_CATEGORIES = new Set([\n 'validation',\n 'chain',\n 'unsupported',\n 'config',\n 'signing',\n]);\n\n/**\n * A crypto-aio error as it is; anything else under `fallback`, the most specific\n * existing code for the caller's context (there is no generic code), with a sanitized message.\n */\nexport function serializeError(error: unknown, fallback: ErrorCode): SerializedError {\n return (\n isCryptoAioError(error) ? error : createError(fallback, sanitizeError(error).message)\n ).toJSON();\n}\n\nexport function rehydrateError(serialized: SerializedError, operationId: string): Error {\n return createError(serialized.code, serialized.message, {\n context: { ...serialized.context, operationId },\n ...(serialized.details ? { details: serialized.details } : {}),\n retryable: serialized.retryable,\n ambiguous: serialized.ambiguous,\n });\n}\n\n/**\n * The error's crypto-aio code, or `'UNKNOWN'`. Logs carry this, never the error itself:\n * messages may hold addresses, amounts or store detail.\n */\nexport function errorCode(error: unknown): string {\n return isCryptoAioError(error) ? error.code : 'UNKNOWN';\n}\n\nexport function isDefinitive(error: unknown): error is CryptoAioError {\n return (\n isCryptoAioError(error) &&\n !error.retryable &&\n DEFINITIVE_CATEGORIES.has(error.category)\n );\n}\n\n/**\n * Observation states backed by chain evidence: the Attempt was mined, or its slot or expiry\n * was consumed. No broadcast answer ever overwrites them; `replacedBy` is kept.\n */\nexport const CHAIN_EVIDENCE_STATES: ReadonlySet<TxState> = new Set<TxState>([\n 'included',\n 'final',\n 'failed',\n 'replaced',\n 'expired',\n]);\n\n/**\n * A node once accepted these bytes, or they may be live. `firstSeenAt` is the durable\n * marker: set when a node accepted them or the monitor saw them held, and kept by a refusal\n * that found them possibly live. A `pending`, `mempool` or `dropped` observation also\n * counts: one left by an accepted or ambiguous send, and the `pending` the monitor\n * writes for a signed Attempt that was never broadcast, which errs in the safe direction\n * (a genuine rejection then stalls it and keeps its nonce instead of failing it). A later\n * rejection says nothing about bytes that may still sit in a mempool, so it is only a\n * refusal: never terminal, the nonce is kept.\n */\nexport function mayBeLive(observation: AttemptObservation | null): boolean {\n return (\n observation !== null &&\n (observation.firstSeenAt !== undefined ||\n observation.state === 'pending' ||\n observation.state === 'mempool' ||\n observation.state === 'dropped')\n );\n}\n\n/**\n * A send that may have delivered the bytes (accepted, already known or ambiguous): a\n * missing, `refused` or `dropped` observation becomes `pending`, and a refusal's reason no\n * longer applies. Stronger evidence (chain or proven states) is left alone. The one rule\n * for every such send, so they cannot drift apart. Known gap: an ambiguous send clears a\n * refusal and its reason, so the monitor then shows bytes the node still refuses as\n * `dropped`, not `refused`; only a resend through the engine (a same-key repeat,\n * `rebroadcast`, recovery) records the refusal again.\n */\nexport function pendingAfterSend(current: AttemptObservation | null): ObservationPatch {\n return current === null || current.state === 'refused' || current.state === 'dropped'\n ? { state: 'pending', reason: undefined }\n : {};\n}\n\n/** Every nonce a live Operation holds: its reservation, unsigned payload and Attempts. */\nexport function heldNonces(op: OperationRecord): bigint[] {\n const orderings = [\n op.reservation,\n op.unsigned?.ordering,\n ...op.attempts.map((a) => a.ordering),\n ];\n return orderings.flatMap((o) => (o?.kind === 'nonce' ? [o.nonce] : []));\n}\n\n/** Failures proven on chain at finality: their nonce was consumed. */\nexport const CONSUMED_FAILURES: ReadonlySet<string> = new Set([\n 'TX_REVERTED',\n 'TX_REPLACED',\n]);\n\n/** Whether some value in [from, next) is neither released nor held. */\nexport function hasReclaimable(\n state: SequenceState | null,\n from: bigint,\n held: readonly bigint[],\n): boolean {\n if (!state) return false;\n const taken = new Set([...state.released, ...held]);\n for (let value = from; value < state.next; value++) if (!taken.has(value)) return true;\n return false;\n}\n\n/** `signed`, or `submitted` with an unknown broadcast outcome: its stored bytes are (re)sent. */\nexport function awaitsBroadcast(op: OperationRecord): boolean {\n return op.state === 'signed' || (op.state === 'submitted' && op.ambiguous === true);\n}\n\n/** The node's own answer (refused, rejected): not ambiguous, so the bytes were not taken. */\nexport function isRefusal(error: unknown): boolean {\n return isCryptoAioError(error) && error.category === 'chain' && !error.ambiguous;\n}\n\n/**\n * A replacement records the fee spec it was asked for (plain data, as every store record\n * must be) in its fee details, so a repeat of the same request is recognised instead of\n * signed again. The core adds it after `build`, so a driver never sets or reads it: its\n * `assemble` reads only the fee fields it named itself.\n */\nexport function withRequestedFee(\n unsigned: UnsignedTx,\n fee: FeeSpeed | FeeOverride,\n): UnsignedTx {\n return {\n ...unsigned,\n fee: { ...unsigned.fee, details: { ...unsigned.fee.details, requestedFee: fee } },\n };\n}\n\n/** An Attempt's total fee, summed over its charges. */\nexport function feeOf(attempt: AttemptRecord): bigint {\n return attempt.fee.charges.reduce((sum, charge) => sum + charge.amount, 0n);\n}\n\n/**\n * The cancel a new cancel is bumped from: among the earlier cancels that superseded\n * the active Attempt `previous` (refused ones gave it the active role back), the one paying\n * the most, so repeated bumps climb; otherwise `previous` itself, so a cancel never starts\n * below a newer, higher replacement. Fees are compared only between cancels. Known gap:\n * they are ranked by total charges, not by a price the driver compares, so on EVM a\n * repeat cancel may need an explicit `fee`.\n */\nexport function cancelBase(op: OperationRecord, previous: AttemptRecord): AttemptRecord {\n let best: AttemptRecord | undefined;\n for (const attempt of op.attempts) {\n if (attempt.purpose !== 'cancel' || attempt.supersedes !== previous.id) continue;\n if (!best || feeOf(attempt) > feeOf(best)) best = attempt;\n }\n return best ?? previous;\n}\n\n/** The same `FeeSpeed` name, or a canonically equal `FeeOverride`. */\nexport function sameFeeSpec(\n attempt: AttemptRecord,\n fee: FeeSpeed | FeeOverride | undefined,\n): boolean {\n const recorded = attempt.fee.details.requestedFee;\n if (recorded === undefined || fee === undefined) return false;\n try {\n return canonicalJson(recorded) === canonicalJson(fee);\n } catch {\n return false;\n }\n}\n\n/**\n * What `restoreAfterRefusal` puts back: the Operation as the refused Attempt found it.\n * Without `state`, only the active Attempt is swapped (state and error are kept).\n */\nexport interface RestorePoint {\n readonly state?: OperationState;\n readonly activeAttemptId: string;\n readonly error?: SerializedError;\n readonly ambiguous?: boolean;\n}\n\n/** A node's refusal or rejection of these bytes; both are handled alike. */\nexport const NODE_REFUSED_STATES: ReadonlySet<TxState> = new Set<TxState>([\n 'refused',\n 'rejected',\n]);\n\n/** Observations under which a superseded Attempt may still be live on the network. */\nexport const LIVE_STATES: ReadonlySet<TxState> = new Set<TxState>([\n 'pending',\n 'mempool',\n 'included',\n]);\n\n/** The states in which a replacement or cancel may supersede the active Attempt. */\nexport const CONFLICTABLE_STATES: ReadonlySet<OperationState> = new Set<OperationState>([\n 'submitted',\n 'stalled',\n]);\n/** The only state `rebuild` reopens. */\nexport const REBUILDABLE_STATES: ReadonlySet<OperationState> = new Set<OperationState>([\n 'expired',\n]);\n/** Where the stored bytes of a new Attempt whose broadcast never completed are resent. */\nexport const RESUMABLE_STATES: ReadonlySet<OperationState> = new Set<OperationState>([\n 'signed',\n 'submitted',\n 'stalled',\n]);\n\n/**\n * The built transaction must use exactly the slot the engine reserved: the ordering\n * kind of the driver, the allocated nonce or seqno, and no input held by another live\n * Operation. Anything else would persist a reservation nobody allocated.\n */\nexport function assertBuiltOrdering(\n operationId: string,\n kind: OrderingKind,\n allocated: OrderingData | undefined,\n excluded: readonly string[] | undefined,\n built: OrderingData,\n): void {\n // No existing code names an input conflict without implying a nonce, so NONCE_CONFLICT\n // (the ordering-slot conflict code) is kept and the message says what actually clashed.\n if (built.kind === kind && built.kind === 'inputs') {\n if (built.inputs.some((input) => excluded?.includes(input))) {\n throw new ChainError(\n 'NONCE_CONFLICT',\n 'input reservation conflict: the built transaction spends an input held by another operation',\n { context: { operationId } },\n );\n }\n return;\n }\n const matches =\n built.kind === kind &&\n (built.kind === 'nonce'\n ? allocated?.kind === 'nonce' && built.nonce === allocated.nonce\n : built.kind === 'seqno'\n ? allocated?.kind === 'seqno' && built.seqno === allocated.seqno\n : true);\n if (!matches) {\n throw new ChainError(\n 'NONCE_CONFLICT',\n 'the built transaction does not use the ordering slot reserved for it',\n { context: { operationId } },\n );\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"engine-rules.js","sourceRoot":"","sources":["../../../src/core/lifecycle/engine-rules.ts"],"names":[],"mappings":"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","sourcesContent":["import type { LifecycleOptions } from '../config/types';\nimport {\n ChainError,\n ConfigError,\n createError,\n isCryptoAioError,\n type CryptoAioError,\n type ErrorCode,\n type SerializedError,\n} from '../errors/error';\nimport type { FeeOverride, FeeSpeed } from '../model/fee';\nimport type { OrderingData, OrderingKind } from '../model/ordering';\nimport type { TxState, UnsignedTx } from '../model/transaction';\nimport { sanitizeError } from '../secret/redact';\nimport type {\n AttemptObservation,\n AttemptRecord,\n OperationRecord,\n OperationState,\n SequenceState,\n} from '../store/types';\nimport { canonicalJson } from '../util/json';\nimport type { ObservationPatch } from './observations';\n\n/*\n * The engine's pure rules: lifecycle defaults, error serialization, and the state\n * sets and predicates `OperationEngine` decides with. No I/O and no engine state.\n */\n\nexport type ResolvedLifecycle = Required<LifecycleOptions>;\n\nexport const LIFECYCLE_DEFAULTS: ResolvedLifecycle = {\n pollIntervalMs: 5_000,\n droppedGracePeriodMs: 120_000,\n rebroadcastIntervalMs: 60_000,\n leaseMs: 30_000,\n leaseWaitMs: 30_000,\n claimLeaseMs: 60_000,\n waitTimeoutMs: 600_000,\n requireIdempotencyKey: false,\n broadcastFanout: 1,\n signTimeoutMs: 120_000,\n};\n\nexport function withLifecycleDefaults(options: LifecycleOptions): ResolvedLifecycle {\n const out: Record<string, unknown> = { ...LIFECYCLE_DEFAULTS };\n for (const [key, value] of Object.entries(options))\n if (value !== undefined) out[key] = value;\n // Waiting for the lease follows its length unless set on its own.\n if (options.leaseWaitMs === undefined) out.leaseWaitMs = out.leaseMs;\n const { leaseWaitMs, signTimeoutMs } = out as ResolvedLifecycle;\n if (\n typeof leaseWaitMs !== 'number' ||\n !Number.isFinite(leaseWaitMs) ||\n leaseWaitMs < 0\n ) {\n throw new ConfigError(\n 'CONFIG_INVALID',\n 'lifecycle.leaseWaitMs must be a finite number of 0 or more',\n );\n }\n if (\n typeof signTimeoutMs !== 'number' ||\n !Number.isFinite(signTimeoutMs) ||\n signTimeoutMs <= 0\n ) {\n throw new ConfigError(\n 'CONFIG_INVALID',\n 'lifecycle.signTimeoutMs must be a finite number greater than 0',\n );\n }\n return out as ResolvedLifecycle;\n}\n\nexport const PRE_SIGNING_STATES: ReadonlySet<OperationState> = new Set<OperationState>([\n 'created',\n 'prepared',\n 'awaiting-signature',\n]);\n\nexport const LEASED_ORDERINGS = new Set(['nonce', 'seqno', 'inputs']);\nexport const DEFINITIVE_CATEGORIES = new Set([\n 'validation',\n 'chain',\n 'unsupported',\n 'config',\n 'signing',\n]);\n\n/**\n * A crypto-aio error as it is; anything else under `fallback`, the most specific\n * existing code for the caller's context (there is no generic code), with a sanitized message.\n */\nexport function serializeError(error: unknown, fallback: ErrorCode): SerializedError {\n return (\n isCryptoAioError(error) ? error : createError(fallback, sanitizeError(error).message)\n ).toJSON();\n}\n\nexport function rehydrateError(serialized: SerializedError, operationId: string): Error {\n return createError(serialized.code, serialized.message, {\n context: { ...serialized.context, operationId },\n ...(serialized.details ? { details: serialized.details } : {}),\n retryable: serialized.retryable,\n ambiguous: serialized.ambiguous,\n });\n}\n\n/**\n * The error's crypto-aio code, or `'UNKNOWN'`. Logs carry this, never the error itself:\n * messages may hold addresses, amounts or store detail.\n */\nexport function errorCode(error: unknown): string {\n return isCryptoAioError(error) ? error.code : 'UNKNOWN';\n}\n\nexport function isDefinitive(error: unknown): error is CryptoAioError {\n return (\n isCryptoAioError(error) &&\n !error.retryable &&\n DEFINITIVE_CATEGORIES.has(error.category)\n );\n}\n\n/**\n * Observation states backed by chain evidence: the Attempt was mined, or its slot or expiry\n * was consumed. No broadcast answer ever overwrites them; `replacedBy` is kept.\n */\nexport const CHAIN_EVIDENCE_STATES: ReadonlySet<TxState> = new Set<TxState>([\n 'included',\n 'final',\n 'failed',\n 'replaced',\n 'expired',\n]);\n\n/**\n * A node once accepted these bytes, or they may be live. `firstSeenAt` is the durable\n * marker: set when a node accepted them or the monitor saw them held, and kept by a refusal\n * that found them possibly live. A `pending`, `mempool` or `dropped` observation also\n * counts: one left by an accepted or ambiguous send, and the `pending` the monitor\n * writes for a signed Attempt that was never broadcast, which errs in the safe direction\n * (a genuine rejection then stalls it and keeps its nonce instead of failing it). A later\n * rejection says nothing about bytes that may still sit in a mempool, so it is only a\n * refusal: never terminal, the nonce is kept.\n */\nexport function mayBeLive(observation: AttemptObservation | null): boolean {\n return (\n observation !== null &&\n (observation.firstSeenAt !== undefined ||\n observation.state === 'pending' ||\n observation.state === 'mempool' ||\n observation.state === 'dropped')\n );\n}\n\n/**\n * A send that may have delivered the bytes (accepted, already known or ambiguous): a\n * missing, `refused` or `dropped` observation becomes `pending`, and a refusal's reason no\n * longer applies. Stronger evidence (chain or proven states) is left alone. The one rule\n * for every such send, so they cannot drift apart. Known gap: an ambiguous send clears a\n * refusal and its reason, so the monitor then shows bytes the node still refuses as\n * `dropped`, not `refused`; only a resend through the engine (a same-key repeat,\n * `rebroadcast`, recovery) records the refusal again.\n */\nexport function pendingAfterSend(current: AttemptObservation | null): ObservationPatch {\n return current === null || current.state === 'refused' || current.state === 'dropped'\n ? { state: 'pending', reason: undefined }\n : {};\n}\n\n/** Every nonce a live Operation holds: its reservation, unsigned payload and Attempts. */\nexport function heldNonces(op: OperationRecord): bigint[] {\n const orderings = [\n op.reservation,\n op.unsigned?.ordering,\n ...op.attempts.map((a) => a.ordering),\n ];\n return orderings.flatMap((o) => (o?.kind === 'nonce' ? [o.nonce] : []));\n}\n\n/** Failures proven on chain at finality: their nonce was consumed. */\nexport const CONSUMED_FAILURES: ReadonlySet<string> = new Set([\n 'TX_REVERTED',\n 'TX_UNEVIDENCED',\n 'TX_REPLACED',\n]);\n\n/** Whether some value in [from, next) is neither released nor held. */\nexport function hasReclaimable(\n state: SequenceState | null,\n from: bigint,\n held: readonly bigint[],\n): boolean {\n if (!state) return false;\n const taken = new Set([...state.released, ...held]);\n for (let value = from; value < state.next; value++) if (!taken.has(value)) return true;\n return false;\n}\n\n/** `signed`, or `submitted` with an unknown broadcast outcome: its stored bytes are (re)sent. */\nexport function awaitsBroadcast(op: OperationRecord): boolean {\n return op.state === 'signed' || (op.state === 'submitted' && op.ambiguous === true);\n}\n\n/** The node's own answer (refused, rejected): not ambiguous, so the bytes were not taken. */\nexport function isRefusal(error: unknown): boolean {\n return isCryptoAioError(error) && error.category === 'chain' && !error.ambiguous;\n}\n\n/**\n * A replacement records the fee spec it was asked for (plain data, as every store record\n * must be) in its fee details, so a repeat of the same request is recognised instead of\n * signed again. The core adds it after `build`, so a driver never sets or reads it: its\n * `assemble` reads only the fee fields it named itself.\n */\nexport function withRequestedFee(\n unsigned: UnsignedTx,\n fee: FeeSpeed | FeeOverride,\n): UnsignedTx {\n return {\n ...unsigned,\n fee: { ...unsigned.fee, details: { ...unsigned.fee.details, requestedFee: fee } },\n };\n}\n\n/** An Attempt's total fee, summed over its charges. */\nexport function feeOf(attempt: AttemptRecord): bigint {\n return attempt.fee.charges.reduce((sum, charge) => sum + charge.amount, 0n);\n}\n\n/**\n * The cancel a new cancel is bumped from: among the earlier cancels that superseded\n * the active Attempt `previous` (refused ones gave it the active role back), the one paying\n * the most, so repeated bumps climb; otherwise `previous` itself, so a cancel never starts\n * below a newer, higher replacement. Fees are compared only between cancels: by the\n * driver's `comparePrice` when it has one, otherwise by total charges.\n */\nexport function cancelBase(\n op: OperationRecord,\n previous: AttemptRecord,\n comparePrice?: (a: UnsignedTx, b: UnsignedTx) => number,\n): AttemptRecord {\n const higher = (a: AttemptRecord, b: AttemptRecord) =>\n comparePrice ? comparePrice(a.unsigned, b.unsigned) > 0 : feeOf(a) > feeOf(b);\n let best: AttemptRecord | undefined;\n for (const attempt of op.attempts) {\n if (attempt.purpose !== 'cancel' || attempt.supersedes !== previous.id) continue;\n if (!best || higher(attempt, best)) best = attempt;\n }\n return best ?? previous;\n}\n\n/** The same `FeeSpeed` name, or a canonically equal `FeeOverride`. */\nexport function sameFeeSpec(\n attempt: AttemptRecord,\n fee: FeeSpeed | FeeOverride | undefined,\n): boolean {\n const recorded = attempt.fee.details.requestedFee;\n if (recorded === undefined || fee === undefined) return false;\n try {\n return canonicalJson(recorded) === canonicalJson(fee);\n } catch {\n return false;\n }\n}\n\n/**\n * What `restoreAfterRefusal` puts back: the Operation as the refused Attempt found it.\n * Without `state`, only the active Attempt is swapped (state and error are kept).\n */\nexport interface RestorePoint {\n readonly state?: OperationState;\n readonly activeAttemptId: string;\n readonly error?: SerializedError;\n readonly ambiguous?: boolean;\n}\n\n/** A node's refusal or rejection of these bytes; both are handled alike. */\nexport const NODE_REFUSED_STATES: ReadonlySet<TxState> = new Set<TxState>([\n 'refused',\n 'rejected',\n]);\n\n/** Observations under which a superseded Attempt may still be live on the network. */\nexport const LIVE_STATES: ReadonlySet<TxState> = new Set<TxState>([\n 'pending',\n 'mempool',\n 'included',\n]);\n\n/** The states in which a replacement or cancel may supersede the active Attempt. */\nexport const CONFLICTABLE_STATES: ReadonlySet<OperationState> = new Set<OperationState>([\n 'submitted',\n 'stalled',\n]);\n/** The only state `rebuild` reopens. */\nexport const REBUILDABLE_STATES: ReadonlySet<OperationState> = new Set<OperationState>([\n 'expired',\n]);\n/** Where the stored bytes of a new Attempt whose broadcast never completed are resent. */\nexport const RESUMABLE_STATES: ReadonlySet<OperationState> = new Set<OperationState>([\n 'signed',\n 'submitted',\n 'stalled',\n]);\n\n/**\n * The built transaction must use exactly the slot the engine reserved: the ordering\n * kind of the driver, the allocated nonce or seqno, and no input held by another live\n * Operation. Anything else would persist a reservation nobody allocated.\n */\nexport function assertBuiltOrdering(\n operationId: string,\n kind: OrderingKind,\n allocated: OrderingData | undefined,\n excluded: readonly string[] | undefined,\n built: OrderingData,\n): void {\n // No existing code names an input conflict without implying a nonce, so NONCE_CONFLICT\n // (the ordering-slot conflict code) is kept and the message says what actually clashed.\n if (built.kind === kind && built.kind === 'inputs') {\n if (built.inputs.some((input) => excluded?.includes(input))) {\n throw new ChainError(\n 'NONCE_CONFLICT',\n 'input reservation conflict: the built transaction spends an input held by another operation',\n { context: { operationId } },\n );\n }\n return;\n }\n const matches =\n built.kind === kind &&\n (built.kind === 'nonce'\n ? allocated?.kind === 'nonce' && built.nonce === allocated.nonce\n : built.kind === 'seqno'\n ? allocated?.kind === 'seqno' && built.seqno === allocated.seqno\n : true);\n if (!matches) {\n throw new ChainError(\n 'NONCE_CONFLICT',\n 'the built transaction does not use the ordering slot reserved for it',\n { context: { operationId } },\n );\n }\n}\n"]}
|
|
@@ -140,6 +140,8 @@ export declare class OperationEngine {
|
|
|
140
140
|
record: OperationRecord;
|
|
141
141
|
stored: StoredIntent;
|
|
142
142
|
}>;
|
|
143
|
+
/** The decimals the intent's amounts were scaled with (see `BuildContext.decimals`). */
|
|
144
|
+
protected decimalsOf(target: OperationTarget, intent: StoredIntent): Promise<number>;
|
|
143
145
|
protected executionContext(target: OperationTarget): ExecutionContext;
|
|
144
146
|
/**
|
|
145
147
|
* Rejects an Operation of another chain, network or sending address: its reservation
|
|
@@ -41,9 +41,11 @@ const reservations_1 = require("../ordering/reservations");
|
|
|
41
41
|
const sequence_1 = require("../ordering/sequence");
|
|
42
42
|
const redact_1 = require("../secret/redact");
|
|
43
43
|
const types_1 = require("../store/types");
|
|
44
|
+
const format_1 = require("../store/format");
|
|
44
45
|
const bytes_1 = require("../util/bytes");
|
|
45
46
|
const clock_1 = require("../util/clock");
|
|
46
47
|
const json_1 = require("../util/json");
|
|
48
|
+
const names_1 = require("../util/names");
|
|
47
49
|
const engine_rules_1 = require("./engine-rules");
|
|
48
50
|
const intent_2 = require("./intent");
|
|
49
51
|
const observations_1 = require("./observations");
|
|
@@ -314,6 +316,7 @@ class OperationEngine {
|
|
|
314
316
|
keys: target.wallet.keys,
|
|
315
317
|
wallet: target.wallet.options,
|
|
316
318
|
...(ordering ? { ordering } : {}),
|
|
319
|
+
decimals: await this.decimalsOf(target, op.intent),
|
|
317
320
|
};
|
|
318
321
|
const fee = await driver.builder.estimateFee(op.intent, build);
|
|
319
322
|
const unsigned = await driver.builder.build(op.intent, fee, build);
|
|
@@ -351,12 +354,17 @@ class OperationEngine {
|
|
|
351
354
|
if (key !== undefined && (key.length === 0 || key.length > 200)) {
|
|
352
355
|
throw new error_1.ValidationError('INVALID_INTENT', 'idempotencyKey must be 1-200 characters');
|
|
353
356
|
}
|
|
357
|
+
// Stores key Operations by it: text a database would refuse or alter is refused here.
|
|
358
|
+
if (key !== undefined && !(0, names_1.storableText)(key)) {
|
|
359
|
+
throw new error_1.ValidationError('INVALID_INTENT', 'idempotencyKey must be well-formed text without U+0000');
|
|
360
|
+
}
|
|
354
361
|
const { selection } = target;
|
|
355
362
|
const normalized = await (0, intent_2.normalizeIntent)({ selection, driver: target.pooled.driver, assets: target.assets }, intent, target.wallet.address);
|
|
356
363
|
const stored = (0, intent_1.toStoredIntent)(normalized);
|
|
357
364
|
const hash = (0, intent_1.intentHash)(selection.chain.id, selection.network.id, stored);
|
|
358
365
|
const created = await this.deps.stores.operations.create({
|
|
359
366
|
id: (0, bytes_1.randomId)('op'),
|
|
367
|
+
format: format_1.OPERATION_FORMAT,
|
|
360
368
|
namespace: this.deps.namespace,
|
|
361
369
|
idempotencyKey: key ?? (0, bytes_1.randomId)('idem'),
|
|
362
370
|
intentHash: hash,
|
|
@@ -374,6 +382,12 @@ class OperationEngine {
|
|
|
374
382
|
}
|
|
375
383
|
return { record: created.record, stored: created.record.intent };
|
|
376
384
|
}
|
|
385
|
+
/** The decimals the intent's amounts were scaled with (see `BuildContext.decimals`). */
|
|
386
|
+
async decimalsOf(target, intent) {
|
|
387
|
+
const { assets, selection, pooled } = target;
|
|
388
|
+
return (await assets.resolve(selection, pooled.driver, intent.asset)).metadata
|
|
389
|
+
.decimals;
|
|
390
|
+
}
|
|
377
391
|
executionContext(target) {
|
|
378
392
|
const { selection, wallet } = target;
|
|
379
393
|
return {
|
|
@@ -474,6 +488,7 @@ class OperationEngine {
|
|
|
474
488
|
wallet: target.wallet.options,
|
|
475
489
|
...(reservation ? { ordering: reservation } : {}),
|
|
476
490
|
...(excludeInputs ? { excludeInputs } : {}),
|
|
491
|
+
decimals: await this.decimalsOf(target, stored),
|
|
477
492
|
...(signal ? { signal } : {}),
|
|
478
493
|
};
|
|
479
494
|
const fee = await driver.builder.estimateFee(stored, build);
|
|
@@ -882,7 +897,9 @@ class OperationEngine {
|
|
|
882
897
|
? (0, reservations_1.reservedInputs)(await this.walletOperations(op), op.id)
|
|
883
898
|
: undefined;
|
|
884
899
|
// A cancel climbs from the earlier cancels of this same Attempt (`cancelBase`).
|
|
885
|
-
const base = purpose === 'cancel'
|
|
900
|
+
const base = purpose === 'cancel'
|
|
901
|
+
? (0, engine_rules_1.cancelBase)(op, previous, driver.replacement?.comparePrice)
|
|
902
|
+
: previous;
|
|
886
903
|
const unsigned = await build(base.unsigned, {
|
|
887
904
|
from: op.intent.from,
|
|
888
905
|
keys: target.wallet.keys,
|
|
@@ -1366,6 +1383,7 @@ class OperationEngine {
|
|
|
1366
1383
|
state: proven ? 'rejected' : 'refused',
|
|
1367
1384
|
evidence: proven ? 'proven' : 'observed',
|
|
1368
1385
|
reason: result.reason,
|
|
1386
|
+
failure: undefined,
|
|
1369
1387
|
lastBroadcastAt: now,
|
|
1370
1388
|
// The `refused` state overwrites the live one, so the marker keeps a later
|
|
1371
1389
|
// rejection seeing these bytes as possibly live (e.g. after an ambiguous send).
|