@midnight-ntwrk/wallet-sdk-abstractions 3.0.0-beta.0 → 3.0.0-beta.2
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/dist/InMemoryTransactionHistoryStorage.js +14 -12
- package/dist/ProtocolState.d.ts +12 -4
- package/dist/ProtocolState.js +2 -1
- package/dist/ProtocolVersion.d.ts +146 -0
- package/dist/ProtocolVersion.js +124 -0
- package/dist/TokenType.d.ts +57 -0
- package/dist/TokenType.js +68 -0
- package/dist/TransactionHistoryStorage.d.ts +19 -3
- package/dist/TransactionHistoryStorage.js +15 -0
- package/dist/WalletTransaction.d.ts +207 -0
- package/dist/WalletTransaction.js +250 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/package.json +1 -1
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
import { Schema } from 'effect';
|
|
14
|
+
import { coversTransaction, } from './TransactionHistoryStorage.js';
|
|
14
15
|
/**
|
|
15
16
|
* In-memory implementation of the TransactionHistoryStorage interface.
|
|
16
17
|
*
|
|
@@ -42,9 +43,11 @@ export class InMemoryTransactionHistoryStorage {
|
|
|
42
43
|
const { finalizedBlock, ...rest } = input;
|
|
43
44
|
const entry = { ...rest, lifecycle: { status: 'finalized', finalizedBlock } };
|
|
44
45
|
await this.#upsert(entry);
|
|
45
|
-
this.#
|
|
46
|
+
this.#clearUnfinalizedCoveredBy(entry);
|
|
46
47
|
}
|
|
47
48
|
async gotRejected(input) {
|
|
49
|
+
if (this.#inclusionRecorded(input))
|
|
50
|
+
return;
|
|
48
51
|
const { rejectedAt, reason, ...rest } = input;
|
|
49
52
|
const lifecycle = reason !== undefined
|
|
50
53
|
? { status: 'rejected', rejectedAt, reason }
|
|
@@ -80,16 +83,15 @@ export class InMemoryTransactionHistoryStorage {
|
|
|
80
83
|
this.#storage.set(entry.hash, existing ? this.#merge(existing, entry) : entry);
|
|
81
84
|
return Promise.resolve();
|
|
82
85
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
entry.lifecycle.status
|
|
90
|
-
entry.hash !==
|
|
91
|
-
|
|
92
|
-
this.#storage.delete(
|
|
93
|
-
}
|
|
86
|
+
/** Checked synchronously with the write that follows, so a verdict racing sync cannot supersede inclusion. */
|
|
87
|
+
#inclusionRecorded(key) {
|
|
88
|
+
return [...this.#storage.values()].some((entry) => entry.lifecycle.status === 'finalized' && coversTransaction(entry, key));
|
|
89
|
+
}
|
|
90
|
+
#clearUnfinalizedCoveredBy(finalized) {
|
|
91
|
+
[...this.#storage.values()]
|
|
92
|
+
.filter((entry) => entry.lifecycle.status !== 'finalized' &&
|
|
93
|
+
entry.hash !== finalized.hash &&
|
|
94
|
+
coversTransaction(finalized, entry))
|
|
95
|
+
.forEach((entry) => this.#storage.delete(entry.hash));
|
|
94
96
|
}
|
|
95
97
|
}
|
package/dist/ProtocolState.d.ts
CHANGED
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
import { Equivalence } from 'effect';
|
|
2
2
|
import type * as ProtocolVersion from './ProtocolVersion.js';
|
|
3
3
|
/**
|
|
4
|
-
* A type that associates some state with a given version of the Midnight protocol
|
|
4
|
+
* A type that associates some state with a given version of the Midnight protocol, and with the variant that produced
|
|
5
|
+
* it.
|
|
5
6
|
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* `variantTag` is the tag of the wallet variant the emission came from. The runtime knows it at the moment it publishes
|
|
9
|
+
* a state, so it hands it over rather than leaving readers to infer the producing implementation from `version` —
|
|
10
|
+
* which would mean re-deriving activation ranges, and casting, on every emission. It travels with the state so that
|
|
11
|
+
* selecting the capabilities that understand a state is a lookup by tag.
|
|
6
12
|
* @typeParam TState The type of state.
|
|
13
|
+
* @typeParam TVariantTag The tag, or union of tags, of the variants that can produce this state.
|
|
7
14
|
*/
|
|
8
|
-
export type ProtocolState<TState> = Readonly<{
|
|
15
|
+
export type ProtocolState<TState, TVariantTag extends string | symbol = string | symbol> = Readonly<{
|
|
9
16
|
version: ProtocolVersion.ProtocolVersion;
|
|
17
|
+
variantTag: TVariantTag;
|
|
10
18
|
state: TState;
|
|
11
19
|
}>;
|
|
12
20
|
export declare const state: <TState>(ps: ProtocolState<TState>) => TState;
|
|
13
21
|
/**
|
|
14
22
|
* Derives an {@link Equivalence.Equivalence} for {@link ProtocolState} values from an equivalence of the underlying
|
|
15
|
-
* state. Versions are compared strictly.
|
|
23
|
+
* state. Versions and producing variant tags are compared strictly.
|
|
16
24
|
*
|
|
17
25
|
* @param stateEquivalence The equivalence used to compare the `state` field.
|
|
18
26
|
* @returns An equivalence over `ProtocolState<TState>`.
|
|
19
27
|
*/
|
|
20
|
-
export declare const getEquivalence: <TState>(stateEquivalence: Equivalence.Equivalence<TState>) => Equivalence.Equivalence<ProtocolState<TState>>;
|
|
28
|
+
export declare const getEquivalence: <TState, TVariantTag extends string | symbol = string | symbol>(stateEquivalence: Equivalence.Equivalence<TState>) => Equivalence.Equivalence<ProtocolState<TState, TVariantTag>>;
|
package/dist/ProtocolState.js
CHANGED
|
@@ -14,12 +14,13 @@ import { Equivalence } from 'effect';
|
|
|
14
14
|
export const state = (ps) => ps.state;
|
|
15
15
|
/**
|
|
16
16
|
* Derives an {@link Equivalence.Equivalence} for {@link ProtocolState} values from an equivalence of the underlying
|
|
17
|
-
* state. Versions are compared strictly.
|
|
17
|
+
* state. Versions and producing variant tags are compared strictly.
|
|
18
18
|
*
|
|
19
19
|
* @param stateEquivalence The equivalence used to compare the `state` field.
|
|
20
20
|
* @returns An equivalence over `ProtocolState<TState>`.
|
|
21
21
|
*/
|
|
22
22
|
export const getEquivalence = (stateEquivalence) => Equivalence.struct({
|
|
23
23
|
version: Equivalence.strict(),
|
|
24
|
+
variantTag: Equivalence.strict(),
|
|
24
25
|
state: stateEquivalence,
|
|
25
26
|
});
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import * as Brand from 'effect/Brand';
|
|
2
|
+
import * as Either from 'effect/Either';
|
|
3
|
+
import * as Option from 'effect/Option';
|
|
2
4
|
import * as Schema from 'effect/Schema';
|
|
3
5
|
/** A branded `bigint` that represents a protocol version. */
|
|
4
6
|
export type ProtocolVersion = Brand.Branded<bigint, 'ProtocolVersion'>;
|
|
@@ -38,3 +40,147 @@ export declare const is: (u: unknown, overrideOptions?: import("effect/SchemaAST
|
|
|
38
40
|
export declare const MinSupportedVersion: ProtocolVersion;
|
|
39
41
|
/** Represents the maximum supported protocol version. */
|
|
40
42
|
export declare const MaxSupportedVersion: ProtocolVersion;
|
|
43
|
+
/**
|
|
44
|
+
* The protocol version a ledger-v9-native chain hands over at, for the current node line.
|
|
45
|
+
*
|
|
46
|
+
* @remarks
|
|
47
|
+
* Measured, not assumed: a `midnight-node` 2.x reports protocol version `2000000` on its ledger events — the runtime
|
|
48
|
+
* version, scaled, rather than a small ordinal or the ledger major. A chain of the previous node line reports a
|
|
49
|
+
* 1.x-encoded value, which is below this, so a wallet configured with it stays on the V1 variant there and reaches
|
|
50
|
+
* the V2 variant on any v9-native chain.
|
|
51
|
+
*
|
|
52
|
+
* Offered as a named value so applications and test suites pointed at a v9-native chain do not each invent a magic
|
|
53
|
+
* number; {@link V9NativeForkSchedule} is the schedule built from it. The final mainnet fork constant is still an open
|
|
54
|
+
* question; when it is fixed it will join this one here.
|
|
55
|
+
*/
|
|
56
|
+
export declare const V9NativeForkVersion: ProtocolVersion;
|
|
57
|
+
/**
|
|
58
|
+
* Where each ledger version after the first begins on a chain, by protocol version.
|
|
59
|
+
*
|
|
60
|
+
* @remarks
|
|
61
|
+
* One key per ledger version the SDK can run, minus the first: ledger-v8 begins at {@link MinSupportedVersion} and needs
|
|
62
|
+
* no entry. `v9` is the protocol version from which ledger-v9 reads the chain — {@link V9NativeForkVersion} on a chain
|
|
63
|
+
* born on ledger-v9, and whatever version the hand-over is enacted at on a chain with ledger-v8 history. The next
|
|
64
|
+
* hard fork adds a key (`v10`) rather than changing this shape, which is what lets an application's configuration
|
|
65
|
+
* outlive the fork.
|
|
66
|
+
*
|
|
67
|
+
* Every entry is required in the wallet packages, for the same reason a single fork version was: where a chain forks is
|
|
68
|
+
* a fact about the chain rather than the SDK, and a wrong guess does not degrade — it decides which ledger version
|
|
69
|
+
* reads the chain. {@link V9NativeForkSchedule} names the schedule of a chain born on ledger-v9; only the facade
|
|
70
|
+
* presets one, `DefaultForkSchedule`, and says there why.
|
|
71
|
+
*/
|
|
72
|
+
export type ForkSchedule = Readonly<{
|
|
73
|
+
/** The protocol version from which ledger-v9 reads the chain. */
|
|
74
|
+
v9: ProtocolVersion;
|
|
75
|
+
}>;
|
|
76
|
+
/**
|
|
77
|
+
* The fork schedule of a chain born on ledger-v9: ledger-v9 from {@link V9NativeForkVersion}, and nothing else.
|
|
78
|
+
*
|
|
79
|
+
* @remarks
|
|
80
|
+
* Offered as a named value for the same reason {@link V9NativeForkVersion} is: so applications and test suites pointed
|
|
81
|
+
* at a v9-native chain do not each write the schedule out by hand. A named value and not a default — the wallet
|
|
82
|
+
* packages still require `forks`. The facade's `DefaultForkSchedule` is this value, under the name of what the facade
|
|
83
|
+
* presets.
|
|
84
|
+
*/
|
|
85
|
+
export declare const V9NativeForkSchedule: ForkSchedule;
|
|
86
|
+
/**
|
|
87
|
+
* The range of protocol versions on the same side of a protocol boundary as a given version.
|
|
88
|
+
*
|
|
89
|
+
* @remarks
|
|
90
|
+
* What the SDK means by "the same ledger version made these bytes". A protocol boundary divides the timeline into two
|
|
91
|
+
* epochs, and everything that routes on a version — which prover proves a transaction, which validator checks it,
|
|
92
|
+
* which variant may unwrap it — is really asking which epoch it belongs to. Stated once here so the two ends of that
|
|
93
|
+
* question, the wallet stamping a transaction and the caller unwrapping it, cannot compute the boundary differently.
|
|
94
|
+
*
|
|
95
|
+
* A chain whose boundary is at or below the minimum supported version has never had two epochs, so the whole range is
|
|
96
|
+
* one.
|
|
97
|
+
* @param version A version in the epoch of interest.
|
|
98
|
+
* @param forkVersion The version at which the chain hands over to the next ledger version.
|
|
99
|
+
* @returns The half-open range of versions in that epoch.
|
|
100
|
+
*/
|
|
101
|
+
export declare const epochOf: (version: ProtocolVersion, forkVersion: ProtocolVersion) => ProtocolVersion.Range;
|
|
102
|
+
/**
|
|
103
|
+
* A value together with the protocol version range it serves.
|
|
104
|
+
*
|
|
105
|
+
* @typeParam T The type of the registered value.
|
|
106
|
+
*/
|
|
107
|
+
export type RegistryEntry<T> = Readonly<{
|
|
108
|
+
range: ProtocolVersion.Range;
|
|
109
|
+
value: T;
|
|
110
|
+
}>;
|
|
111
|
+
/**
|
|
112
|
+
* An ordered collection of values, each keyed by the protocol version range it serves.
|
|
113
|
+
*
|
|
114
|
+
* @remarks
|
|
115
|
+
* This is the shared shape behind every "which implementation speaks this protocol version?" question the SDK asks —
|
|
116
|
+
* which codec decodes a block, which prover proves a recipe, which transaction trait understands a pending
|
|
117
|
+
* transaction, which variant a snapshot should be restored into. Keeping one primitive means those answers cannot
|
|
118
|
+
* disagree about what a version range is or where its boundaries lie.
|
|
119
|
+
*
|
|
120
|
+
* Entries are ascending and non-overlapping by construction, and ranges are half-open (`withinRange`), so exactly one
|
|
121
|
+
* entry can match a version. Gaps are allowed: a version no entry covers selects nothing, which callers turn into
|
|
122
|
+
* whichever typed error means "unsupported" in their domain.
|
|
123
|
+
* @typeParam T The type of the registered values.
|
|
124
|
+
*/
|
|
125
|
+
export type Registry<T> = Readonly<{
|
|
126
|
+
entries: readonly RegistryEntry<T>[];
|
|
127
|
+
}>;
|
|
128
|
+
declare const RegistryError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
129
|
+
readonly _tag: "@midnight-ntwrk/wallet-sdk-abstractions/ProtocolVersion/RegistryError";
|
|
130
|
+
} & Readonly<A>;
|
|
131
|
+
/**
|
|
132
|
+
* Raised when entries cannot form a {@link Registry} because their ranges are not ascending, overlap, or cannot be
|
|
133
|
+
* closed at all.
|
|
134
|
+
*
|
|
135
|
+
* @remarks
|
|
136
|
+
* Construction is the only place this can happen: once built, a registry is total — selection returns an
|
|
137
|
+
* {@link Option.Option} rather than failing.
|
|
138
|
+
*/
|
|
139
|
+
export declare class RegistryError extends RegistryError_base<{
|
|
140
|
+
readonly message: string;
|
|
141
|
+
/** The protocol versions whose ordering or overlap made the registry invalid. */
|
|
142
|
+
readonly versions: readonly ProtocolVersion[];
|
|
143
|
+
}> {
|
|
144
|
+
}
|
|
145
|
+
/** A registry with no entries: {@link select} on it never finds anything. */
|
|
146
|
+
export declare const emptyRegistry: Registry<never>;
|
|
147
|
+
/**
|
|
148
|
+
* Creates a registry from entries that already carry their own ranges.
|
|
149
|
+
*
|
|
150
|
+
* @param entries The entries, in ascending order of range start; ranges must not overlap, though gaps are allowed.
|
|
151
|
+
* @returns The registry, or a {@link RegistryError} naming the two range boundaries that could not be ordered.
|
|
152
|
+
*/
|
|
153
|
+
export declare const makeRegistry: <T>(entries: readonly RegistryEntry<T>[]) => Either.Either<Registry<T>, RegistryError>;
|
|
154
|
+
/**
|
|
155
|
+
* Creates a registry from values keyed by the protocol version each becomes active at, deriving the ranges from the
|
|
156
|
+
* registration order: entry _i_ serves `[sinceVersion(i), sinceVersion(i + 1))`, and the last entry serves
|
|
157
|
+
* `[sinceVersion(last), MaxSupportedVersion)`.
|
|
158
|
+
*
|
|
159
|
+
* @remarks
|
|
160
|
+
* This is the shape registration has everywhere in the SDK — a variant, codec or prover says which version it starts
|
|
161
|
+
* answering for, never which version it stops at — so the boundary between two of them is derived in exactly one
|
|
162
|
+
* place and cannot drift.
|
|
163
|
+
* @param activations The values and the versions they activate at, in strictly ascending order.
|
|
164
|
+
* @returns The registry, or a {@link RegistryError} naming the versions that broke the ordering.
|
|
165
|
+
*/
|
|
166
|
+
export declare const makeRegistryFromActivations: <T>(activations: readonly Readonly<{
|
|
167
|
+
sinceVersion: ProtocolVersion;
|
|
168
|
+
value: T;
|
|
169
|
+
}>[]) => Either.Either<Registry<T>, RegistryError>;
|
|
170
|
+
/**
|
|
171
|
+
* Finds the entry whose range contains a given protocol version.
|
|
172
|
+
*
|
|
173
|
+
* @param registry The {@link Registry} to search.
|
|
174
|
+
* @param version The version to find an entry for.
|
|
175
|
+
* @returns The matching entry, or `Option.none()` when no entry covers `version`.
|
|
176
|
+
*/
|
|
177
|
+
export declare const selectEntry: <T>(registry: Registry<T>, version: ProtocolVersion) => Option.Option<RegistryEntry<T>>;
|
|
178
|
+
/**
|
|
179
|
+
* Finds the value whose range contains a given protocol version.
|
|
180
|
+
*
|
|
181
|
+
* @param registry The {@link Registry} to search.
|
|
182
|
+
* @param version The version to find a value for.
|
|
183
|
+
* @returns The matching value, or `Option.none()` when no entry covers `version`.
|
|
184
|
+
*/
|
|
185
|
+
export declare const select: <T>(registry: Registry<T>, version: ProtocolVersion) => Option.Option<T>;
|
|
186
|
+
export {};
|
package/dist/ProtocolVersion.js
CHANGED
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
import * as Brand from 'effect/Brand';
|
|
14
|
+
import * as Data from 'effect/Data';
|
|
15
|
+
import * as Either from 'effect/Either';
|
|
16
|
+
import * as Option from 'effect/Option';
|
|
14
17
|
import * as Schema from 'effect/Schema';
|
|
15
18
|
/** Constructs a branded `bigint` represents a protocol version. */
|
|
16
19
|
export const ProtocolVersion = Brand.nominal();
|
|
@@ -52,3 +55,124 @@ export const is = Schema.is(ProtocolVersionSchema);
|
|
|
52
55
|
export const MinSupportedVersion = ProtocolVersion(0n);
|
|
53
56
|
/** Represents the maximum supported protocol version. */
|
|
54
57
|
export const MaxSupportedVersion = ProtocolVersion(BigInt(Number.MAX_SAFE_INTEGER));
|
|
58
|
+
/**
|
|
59
|
+
* The protocol version a ledger-v9-native chain hands over at, for the current node line.
|
|
60
|
+
*
|
|
61
|
+
* @remarks
|
|
62
|
+
* Measured, not assumed: a `midnight-node` 2.x reports protocol version `2000000` on its ledger events — the runtime
|
|
63
|
+
* version, scaled, rather than a small ordinal or the ledger major. A chain of the previous node line reports a
|
|
64
|
+
* 1.x-encoded value, which is below this, so a wallet configured with it stays on the V1 variant there and reaches
|
|
65
|
+
* the V2 variant on any v9-native chain.
|
|
66
|
+
*
|
|
67
|
+
* Offered as a named value so applications and test suites pointed at a v9-native chain do not each invent a magic
|
|
68
|
+
* number; {@link V9NativeForkSchedule} is the schedule built from it. The final mainnet fork constant is still an open
|
|
69
|
+
* question; when it is fixed it will join this one here.
|
|
70
|
+
*/
|
|
71
|
+
export const V9NativeForkVersion = ProtocolVersion(2000000n);
|
|
72
|
+
/**
|
|
73
|
+
* The fork schedule of a chain born on ledger-v9: ledger-v9 from {@link V9NativeForkVersion}, and nothing else.
|
|
74
|
+
*
|
|
75
|
+
* @remarks
|
|
76
|
+
* Offered as a named value for the same reason {@link V9NativeForkVersion} is: so applications and test suites pointed
|
|
77
|
+
* at a v9-native chain do not each write the schedule out by hand. A named value and not a default — the wallet
|
|
78
|
+
* packages still require `forks`. The facade's `DefaultForkSchedule` is this value, under the name of what the facade
|
|
79
|
+
* presets.
|
|
80
|
+
*/
|
|
81
|
+
export const V9NativeForkSchedule = { v9: V9NativeForkVersion };
|
|
82
|
+
/**
|
|
83
|
+
* The range of protocol versions on the same side of a protocol boundary as a given version.
|
|
84
|
+
*
|
|
85
|
+
* @remarks
|
|
86
|
+
* What the SDK means by "the same ledger version made these bytes". A protocol boundary divides the timeline into two
|
|
87
|
+
* epochs, and everything that routes on a version — which prover proves a transaction, which validator checks it,
|
|
88
|
+
* which variant may unwrap it — is really asking which epoch it belongs to. Stated once here so the two ends of that
|
|
89
|
+
* question, the wallet stamping a transaction and the caller unwrapping it, cannot compute the boundary differently.
|
|
90
|
+
*
|
|
91
|
+
* A chain whose boundary is at or below the minimum supported version has never had two epochs, so the whole range is
|
|
92
|
+
* one.
|
|
93
|
+
* @param version A version in the epoch of interest.
|
|
94
|
+
* @param forkVersion The version at which the chain hands over to the next ledger version.
|
|
95
|
+
* @returns The half-open range of versions in that epoch.
|
|
96
|
+
*/
|
|
97
|
+
export const epochOf = (version, forkVersion) => forkVersion <= MinSupportedVersion
|
|
98
|
+
? makeRange(MinSupportedVersion, MaxSupportedVersion)
|
|
99
|
+
: version < forkVersion
|
|
100
|
+
? makeRange(MinSupportedVersion, forkVersion)
|
|
101
|
+
: makeRange(forkVersion, MaxSupportedVersion);
|
|
102
|
+
/**
|
|
103
|
+
* Raised when entries cannot form a {@link Registry} because their ranges are not ascending, overlap, or cannot be
|
|
104
|
+
* closed at all.
|
|
105
|
+
*
|
|
106
|
+
* @remarks
|
|
107
|
+
* Construction is the only place this can happen: once built, a registry is total — selection returns an
|
|
108
|
+
* {@link Option.Option} rather than failing.
|
|
109
|
+
*/
|
|
110
|
+
export class RegistryError extends Data.TaggedError('@midnight-ntwrk/wallet-sdk-abstractions/ProtocolVersion/RegistryError') {
|
|
111
|
+
}
|
|
112
|
+
/** A registry with no entries: {@link select} on it never finds anything. */
|
|
113
|
+
export const emptyRegistry = { entries: [] };
|
|
114
|
+
/**
|
|
115
|
+
* Creates a registry from entries that already carry their own ranges.
|
|
116
|
+
*
|
|
117
|
+
* @param entries The entries, in ascending order of range start; ranges must not overlap, though gaps are allowed.
|
|
118
|
+
* @returns The registry, or a {@link RegistryError} naming the two range boundaries that could not be ordered.
|
|
119
|
+
*/
|
|
120
|
+
export const makeRegistry = (entries) => {
|
|
121
|
+
const brokenAt = entries.findIndex((entry, index) => index > 0 && entry.range[0] < entries[index - 1].range[1]);
|
|
122
|
+
return brokenAt < 0
|
|
123
|
+
? Either.right({ entries })
|
|
124
|
+
: Either.left(new RegistryError({
|
|
125
|
+
message: 'Protocol version registry entries must be ascending and must not overlap.',
|
|
126
|
+
versions: [entries[brokenAt - 1].range[1], entries[brokenAt].range[0]],
|
|
127
|
+
}));
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Creates a registry from values keyed by the protocol version each becomes active at, deriving the ranges from the
|
|
131
|
+
* registration order: entry _i_ serves `[sinceVersion(i), sinceVersion(i + 1))`, and the last entry serves
|
|
132
|
+
* `[sinceVersion(last), MaxSupportedVersion)`.
|
|
133
|
+
*
|
|
134
|
+
* @remarks
|
|
135
|
+
* This is the shape registration has everywhere in the SDK — a variant, codec or prover says which version it starts
|
|
136
|
+
* answering for, never which version it stops at — so the boundary between two of them is derived in exactly one
|
|
137
|
+
* place and cannot drift.
|
|
138
|
+
* @param activations The values and the versions they activate at, in strictly ascending order.
|
|
139
|
+
* @returns The registry, or a {@link RegistryError} naming the versions that broke the ordering.
|
|
140
|
+
*/
|
|
141
|
+
export const makeRegistryFromActivations = (activations) => {
|
|
142
|
+
const brokenAt = activations.findIndex((activation, index) => index > 0 && activation.sinceVersion <= activations[index - 1].sinceVersion);
|
|
143
|
+
if (brokenAt >= 0) {
|
|
144
|
+
return Either.left(new RegistryError({
|
|
145
|
+
message: 'Protocol version activations must be strictly ascending.',
|
|
146
|
+
versions: [activations[brokenAt - 1].sinceVersion, activations[brokenAt].sinceVersion],
|
|
147
|
+
}));
|
|
148
|
+
}
|
|
149
|
+
const last = activations.at(-1);
|
|
150
|
+
if (last !== undefined && last.sinceVersion >= MaxSupportedVersion) {
|
|
151
|
+
return Either.left(new RegistryError({
|
|
152
|
+
message: 'A protocol version activation must leave room for a range above it.',
|
|
153
|
+
versions: [last.sinceVersion],
|
|
154
|
+
}));
|
|
155
|
+
}
|
|
156
|
+
return Either.right({
|
|
157
|
+
entries: activations.map(({ sinceVersion, value }, index) => ({
|
|
158
|
+
range: makeRange(sinceVersion, activations[index + 1]?.sinceVersion ?? MaxSupportedVersion),
|
|
159
|
+
value,
|
|
160
|
+
})),
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* Finds the entry whose range contains a given protocol version.
|
|
165
|
+
*
|
|
166
|
+
* @param registry The {@link Registry} to search.
|
|
167
|
+
* @param version The version to find an entry for.
|
|
168
|
+
* @returns The matching entry, or `Option.none()` when no entry covers `version`.
|
|
169
|
+
*/
|
|
170
|
+
export const selectEntry = (registry, version) => Option.fromNullable(registry.entries.find((entry) => withinRange(version, entry.range)));
|
|
171
|
+
/**
|
|
172
|
+
* Finds the value whose range contains a given protocol version.
|
|
173
|
+
*
|
|
174
|
+
* @param registry The {@link Registry} to search.
|
|
175
|
+
* @param version The version to find a value for.
|
|
176
|
+
* @returns The matching value, or `Option.none()` when no entry covers `version`.
|
|
177
|
+
*/
|
|
178
|
+
export const select = (registry, version) => selectEntry(registry, version).pipe(Option.map((entry) => entry.value));
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import * as Either from 'effect/Either';
|
|
2
|
+
/**
|
|
3
|
+
* A token type, in the raw form every balance and coin is keyed by.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* Deliberately a plain `string` and **not** a branded type, which is a considered exception to this codebase's
|
|
7
|
+
* parse-don't-validate default. Balances are `Record<TokenType, bigint>`, and a branded key type does not produce a
|
|
8
|
+
* string index signature: branding here would break every balance read in the SDK and in every application, to buy a
|
|
9
|
+
* guarantee that the values are not confusable with anything else a caller holds anyway. Use {@link parseTokenType} at
|
|
10
|
+
* the boundary where a token type arrives from outside, which is where the guarantee is actually worth having.
|
|
11
|
+
*/
|
|
12
|
+
export type TokenType = string;
|
|
13
|
+
declare const InvalidTokenTypeError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
14
|
+
readonly _tag: "@midnight-ntwrk/wallet-sdk-abstractions/TokenType/InvalidTokenTypeError";
|
|
15
|
+
} & Readonly<A>;
|
|
16
|
+
/** Raised when a value offered as a token type is not one. */
|
|
17
|
+
export declare class InvalidTokenTypeError extends InvalidTokenTypeError_base<{
|
|
18
|
+
readonly message: string;
|
|
19
|
+
/** What was offered, so the caller can report it alongside the field it came from. */
|
|
20
|
+
readonly value: string;
|
|
21
|
+
}> {
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The token types the SDK names.
|
|
25
|
+
*
|
|
26
|
+
* @remarks
|
|
27
|
+
* String literals rather than calls into a ledger, and that is load-bearing: these constants reach an application
|
|
28
|
+
* through the umbrella package's root barrel, which is the one place no ledger version's WebAssembly may be loaded. A
|
|
29
|
+
* test pins each of them against both ledger versions, so the literal cannot drift away from what the chain means.
|
|
30
|
+
*
|
|
31
|
+
* There is one constant because there is one raw type to name. Both ledger versions report the same thirty-two bytes
|
|
32
|
+
* for the native, shielded and unshielded token; what distinguishes them is a `tag` on the ledger's own wrapper, and
|
|
33
|
+
* a balance is keyed by the raw type alone.
|
|
34
|
+
*/
|
|
35
|
+
export declare const Token: {
|
|
36
|
+
/** Night: the token the chain is denominated in, and the resource Dust is generated from. */
|
|
37
|
+
readonly night: "0000000000000000000000000000000000000000000000000000000000000000";
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Reads a token type from a value that came from outside the SDK.
|
|
41
|
+
*
|
|
42
|
+
* @remarks
|
|
43
|
+
* The boundary check the unbranded {@link TokenType} does not make for itself: a dApp request, a URL parameter or a
|
|
44
|
+
* configuration file can carry anything, and a token type that is quietly wrong selects no coins rather than failing.
|
|
45
|
+
* Returns rather than throws, because "this is not a token type" is an ordinary thing for input to be.
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* const tokenType = parseTokenType(request.tokenType).pipe(
|
|
49
|
+
* Either.getOrElse(() => Token.night),
|
|
50
|
+
* );
|
|
51
|
+
* ```;
|
|
52
|
+
*
|
|
53
|
+
* @param value The value offered as a token type.
|
|
54
|
+
* @returns The token type, or an {@link InvalidTokenTypeError} naming what was offered.
|
|
55
|
+
*/
|
|
56
|
+
export declare const parseTokenType: (value: string) => Either.Either<TokenType, InvalidTokenTypeError>;
|
|
57
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// This file is part of MIDNIGHT-WALLET-SDK.
|
|
2
|
+
// Copyright (C) Midnight Foundation
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// You may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
/**
|
|
14
|
+
* The names the SDK gives to token types, so an application never has to ask a ledger version for one.
|
|
15
|
+
*
|
|
16
|
+
* @remarks
|
|
17
|
+
* A token type is thirty-two bytes, written as hexadecimal, and it is the same thirty-two bytes on either side of a
|
|
18
|
+
* protocol boundary — which is the whole reason the SDK can name one for itself. Until now an application wanting the
|
|
19
|
+
* Night token type had to call `nativeToken().raw` on a ledger package, which meant importing a ledger version to
|
|
20
|
+
* read a constant that does not depend on one.
|
|
21
|
+
*/
|
|
22
|
+
import * as Data from 'effect/Data';
|
|
23
|
+
import * as Either from 'effect/Either';
|
|
24
|
+
/** Raised when a value offered as a token type is not one. */
|
|
25
|
+
export class InvalidTokenTypeError extends Data.TaggedError('@midnight-ntwrk/wallet-sdk-abstractions/TokenType/InvalidTokenTypeError') {
|
|
26
|
+
}
|
|
27
|
+
/** Thirty-two bytes of lower-case hexadecimal: the form both ledger versions write a token type in. */
|
|
28
|
+
const TOKEN_TYPE_PATTERN = /^[0-9a-f]{64}$/;
|
|
29
|
+
/**
|
|
30
|
+
* The token types the SDK names.
|
|
31
|
+
*
|
|
32
|
+
* @remarks
|
|
33
|
+
* String literals rather than calls into a ledger, and that is load-bearing: these constants reach an application
|
|
34
|
+
* through the umbrella package's root barrel, which is the one place no ledger version's WebAssembly may be loaded. A
|
|
35
|
+
* test pins each of them against both ledger versions, so the literal cannot drift away from what the chain means.
|
|
36
|
+
*
|
|
37
|
+
* There is one constant because there is one raw type to name. Both ledger versions report the same thirty-two bytes
|
|
38
|
+
* for the native, shielded and unshielded token; what distinguishes them is a `tag` on the ledger's own wrapper, and
|
|
39
|
+
* a balance is keyed by the raw type alone.
|
|
40
|
+
*/
|
|
41
|
+
export const Token = {
|
|
42
|
+
/** Night: the token the chain is denominated in, and the resource Dust is generated from. */
|
|
43
|
+
night: '0000000000000000000000000000000000000000000000000000000000000000',
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Reads a token type from a value that came from outside the SDK.
|
|
47
|
+
*
|
|
48
|
+
* @remarks
|
|
49
|
+
* The boundary check the unbranded {@link TokenType} does not make for itself: a dApp request, a URL parameter or a
|
|
50
|
+
* configuration file can carry anything, and a token type that is quietly wrong selects no coins rather than failing.
|
|
51
|
+
* Returns rather than throws, because "this is not a token type" is an ordinary thing for input to be.
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* const tokenType = parseTokenType(request.tokenType).pipe(
|
|
55
|
+
* Either.getOrElse(() => Token.night),
|
|
56
|
+
* );
|
|
57
|
+
* ```;
|
|
58
|
+
*
|
|
59
|
+
* @param value The value offered as a token type.
|
|
60
|
+
* @returns The token type, or an {@link InvalidTokenTypeError} naming what was offered.
|
|
61
|
+
*/
|
|
62
|
+
export const parseTokenType = (value) => TOKEN_TYPE_PATTERN.test(value)
|
|
63
|
+
? Either.right(value)
|
|
64
|
+
: Either.left(new InvalidTokenTypeError({
|
|
65
|
+
message: `"${value}" is not a token type: a token type is thirty-two bytes written as sixty-four lower-case ` +
|
|
66
|
+
`hexadecimal characters.`,
|
|
67
|
+
value,
|
|
68
|
+
}));
|
|
@@ -109,6 +109,16 @@ export type RejectedTransactionHistoryCommon = TransactionHistoryEntryCommon & {
|
|
|
109
109
|
readonly lifecycle: RejectedLifecycle;
|
|
110
110
|
};
|
|
111
111
|
export type SerializedTransactionHistory = string;
|
|
112
|
+
/**
|
|
113
|
+
* Whether `entry` records the transaction `key` names: the same hash, or every one of `key`'s identifiers. Identifiers
|
|
114
|
+
* are per-build commitments, so an entry carrying all of them is the same transaction included under an aggregated
|
|
115
|
+
* hash. An empty identifier set names no transaction and therefore neither covers nor is covered.
|
|
116
|
+
*
|
|
117
|
+
* @param entry - The recorded entry.
|
|
118
|
+
* @param key - The hash and identifiers of the transaction being asked about.
|
|
119
|
+
* @returns Whether `entry` is a record of that transaction.
|
|
120
|
+
*/
|
|
121
|
+
export declare const coversTransaction: (entry: Pick<TransactionHistoryEntryCommon, "hash" | "identifiers">, key: Pick<TransactionHistoryEntryCommon, "hash" | "identifiers">) => boolean;
|
|
112
122
|
/**
|
|
113
123
|
* An entry with common fields plus any additional properties (wallet sections). Used by wallet packages for
|
|
114
124
|
* projection/filtering when the exact type is not known.
|
|
@@ -160,6 +170,11 @@ export interface TransactionHistoryReader<T extends {
|
|
|
160
170
|
* keys (in particular: clearing a prior `pending` entry keyed by an identifier when its `finalized` or `rejected`
|
|
161
171
|
* counterpart arrives keyed by the tx hash).
|
|
162
172
|
*
|
|
173
|
+
* Recorded inclusion takes precedence over a later rejection. Sync records inclusion independently of the
|
|
174
|
+
* pending-status poller, so a verdict may arrive after a `finalized` entry {@link coversTransaction covers} the
|
|
175
|
+
* transaction, and that verdict must neither overwrite the entry nor leave a rejected duplicate under the submission's
|
|
176
|
+
* own hash. Implementations check this atomically with the write.
|
|
177
|
+
*
|
|
163
178
|
* `T` appears only in input position, so this interface is **contravariant** in T: a `TransactionHistoryWriter<Wider>`
|
|
164
179
|
* is assignable to `TransactionHistoryWriter<Narrower>`.
|
|
165
180
|
*/
|
|
@@ -167,13 +182,14 @@ export interface TransactionHistoryWriter<T extends TransactionHistoryEntryCommo
|
|
|
167
182
|
/** Record that a tx has been submitted and is awaiting confirmation. */
|
|
168
183
|
gotPending(entry: PendingEntryInput<T>): Promise<void>;
|
|
169
184
|
/**
|
|
170
|
-
* Record that a tx has been confirmed on-chain. Inserts/merges the entry under its tx hash and clears
|
|
171
|
-
* `pending` entry
|
|
185
|
+
* Record that a tx has been confirmed on-chain. Inserts/merges the entry under its tx hash and clears every earlier
|
|
186
|
+
* `pending` or `rejected` entry this entry {@link coversTransaction covers} under another hash.
|
|
172
187
|
*/
|
|
173
188
|
gotFinalized(entry: FinalizedEntryInput<T>): Promise<void>;
|
|
174
189
|
/**
|
|
175
190
|
* Record that a tx will not land — failed, partial-success, TTL-expired, or otherwise reverted. Keyed the same way as
|
|
176
|
-
* `gotPending` so the lifecycle transition is in-place.
|
|
191
|
+
* `gotPending` so the lifecycle transition is in-place. Writes nothing when a `finalized` entry already covers the
|
|
192
|
+
* tx.
|
|
177
193
|
*/
|
|
178
194
|
gotRejected(entry: RejectedEntryInput<T>): Promise<void>;
|
|
179
195
|
}
|
|
@@ -71,3 +71,18 @@ export const extendEntrySchema = (extensionFields) => Schema.Struct({
|
|
|
71
71
|
...TransactionHistoryEntryCommonSchema.fields,
|
|
72
72
|
...extensionFields,
|
|
73
73
|
});
|
|
74
|
+
/**
|
|
75
|
+
* Whether `entry` records the transaction `key` names: the same hash, or every one of `key`'s identifiers. Identifiers
|
|
76
|
+
* are per-build commitments, so an entry carrying all of them is the same transaction included under an aggregated
|
|
77
|
+
* hash. An empty identifier set names no transaction and therefore neither covers nor is covered.
|
|
78
|
+
*
|
|
79
|
+
* @param entry - The recorded entry.
|
|
80
|
+
* @param key - The hash and identifiers of the transaction being asked about.
|
|
81
|
+
* @returns Whether `entry` is a record of that transaction.
|
|
82
|
+
*/
|
|
83
|
+
export const coversTransaction = (entry, key) => {
|
|
84
|
+
if (entry.hash === key.hash)
|
|
85
|
+
return true;
|
|
86
|
+
const recorded = new Set(entry.identifiers);
|
|
87
|
+
return key.identifiers.length > 0 && key.identifiers.every((id) => recorded.has(id));
|
|
88
|
+
};
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import * as Either from 'effect/Either';
|
|
2
|
+
import * as Option from 'effect/Option';
|
|
3
|
+
import * as Schema from 'effect/Schema';
|
|
4
|
+
import * as ProtocolVersion from './ProtocolVersion.js';
|
|
5
|
+
import * as SerializedTransaction from './SerializedTransaction.js';
|
|
6
|
+
/**
|
|
7
|
+
* The three stages, as the one thing that decides what they are.
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* Declared here rather than beside the wire envelope that reads it, because {@link TransactionStage} — the type an
|
|
11
|
+
* application names — is read back off it. Stating the three words once means the type a caller is held to and the
|
|
12
|
+
* values an envelope is parsed against cannot come to disagree: adding a stage to one adds it to the other.
|
|
13
|
+
*/
|
|
14
|
+
declare const TransactionStageSchema: Schema.Literal<["Unproven", "Unbound", "Finalized"]>;
|
|
15
|
+
/**
|
|
16
|
+
* How far along the building of a transaction a handle is.
|
|
17
|
+
*
|
|
18
|
+
* @remarks
|
|
19
|
+
* The three points at which a transaction changes hands between an application and the SDK: built but not proved,
|
|
20
|
+
* proved but not yet bound to its own contents, and finalized — proved, signed and bound, which is the only shape the
|
|
21
|
+
* network takes. Both ledger versions have all three, and the words mean the same thing on either side of a protocol
|
|
22
|
+
* boundary, which is why the stage can be plain data on a version-agnostic handle.
|
|
23
|
+
*/
|
|
24
|
+
export type TransactionStage = Schema.Schema.Type<typeof TransactionStageSchema>;
|
|
25
|
+
/** What a handle can carry: anything that can write itself out as bytes, which is every ledger's transaction. */
|
|
26
|
+
type Serializable = Readonly<{
|
|
27
|
+
serialize: () => Uint8Array;
|
|
28
|
+
}>;
|
|
29
|
+
declare const ProtocolVersionMismatchError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
30
|
+
readonly _tag: "@midnight-ntwrk/wallet-sdk-abstractions/WalletTransaction/ProtocolVersionMismatchError";
|
|
31
|
+
} & Readonly<A>;
|
|
32
|
+
/**
|
|
33
|
+
* Raised when a transaction is used at a protocol version other than the one it was built for.
|
|
34
|
+
*
|
|
35
|
+
* @remarks
|
|
36
|
+
* The bytes of a transaction are fixed when it is built, by the ledger version that built them. A protocol boundary
|
|
37
|
+
* does not rewrite them, so a transaction authored on one side of the boundary cannot be understood on the other —
|
|
38
|
+
* not merged, not proved, not submitted. This is what the SDK answers with rather than handing a transaction to a
|
|
39
|
+
* ledger version that would misread it.
|
|
40
|
+
*/
|
|
41
|
+
export declare class ProtocolVersionMismatchError extends ProtocolVersionMismatchError_base<{
|
|
42
|
+
readonly message: string;
|
|
43
|
+
/** The protocol version the transaction was built at. */
|
|
44
|
+
readonly authoredFor: ProtocolVersion.ProtocolVersion;
|
|
45
|
+
/** The range of protocol versions the caller can act at. */
|
|
46
|
+
readonly accepted: ProtocolVersion.ProtocolVersion.Range;
|
|
47
|
+
/** How far along the building of the transaction the handle is, for the diagnostic. */
|
|
48
|
+
readonly stage: TransactionStage;
|
|
49
|
+
}> {
|
|
50
|
+
}
|
|
51
|
+
declare const WireFormatError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
52
|
+
readonly _tag: "@midnight-ntwrk/wallet-sdk-abstractions/WalletTransaction/WireFormatError";
|
|
53
|
+
} & Readonly<A>;
|
|
54
|
+
/**
|
|
55
|
+
* Raised when bytes cannot be read as a transaction of a version this SDK knows — whether they arrived in a wire
|
|
56
|
+
* envelope that names their version, or bare, to be read at the version the reader is acting at.
|
|
57
|
+
*/
|
|
58
|
+
export declare class WireFormatError extends WireFormatError_base<{
|
|
59
|
+
readonly message: string;
|
|
60
|
+
/** What went wrong underneath: a parse failure, or whatever the caller's decoder raised. */
|
|
61
|
+
readonly cause?: unknown;
|
|
62
|
+
}> {
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The envelope a handle crosses a process boundary in.
|
|
66
|
+
*
|
|
67
|
+
* @remarks
|
|
68
|
+
* Everything in it is JSON-safe, because the boundary it was designed for — a dApp connector — is a JSON one: the
|
|
69
|
+
* protocol version is a decimal string rather than a `bigint`, and the transaction is hex rather than bytes. What
|
|
70
|
+
* travels is exactly what a reader needs to decide whether it can read the rest at all: the envelope format, the
|
|
71
|
+
* protocol version the transaction was built at, the stage it reached, and the transaction's own serialization.
|
|
72
|
+
*
|
|
73
|
+
* Kept deliberately small. The connector contract is an open question at the time of writing, and an envelope that
|
|
74
|
+
* guesses at it would be harder to reconcile than one that carries the minimum.
|
|
75
|
+
*/
|
|
76
|
+
declare const WireTransactionSchema: Schema.Struct<{
|
|
77
|
+
wireFormat: Schema.Literal<[1]>;
|
|
78
|
+
protocolVersion: Schema.BrandSchema<bigint & import("effect/Brand").Brand<"ProtocolVersion">, string, never>;
|
|
79
|
+
stage: Schema.Literal<["Unproven", "Unbound", "Finalized"]>;
|
|
80
|
+
transaction: Schema.Schema<Uint8Array<ArrayBufferLike>, string, never>;
|
|
81
|
+
}>;
|
|
82
|
+
/** The wire envelope as it travels: a plain JSON-safe record. See {@link WalletTransaction.toWire}. */
|
|
83
|
+
export type WireTransaction = Schema.Schema.Encoded<typeof WireTransactionSchema>;
|
|
84
|
+
/**
|
|
85
|
+
* A transaction the SDK produced or adopted, sealed together with the protocol version it was built at.
|
|
86
|
+
*
|
|
87
|
+
* @remarks
|
|
88
|
+
* A class rather than a record, and the one place in this package where that earns its keep: a `#private` field is the
|
|
89
|
+
* only way to carry something an application provably cannot reach, and reaching the wrapped transaction is exactly
|
|
90
|
+
* what would re-introduce the ledger-version dependency the handle exists to remove. Nothing about it mutates — the
|
|
91
|
+
* fields are read-only and every operation returns a new value.
|
|
92
|
+
*
|
|
93
|
+
* The stage is a type parameter as well as a field, so a signature can ask for the shape it needs
|
|
94
|
+
* (`WalletTransaction<'Finalized'>`, spelled {@link FinalizedTx}) and a caller who has an unproven transaction finds
|
|
95
|
+
* out at compile time. It is covariant: a `FinalizedTx` is an {@link AnyTx}, which is what the operations that accept
|
|
96
|
+
* a transaction at any stage — reverting one, say — are stated in terms of.
|
|
97
|
+
* @example
|
|
98
|
+
* Sealing a transaction an application built for itself, and handing it back to the wallet:
|
|
99
|
+
*
|
|
100
|
+
* ```typescript
|
|
101
|
+
* import * as ledger from '@midnight-ntwrk/wallet-sdk/ledger/v9';
|
|
102
|
+
*
|
|
103
|
+
* const intent = ledger.Intent.new(ttl);
|
|
104
|
+
* const handle = WalletTransaction.adopt('Unproven', ledger.Transaction.fromParts(networkId, undefined, undefined, intent), version);
|
|
105
|
+
* ```;
|
|
106
|
+
*
|
|
107
|
+
* @typeParam TStage How far along the building of this transaction the handle is.
|
|
108
|
+
*/
|
|
109
|
+
export declare class WalletTransaction<out TStage extends TransactionStage = TransactionStage> {
|
|
110
|
+
#private;
|
|
111
|
+
/** The protocol version this transaction was built at, and so the only one it can be used at. */
|
|
112
|
+
readonly protocolVersion: ProtocolVersion.ProtocolVersion;
|
|
113
|
+
/** How far along the building of this transaction the handle is. */
|
|
114
|
+
readonly stage: TStage;
|
|
115
|
+
private constructor();
|
|
116
|
+
/**
|
|
117
|
+
* Seals a transaction built elsewhere into a handle.
|
|
118
|
+
*
|
|
119
|
+
* @remarks
|
|
120
|
+
* How an application that builds its own transactions — with the ledger version it imported from
|
|
121
|
+
* `@midnight-ntwrk/wallet-sdk/ledger/v8` or `/v9` — hands one to the wallet. The version passed here is a claim
|
|
122
|
+
* about which ledger version produced the transaction, and the SDK holds the caller to it: a handle stamped for one
|
|
123
|
+
* side of a protocol boundary is refused by a wallet acting on the other side, with a
|
|
124
|
+
* {@link ProtocolVersionMismatchError}. Until the fork, that means a transaction built with ledger-v9 is refused at
|
|
125
|
+
* run time, however well it type-checks.
|
|
126
|
+
* @param stage How far along the building of the transaction is.
|
|
127
|
+
* @param transaction The transaction, of whichever ledger version built it.
|
|
128
|
+
* @param protocolVersion The protocol version that ledger version serves.
|
|
129
|
+
* @returns The sealed handle.
|
|
130
|
+
*/
|
|
131
|
+
static adopt<TStage extends TransactionStage>(stage: TStage, transaction: Serializable, protocolVersion: ProtocolVersion.ProtocolVersion): WalletTransaction<TStage>;
|
|
132
|
+
/**
|
|
133
|
+
* Reads the transaction a handle carries, if it was built for a protocol version the caller can act at.
|
|
134
|
+
*
|
|
135
|
+
* @remarks
|
|
136
|
+
* The SDK's own way in, and the enforcement point for everything an application hands back: a caller states the range
|
|
137
|
+
* of protocol versions it speaks — a variant's activation range, or the era the chain is currently in — and gets
|
|
138
|
+
* the transaction only when the stamp falls inside it. The result type is the caller's to name, because the handle
|
|
139
|
+
* deliberately does not remember it; the range is the whole of the guarantee, which is why it is checked here
|
|
140
|
+
* rather than trusted anywhere else.
|
|
141
|
+
* @param handle The handle to read.
|
|
142
|
+
* @param accepted The range of protocol versions the caller can act at.
|
|
143
|
+
* @returns The carried transaction, or a {@link ProtocolVersionMismatchError} naming what was carried and what was
|
|
144
|
+
* asked for.
|
|
145
|
+
*/
|
|
146
|
+
static unwrapWithin<T>(handle: WalletTransaction, accepted: ProtocolVersion.ProtocolVersion.Range): Either.Either<T, ProtocolVersionMismatchError>;
|
|
147
|
+
/**
|
|
148
|
+
* Narrows a handle to a stage, for a caller that has one of unknown stage.
|
|
149
|
+
*
|
|
150
|
+
* @param handle The handle to narrow.
|
|
151
|
+
* @param stage The stage to narrow it to.
|
|
152
|
+
* @returns The same handle, typed at that stage, or nothing when it is at another.
|
|
153
|
+
*/
|
|
154
|
+
static atStage<TStage extends TransactionStage>(handle: WalletTransaction, stage: TStage): Option.Option<WalletTransaction<TStage>>;
|
|
155
|
+
/**
|
|
156
|
+
* Determines whether a value is a handle this SDK produced.
|
|
157
|
+
*
|
|
158
|
+
* @remarks
|
|
159
|
+
* Answers on the sealed transaction itself, so a record that merely has the same readable fields is not mistaken for
|
|
160
|
+
* one.
|
|
161
|
+
* @param value The value to test.
|
|
162
|
+
* @returns `true` when `value` is a {@link WalletTransaction}.
|
|
163
|
+
*/
|
|
164
|
+
static is(value: unknown): value is WalletTransaction;
|
|
165
|
+
/**
|
|
166
|
+
* Writes a handle out as an envelope that can cross a process boundary.
|
|
167
|
+
*
|
|
168
|
+
* @param handle The handle to write out.
|
|
169
|
+
* @returns The envelope. See {@link WireTransaction}.
|
|
170
|
+
*/
|
|
171
|
+
static toWire(handle: WalletTransaction): WireTransaction;
|
|
172
|
+
/**
|
|
173
|
+
* Reads an envelope back into a handle, decoding its bytes with whichever ledger version the envelope names.
|
|
174
|
+
*
|
|
175
|
+
* @remarks
|
|
176
|
+
* The decoder is the caller's because the choice of ledger version is: this package has none, and picking one here
|
|
177
|
+
* would be the hardcoded version the handle exists to avoid. It is handed the version and stage the envelope
|
|
178
|
+
* declares so it can route on them, and it may raise — a deserializer meeting bytes of another ledger version does
|
|
179
|
+
* — which is reported as a {@link WireFormatError} rather than escaping.
|
|
180
|
+
* @param value The envelope, as received.
|
|
181
|
+
* @param decode Produces the transaction from the envelope's bytes, at the version and stage it names.
|
|
182
|
+
* @returns The handle, or a {@link WireFormatError} describing what could not be read.
|
|
183
|
+
*/
|
|
184
|
+
static fromWire(value: unknown, decode: (bytes: Uint8Array, protocolVersion: ProtocolVersion.ProtocolVersion, stage: TransactionStage) => Serializable): Either.Either<WalletTransaction, WireFormatError>;
|
|
185
|
+
/**
|
|
186
|
+
* Writes the carried transaction out as bytes.
|
|
187
|
+
*
|
|
188
|
+
* @remarks
|
|
189
|
+
* The one thing every ledger version's transaction can do identically, and so the one thing a handle can offer
|
|
190
|
+
* without knowing which produced it. What the bytes mean still depends on {@link protocolVersion}.
|
|
191
|
+
* @returns The transaction's own serialization.
|
|
192
|
+
*/
|
|
193
|
+
serialize(): SerializedTransaction.SerializedTransaction;
|
|
194
|
+
}
|
|
195
|
+
export declare namespace WalletTransaction {
|
|
196
|
+
/** How far along the building of a transaction a handle is. See {@link TransactionStage}. */
|
|
197
|
+
type Stage = TransactionStage;
|
|
198
|
+
}
|
|
199
|
+
/** A transaction that has been built but not proved. */
|
|
200
|
+
export type UnprovenTx = WalletTransaction<'Unproven'>;
|
|
201
|
+
/** A transaction that has been proved but not yet bound to its own contents. */
|
|
202
|
+
export type UnboundTx = WalletTransaction<'Unbound'>;
|
|
203
|
+
/** A transaction that is proved, signed and bound: the only shape the network takes. */
|
|
204
|
+
export type FinalizedTx = WalletTransaction<'Finalized'>;
|
|
205
|
+
/** A transaction at whichever stage it happens to have reached. */
|
|
206
|
+
export type AnyTx = WalletTransaction<TransactionStage>;
|
|
207
|
+
export {};
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
// This file is part of MIDNIGHT-WALLET-SDK.
|
|
2
|
+
// Copyright (C) Midnight Foundation
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// You may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
/**
|
|
14
|
+
* The one transaction type an application carries, whichever ledger version produced it.
|
|
15
|
+
*
|
|
16
|
+
* @remarks
|
|
17
|
+
* An SDK that runs one ledger version before a protocol boundary and another from it has a problem its callers should
|
|
18
|
+
* never have to solve: a transaction built before the boundary and a transaction built after it are objects of two
|
|
19
|
+
* different runtimes, with two different types, and no conversion between them. Naming either type in a public
|
|
20
|
+
* signature is naming a ledger version, which is the thing that breaks when the chain moves on.
|
|
21
|
+
*
|
|
22
|
+
* So the wallet layer hands out a handle instead. It seals the transaction away — an application cannot reach it, and
|
|
23
|
+
* so cannot come to depend on which version made it — and carries the protocol version it was built at as ordinary
|
|
24
|
+
* readable data. That stamp is what the SDK routes on: which prover proves it, which trait recognises it, which
|
|
25
|
+
* variant may unwrap it at all.
|
|
26
|
+
*/
|
|
27
|
+
import * as Data from 'effect/Data';
|
|
28
|
+
import * as Either from 'effect/Either';
|
|
29
|
+
import * as Option from 'effect/Option';
|
|
30
|
+
import * as ParseResult from 'effect/ParseResult';
|
|
31
|
+
import * as Schema from 'effect/Schema';
|
|
32
|
+
import * as ProtocolVersion from './ProtocolVersion.js';
|
|
33
|
+
import * as SerializedTransaction from './SerializedTransaction.js';
|
|
34
|
+
/**
|
|
35
|
+
* The three stages, as the one thing that decides what they are.
|
|
36
|
+
*
|
|
37
|
+
* @remarks
|
|
38
|
+
* Declared here rather than beside the wire envelope that reads it, because {@link TransactionStage} — the type an
|
|
39
|
+
* application names — is read back off it. Stating the three words once means the type a caller is held to and the
|
|
40
|
+
* values an envelope is parsed against cannot come to disagree: adding a stage to one adds it to the other.
|
|
41
|
+
*/
|
|
42
|
+
const TransactionStageSchema = Schema.Literal('Unproven', 'Unbound', 'Finalized');
|
|
43
|
+
/**
|
|
44
|
+
* Raised when a transaction is used at a protocol version other than the one it was built for.
|
|
45
|
+
*
|
|
46
|
+
* @remarks
|
|
47
|
+
* The bytes of a transaction are fixed when it is built, by the ledger version that built them. A protocol boundary
|
|
48
|
+
* does not rewrite them, so a transaction authored on one side of the boundary cannot be understood on the other —
|
|
49
|
+
* not merged, not proved, not submitted. This is what the SDK answers with rather than handing a transaction to a
|
|
50
|
+
* ledger version that would misread it.
|
|
51
|
+
*/
|
|
52
|
+
export class ProtocolVersionMismatchError extends Data.TaggedError('@midnight-ntwrk/wallet-sdk-abstractions/WalletTransaction/ProtocolVersionMismatchError') {
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Raised when bytes cannot be read as a transaction of a version this SDK knows — whether they arrived in a wire
|
|
56
|
+
* envelope that names their version, or bare, to be read at the version the reader is acting at.
|
|
57
|
+
*/
|
|
58
|
+
export class WireFormatError extends Data.TaggedError('@midnight-ntwrk/wallet-sdk-abstractions/WalletTransaction/WireFormatError') {
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The envelope's own format version.
|
|
62
|
+
*
|
|
63
|
+
* @remarks
|
|
64
|
+
* Deliberately separate from the protocol version it carries. A reader has to be able to tell "I do not know this
|
|
65
|
+
* envelope" from "I do not know this ledger version", because only the second is a chain fact.
|
|
66
|
+
*/
|
|
67
|
+
const WIRE_FORMAT = 1;
|
|
68
|
+
/**
|
|
69
|
+
* The envelope a handle crosses a process boundary in.
|
|
70
|
+
*
|
|
71
|
+
* @remarks
|
|
72
|
+
* Everything in it is JSON-safe, because the boundary it was designed for — a dApp connector — is a JSON one: the
|
|
73
|
+
* protocol version is a decimal string rather than a `bigint`, and the transaction is hex rather than bytes. What
|
|
74
|
+
* travels is exactly what a reader needs to decide whether it can read the rest at all: the envelope format, the
|
|
75
|
+
* protocol version the transaction was built at, the stage it reached, and the transaction's own serialization.
|
|
76
|
+
*
|
|
77
|
+
* Kept deliberately small. The connector contract is an open question at the time of writing, and an envelope that
|
|
78
|
+
* guesses at it would be harder to reconcile than one that carries the minimum.
|
|
79
|
+
*/
|
|
80
|
+
const WireTransactionSchema = Schema.Struct({
|
|
81
|
+
wireFormat: Schema.Literal(WIRE_FORMAT),
|
|
82
|
+
protocolVersion: ProtocolVersion.ProtocolVersionSchema,
|
|
83
|
+
stage: TransactionStageSchema,
|
|
84
|
+
transaction: Schema.Uint8ArrayFromHex,
|
|
85
|
+
});
|
|
86
|
+
const encodeWire = Schema.encodeSync(WireTransactionSchema);
|
|
87
|
+
const decodeWire = Schema.decodeUnknownEither(WireTransactionSchema);
|
|
88
|
+
/**
|
|
89
|
+
* A transaction the SDK produced or adopted, sealed together with the protocol version it was built at.
|
|
90
|
+
*
|
|
91
|
+
* @remarks
|
|
92
|
+
* A class rather than a record, and the one place in this package where that earns its keep: a `#private` field is the
|
|
93
|
+
* only way to carry something an application provably cannot reach, and reaching the wrapped transaction is exactly
|
|
94
|
+
* what would re-introduce the ledger-version dependency the handle exists to remove. Nothing about it mutates — the
|
|
95
|
+
* fields are read-only and every operation returns a new value.
|
|
96
|
+
*
|
|
97
|
+
* The stage is a type parameter as well as a field, so a signature can ask for the shape it needs
|
|
98
|
+
* (`WalletTransaction<'Finalized'>`, spelled {@link FinalizedTx}) and a caller who has an unproven transaction finds
|
|
99
|
+
* out at compile time. It is covariant: a `FinalizedTx` is an {@link AnyTx}, which is what the operations that accept
|
|
100
|
+
* a transaction at any stage — reverting one, say — are stated in terms of.
|
|
101
|
+
* @example
|
|
102
|
+
* Sealing a transaction an application built for itself, and handing it back to the wallet:
|
|
103
|
+
*
|
|
104
|
+
* ```typescript
|
|
105
|
+
* import * as ledger from '@midnight-ntwrk/wallet-sdk/ledger/v9';
|
|
106
|
+
*
|
|
107
|
+
* const intent = ledger.Intent.new(ttl);
|
|
108
|
+
* const handle = WalletTransaction.adopt('Unproven', ledger.Transaction.fromParts(networkId, undefined, undefined, intent), version);
|
|
109
|
+
* ```;
|
|
110
|
+
*
|
|
111
|
+
* @typeParam TStage How far along the building of this transaction the handle is.
|
|
112
|
+
*/
|
|
113
|
+
export class WalletTransaction {
|
|
114
|
+
/** The protocol version this transaction was built at, and so the only one it can be used at. */
|
|
115
|
+
protocolVersion;
|
|
116
|
+
/** How far along the building of this transaction the handle is. */
|
|
117
|
+
stage;
|
|
118
|
+
/** The transaction itself, reachable only from inside this class. */
|
|
119
|
+
#carried;
|
|
120
|
+
constructor(protocolVersion, stage, carried) {
|
|
121
|
+
this.protocolVersion = protocolVersion;
|
|
122
|
+
this.stage = stage;
|
|
123
|
+
this.#carried = carried;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Seals a transaction built elsewhere into a handle.
|
|
127
|
+
*
|
|
128
|
+
* @remarks
|
|
129
|
+
* How an application that builds its own transactions — with the ledger version it imported from
|
|
130
|
+
* `@midnight-ntwrk/wallet-sdk/ledger/v8` or `/v9` — hands one to the wallet. The version passed here is a claim
|
|
131
|
+
* about which ledger version produced the transaction, and the SDK holds the caller to it: a handle stamped for one
|
|
132
|
+
* side of a protocol boundary is refused by a wallet acting on the other side, with a
|
|
133
|
+
* {@link ProtocolVersionMismatchError}. Until the fork, that means a transaction built with ledger-v9 is refused at
|
|
134
|
+
* run time, however well it type-checks.
|
|
135
|
+
* @param stage How far along the building of the transaction is.
|
|
136
|
+
* @param transaction The transaction, of whichever ledger version built it.
|
|
137
|
+
* @param protocolVersion The protocol version that ledger version serves.
|
|
138
|
+
* @returns The sealed handle.
|
|
139
|
+
*/
|
|
140
|
+
static adopt(stage, transaction, protocolVersion) {
|
|
141
|
+
return new WalletTransaction(protocolVersion, stage, transaction);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Reads the transaction a handle carries, if it was built for a protocol version the caller can act at.
|
|
145
|
+
*
|
|
146
|
+
* @remarks
|
|
147
|
+
* The SDK's own way in, and the enforcement point for everything an application hands back: a caller states the range
|
|
148
|
+
* of protocol versions it speaks — a variant's activation range, or the era the chain is currently in — and gets
|
|
149
|
+
* the transaction only when the stamp falls inside it. The result type is the caller's to name, because the handle
|
|
150
|
+
* deliberately does not remember it; the range is the whole of the guarantee, which is why it is checked here
|
|
151
|
+
* rather than trusted anywhere else.
|
|
152
|
+
* @param handle The handle to read.
|
|
153
|
+
* @param accepted The range of protocol versions the caller can act at.
|
|
154
|
+
* @returns The carried transaction, or a {@link ProtocolVersionMismatchError} naming what was carried and what was
|
|
155
|
+
* asked for.
|
|
156
|
+
*/
|
|
157
|
+
static unwrapWithin(handle, accepted) {
|
|
158
|
+
if (!ProtocolVersion.withinRange(handle.protocolVersion, accepted)) {
|
|
159
|
+
const [from, to] = accepted;
|
|
160
|
+
return Either.left(new ProtocolVersionMismatchError({
|
|
161
|
+
message: `This transaction was built for protocol version ${handle.protocolVersion}, and can only be used at ` +
|
|
162
|
+
`that version: its bytes were fixed by the ledger version that produced them. The caller acts at ` +
|
|
163
|
+
`protocol versions ${from} up to (but not including) ${to}. Build the transaction again on the side of ` +
|
|
164
|
+
`the protocol boundary it is to be used on.`,
|
|
165
|
+
authoredFor: handle.protocolVersion,
|
|
166
|
+
accepted,
|
|
167
|
+
stage: handle.stage,
|
|
168
|
+
}));
|
|
169
|
+
}
|
|
170
|
+
// The carried type is erased when a transaction is sealed, and deliberately: remembering it would put a ledger
|
|
171
|
+
// version back into the handle's own type. The version check above is the whole of what can be guaranteed here,
|
|
172
|
+
// and it is the same guarantee the caller would have had to make for itself.
|
|
173
|
+
return Either.right(handle.#carried);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Narrows a handle to a stage, for a caller that has one of unknown stage.
|
|
177
|
+
*
|
|
178
|
+
* @param handle The handle to narrow.
|
|
179
|
+
* @param stage The stage to narrow it to.
|
|
180
|
+
* @returns The same handle, typed at that stage, or nothing when it is at another.
|
|
181
|
+
*/
|
|
182
|
+
static atStage(handle, stage) {
|
|
183
|
+
return handle.stage === stage
|
|
184
|
+
? // Narrowed by the value that the type parameter names; `stage` is the only thing distinguishing the two types.
|
|
185
|
+
Option.some(handle)
|
|
186
|
+
: Option.none();
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Determines whether a value is a handle this SDK produced.
|
|
190
|
+
*
|
|
191
|
+
* @remarks
|
|
192
|
+
* Answers on the sealed transaction itself, so a record that merely has the same readable fields is not mistaken for
|
|
193
|
+
* one.
|
|
194
|
+
* @param value The value to test.
|
|
195
|
+
* @returns `true` when `value` is a {@link WalletTransaction}.
|
|
196
|
+
*/
|
|
197
|
+
static is(value) {
|
|
198
|
+
return typeof value === 'object' && value !== null && #carried in value;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Writes a handle out as an envelope that can cross a process boundary.
|
|
202
|
+
*
|
|
203
|
+
* @param handle The handle to write out.
|
|
204
|
+
* @returns The envelope. See {@link WireTransaction}.
|
|
205
|
+
*/
|
|
206
|
+
static toWire(handle) {
|
|
207
|
+
return encodeWire({
|
|
208
|
+
wireFormat: WIRE_FORMAT,
|
|
209
|
+
protocolVersion: handle.protocolVersion,
|
|
210
|
+
stage: handle.stage,
|
|
211
|
+
transaction: handle.serialize(),
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Reads an envelope back into a handle, decoding its bytes with whichever ledger version the envelope names.
|
|
216
|
+
*
|
|
217
|
+
* @remarks
|
|
218
|
+
* The decoder is the caller's because the choice of ledger version is: this package has none, and picking one here
|
|
219
|
+
* would be the hardcoded version the handle exists to avoid. It is handed the version and stage the envelope
|
|
220
|
+
* declares so it can route on them, and it may raise — a deserializer meeting bytes of another ledger version does
|
|
221
|
+
* — which is reported as a {@link WireFormatError} rather than escaping.
|
|
222
|
+
* @param value The envelope, as received.
|
|
223
|
+
* @param decode Produces the transaction from the envelope's bytes, at the version and stage it names.
|
|
224
|
+
* @returns The handle, or a {@link WireFormatError} describing what could not be read.
|
|
225
|
+
*/
|
|
226
|
+
static fromWire(value, decode) {
|
|
227
|
+
return decodeWire(value).pipe(Either.mapLeft((error) => new WireFormatError({
|
|
228
|
+
message: `This is not a transaction envelope this SDK can read: ${ParseResult.TreeFormatter.formatErrorSync(error)}`,
|
|
229
|
+
cause: error,
|
|
230
|
+
})), Either.flatMap((envelope) => Either.try({
|
|
231
|
+
try: () => decode(envelope.transaction, envelope.protocolVersion, envelope.stage),
|
|
232
|
+
catch: (cause) => new WireFormatError({
|
|
233
|
+
message: `The envelope declares protocol version ${envelope.protocolVersion}, but its bytes could not be ` +
|
|
234
|
+
`read as a ${envelope.stage.toLowerCase()} transaction of that version.`,
|
|
235
|
+
cause,
|
|
236
|
+
}),
|
|
237
|
+
}).pipe(Either.map((transaction) => WalletTransaction.adopt(envelope.stage, transaction, envelope.protocolVersion)))));
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Writes the carried transaction out as bytes.
|
|
241
|
+
*
|
|
242
|
+
* @remarks
|
|
243
|
+
* The one thing every ledger version's transaction can do identically, and so the one thing a handle can offer
|
|
244
|
+
* without knowing which produced it. What the bytes mean still depends on {@link protocolVersion}.
|
|
245
|
+
* @returns The transaction's own serialization.
|
|
246
|
+
*/
|
|
247
|
+
serialize() {
|
|
248
|
+
return SerializedTransaction.from(this.#carried);
|
|
249
|
+
}
|
|
250
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ export * as ProtocolState from './ProtocolState.js';
|
|
|
5
5
|
export * as ProtocolVersion from './ProtocolVersion.js';
|
|
6
6
|
export * as NetworkId from './NetworkId.js';
|
|
7
7
|
export * as SyncProgress from './SyncProgress.js';
|
|
8
|
+
export * from './TokenType.js';
|
|
9
|
+
export * from './WalletTransaction.js';
|
|
8
10
|
export * from './InMemoryTransactionHistoryStorage.js';
|
|
9
11
|
export * from './NoOpTransactionHistoryStorage.js';
|
|
10
12
|
export * as TransactionHistoryStorage from './TransactionHistoryStorage.js';
|
package/dist/index.js
CHANGED
|
@@ -17,6 +17,8 @@ export * as ProtocolState from './ProtocolState.js';
|
|
|
17
17
|
export * as ProtocolVersion from './ProtocolVersion.js';
|
|
18
18
|
export * as NetworkId from './NetworkId.js';
|
|
19
19
|
export * as SyncProgress from './SyncProgress.js';
|
|
20
|
+
export * from './TokenType.js';
|
|
21
|
+
export * from './WalletTransaction.js';
|
|
20
22
|
export * from './InMemoryTransactionHistoryStorage.js';
|
|
21
23
|
export * from './NoOpTransactionHistoryStorage.js';
|
|
22
24
|
export * as TransactionHistoryStorage from './TransactionHistoryStorage.js';
|
package/package.json
CHANGED