@midnight-ntwrk/wallet-sdk-capabilities 2.0.0 → 3.0.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/balancer/Balancer.d.ts +24 -0
- package/dist/balancer/Balancer.js +68 -0
- package/dist/balancer/CounterOffer.d.ts +19 -0
- package/dist/balancer/CounterOffer.js +65 -0
- package/dist/balancer/Imbalances.d.ts +19 -0
- package/dist/balancer/Imbalances.js +48 -0
- package/dist/balancer/index.d.ts +3 -0
- package/dist/balancer/index.js +15 -0
- package/dist/index.d.ts +1 -38
- package/dist/index.js +13 -124
- package/package.json +30 -15
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
# Midnight Wallet SDK Capabilities
|
|
2
2
|
|
|
3
|
-
This package provides a range of capabilities used only internally by the wallet when implementing
|
|
3
|
+
This package provides a range of capabilities used only internally by the wallet when implementing
|
|
4
|
+
`@midnight-ntwrk/wallet-api`. The version of the package is focused only in transaction balancing.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { CounterOffer, TransactionCostModel } from './CounterOffer.js';
|
|
2
|
+
import { CoinRecipe, Imbalances, TokenType, TokenValue } from './Imbalances.js';
|
|
3
|
+
export declare class InsufficientFundsError extends Error {
|
|
4
|
+
readonly tokenType: TokenType;
|
|
5
|
+
constructor(tokenType: TokenType);
|
|
6
|
+
}
|
|
7
|
+
export interface BalanceRecipe<TInput extends CoinRecipe, TOutput extends CoinRecipe> {
|
|
8
|
+
inputs: TInput[];
|
|
9
|
+
outputs: TOutput[];
|
|
10
|
+
}
|
|
11
|
+
export type CoinSelection<TInput extends CoinRecipe> = (coins: readonly TInput[], tokenType: TokenType, amountNeeded: TokenValue, costModel: TransactionCostModel) => TInput | undefined;
|
|
12
|
+
export type BalanceRecipeProps<TInput extends CoinRecipe, TOutput extends CoinRecipe> = {
|
|
13
|
+
coins: TInput[];
|
|
14
|
+
initialImbalances: Imbalances;
|
|
15
|
+
transactionCostModel: TransactionCostModel;
|
|
16
|
+
feeTokenType: string;
|
|
17
|
+
createOutput: (coin: CoinRecipe) => TOutput;
|
|
18
|
+
isCoinEqual: (a: TInput, b: TInput) => boolean;
|
|
19
|
+
coinSelection?: CoinSelection<TInput> | undefined;
|
|
20
|
+
targetImbalances?: Imbalances;
|
|
21
|
+
};
|
|
22
|
+
export declare const getBalanceRecipe: <TInput extends CoinRecipe, TOutput extends CoinRecipe>({ coins, initialImbalances, transactionCostModel, feeTokenType, createOutput, coinSelection, isCoinEqual, targetImbalances, }: BalanceRecipeProps<TInput, TOutput>) => BalanceRecipe<TInput, TOutput>;
|
|
23
|
+
export declare const createCounterOffer: <TInput extends CoinRecipe, TOutput extends CoinRecipe>(coins: TInput[], initialImbalances: Imbalances, transactionCostModel: TransactionCostModel, feeTokenType: string, coinSelection: CoinSelection<TInput>, createOutput: (coin: CoinRecipe) => TOutput, isCoinEqual: (a: TInput, b: TInput) => boolean, targetImbalances?: Imbalances) => CounterOffer<TInput, TOutput>;
|
|
24
|
+
export declare const chooseCoin: <TInput extends CoinRecipe>(coins: readonly TInput[], tokenType: TokenType) => TInput | undefined;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// This file is part of MIDNIGHT-WALLET-SDK.
|
|
2
|
+
// Copyright (C) 2025 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
|
+
import { CounterOffer } from './CounterOffer.js';
|
|
14
|
+
export class InsufficientFundsError extends Error {
|
|
15
|
+
tokenType;
|
|
16
|
+
constructor(tokenType) {
|
|
17
|
+
super(`Insufficient Funds: could not balance ${tokenType}`);
|
|
18
|
+
this.tokenType = tokenType;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export const getBalanceRecipe = ({ coins, initialImbalances, transactionCostModel, feeTokenType, createOutput, coinSelection, isCoinEqual, targetImbalances, }) => {
|
|
22
|
+
const counterOffer = createCounterOffer(coins, initialImbalances, transactionCostModel, feeTokenType, coinSelection ?? chooseCoin, createOutput, isCoinEqual, targetImbalances);
|
|
23
|
+
return {
|
|
24
|
+
inputs: counterOffer.inputs,
|
|
25
|
+
outputs: counterOffer.outputs,
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
export const createCounterOffer = (coins, initialImbalances, transactionCostModel, feeTokenType, coinSelection, createOutput, isCoinEqual, targetImbalances = new Map()) => {
|
|
29
|
+
const counterOffer = new CounterOffer(initialImbalances, transactionCostModel, feeTokenType, targetImbalances);
|
|
30
|
+
let imbalance;
|
|
31
|
+
while ((imbalance = counterOffer.findNonNativeImbalance())) {
|
|
32
|
+
coins = doBalance(imbalance, coins, counterOffer, coinSelection, createOutput, isCoinEqual);
|
|
33
|
+
}
|
|
34
|
+
while ((imbalance = counterOffer.findNativeImbalance())) {
|
|
35
|
+
coins = doBalance(imbalance, coins, counterOffer, coinSelection, createOutput, isCoinEqual);
|
|
36
|
+
}
|
|
37
|
+
return counterOffer;
|
|
38
|
+
};
|
|
39
|
+
const doBalance = (imbalance, coins, counterOffer, coinSelection, createOutput, isCoinEqual) => {
|
|
40
|
+
const [tokenType, imbalanceAmount] = imbalance;
|
|
41
|
+
const shouldAddOutput = (tokenType === counterOffer.feeTokenType &&
|
|
42
|
+
imbalanceAmount >=
|
|
43
|
+
counterOffer.getTargetImbalance(counterOffer.feeTokenType) +
|
|
44
|
+
counterOffer.transactionCostModel.outputFeeOverhead) ||
|
|
45
|
+
(tokenType !== counterOffer.feeTokenType && imbalanceAmount > counterOffer.getTargetImbalance(tokenType));
|
|
46
|
+
if (shouldAddOutput) {
|
|
47
|
+
const output = createOutput({
|
|
48
|
+
type: tokenType,
|
|
49
|
+
value: imbalanceAmount - counterOffer.getTargetImbalance(tokenType),
|
|
50
|
+
});
|
|
51
|
+
counterOffer.addOutput(output);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
const coin = coinSelection(coins, tokenType, imbalanceAmount, counterOffer.transactionCostModel);
|
|
55
|
+
if (typeof coin === 'undefined') {
|
|
56
|
+
throw new InsufficientFundsError(tokenType);
|
|
57
|
+
}
|
|
58
|
+
counterOffer.addInput(coin);
|
|
59
|
+
coins = coins.filter((c) => !isCoinEqual(c, coin));
|
|
60
|
+
}
|
|
61
|
+
return coins;
|
|
62
|
+
};
|
|
63
|
+
export const chooseCoin = (coins, tokenType) => {
|
|
64
|
+
return coins
|
|
65
|
+
.filter((coin) => coin.type === tokenType)
|
|
66
|
+
.sort((a, b) => Number(a.value - b.value))
|
|
67
|
+
.at(0);
|
|
68
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { CoinRecipe, Imbalance, Imbalances, TokenType } from './Imbalances.js';
|
|
2
|
+
export interface TransactionCostModel {
|
|
3
|
+
inputFeeOverhead: bigint;
|
|
4
|
+
outputFeeOverhead: bigint;
|
|
5
|
+
}
|
|
6
|
+
export declare class CounterOffer<TInput extends CoinRecipe, TOutput extends CoinRecipe> {
|
|
7
|
+
readonly imbalances: Imbalances;
|
|
8
|
+
readonly transactionCostModel: TransactionCostModel;
|
|
9
|
+
readonly feeTokenType: string;
|
|
10
|
+
readonly inputs: TInput[];
|
|
11
|
+
readonly outputs: TOutput[];
|
|
12
|
+
readonly targetImbalances: Imbalances;
|
|
13
|
+
constructor(imbalances: Imbalances, transactionCostModel: TransactionCostModel, feeTokenType: string, targetImbalances: Imbalances);
|
|
14
|
+
getTargetImbalance(tokenType: TokenType): bigint;
|
|
15
|
+
findNonNativeImbalance(): Imbalance | undefined;
|
|
16
|
+
findNativeImbalance(): Imbalance | undefined;
|
|
17
|
+
addInput(input: TInput): void;
|
|
18
|
+
addOutput(output: TOutput): void;
|
|
19
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// This file is part of MIDNIGHT-WALLET-SDK.
|
|
2
|
+
// Copyright (C) 2025 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
|
+
import { Imbalances } from './Imbalances.js';
|
|
14
|
+
export class CounterOffer {
|
|
15
|
+
imbalances;
|
|
16
|
+
transactionCostModel;
|
|
17
|
+
feeTokenType;
|
|
18
|
+
inputs;
|
|
19
|
+
outputs;
|
|
20
|
+
targetImbalances;
|
|
21
|
+
constructor(imbalances, transactionCostModel, feeTokenType, targetImbalances) {
|
|
22
|
+
this.imbalances = Imbalances.ensureZerosFor(imbalances, Imbalances.typeSet(targetImbalances));
|
|
23
|
+
this.transactionCostModel = transactionCostModel;
|
|
24
|
+
this.feeTokenType = feeTokenType;
|
|
25
|
+
this.inputs = [];
|
|
26
|
+
this.outputs = [];
|
|
27
|
+
this.targetImbalances = targetImbalances;
|
|
28
|
+
}
|
|
29
|
+
getTargetImbalance(tokenType) {
|
|
30
|
+
return this.targetImbalances.get(tokenType) ?? 0n;
|
|
31
|
+
}
|
|
32
|
+
findNonNativeImbalance() {
|
|
33
|
+
return Array.from(this.imbalances.entries())
|
|
34
|
+
.filter(([tokenType]) => tokenType !== this.feeTokenType)
|
|
35
|
+
.find(([tokenType, value]) => value !== this.getTargetImbalance(tokenType));
|
|
36
|
+
}
|
|
37
|
+
findNativeImbalance() {
|
|
38
|
+
if (!this.feeTokenType) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
const nativeImbalance = this.imbalances.get(this.feeTokenType);
|
|
42
|
+
if (nativeImbalance !== undefined && nativeImbalance !== this.getTargetImbalance(this.feeTokenType)) {
|
|
43
|
+
return [this.feeTokenType, nativeImbalance];
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
addInput(input) {
|
|
48
|
+
this.inputs.push(input);
|
|
49
|
+
const imbalance = this.imbalances.get(input.type) || 0n;
|
|
50
|
+
this.imbalances.set(input.type, imbalance + input.value);
|
|
51
|
+
const nativeImbalance = this.imbalances.get(this.feeTokenType) || 0n;
|
|
52
|
+
this.imbalances.set(this.feeTokenType, nativeImbalance - this.transactionCostModel.inputFeeOverhead);
|
|
53
|
+
}
|
|
54
|
+
addOutput(output) {
|
|
55
|
+
const imbalance = this.imbalances.get(output.type) || 0n;
|
|
56
|
+
const subtractFee = output.type === this.feeTokenType ? this.transactionCostModel.outputFeeOverhead : 0n;
|
|
57
|
+
const absoluteCoinValue = output.value < 0n ? -output.value : output.value;
|
|
58
|
+
this.outputs.push({ ...output, type: output.type, value: absoluteCoinValue - subtractFee });
|
|
59
|
+
this.imbalances.set(output.type, imbalance - absoluteCoinValue);
|
|
60
|
+
if (output.type !== this.feeTokenType) {
|
|
61
|
+
const nativeImbalance = this.imbalances.get(this.feeTokenType) || 0n;
|
|
62
|
+
this.imbalances.set(this.feeTokenType, nativeImbalance - this.transactionCostModel.outputFeeOverhead);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type TokenType = string;
|
|
2
|
+
export type TokenValue = bigint;
|
|
3
|
+
export interface CoinRecipe {
|
|
4
|
+
type: TokenType;
|
|
5
|
+
value: TokenValue;
|
|
6
|
+
}
|
|
7
|
+
export type Imbalance = [TokenType, TokenValue];
|
|
8
|
+
export type Imbalances = Map<TokenType, TokenValue>;
|
|
9
|
+
export declare const Imbalances: {
|
|
10
|
+
empty: () => Imbalances;
|
|
11
|
+
fromEntry: (tokenType: TokenType, value: bigint) => Imbalances;
|
|
12
|
+
fromEntries: (entries: Iterable<readonly [TokenType, bigint]>) => Imbalances;
|
|
13
|
+
fromMap: (map: Map<TokenType, bigint>) => Imbalances;
|
|
14
|
+
fromMaybeMap: (map: Map<TokenType, bigint> | undefined) => Imbalances;
|
|
15
|
+
getValue: (map: Imbalances, tokenType: TokenType) => bigint;
|
|
16
|
+
typeSet: (map: Imbalances) => Set<TokenType>;
|
|
17
|
+
ensureZerosFor(map: Imbalances, types: Iterable<TokenType>): Imbalances;
|
|
18
|
+
merge: (a: Imbalances, b: Imbalances) => Imbalances;
|
|
19
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export const Imbalances = new (class {
|
|
2
|
+
empty = () => {
|
|
3
|
+
return new Map();
|
|
4
|
+
};
|
|
5
|
+
fromEntry = (tokenType, value) => {
|
|
6
|
+
return new Map([[tokenType, value]]);
|
|
7
|
+
};
|
|
8
|
+
fromEntries = (entries) => {
|
|
9
|
+
const out = new Map();
|
|
10
|
+
for (const [tokenType, value] of entries) {
|
|
11
|
+
const existingValue = this.getValue(out, tokenType);
|
|
12
|
+
out.set(tokenType, value + existingValue);
|
|
13
|
+
}
|
|
14
|
+
return out;
|
|
15
|
+
};
|
|
16
|
+
fromMap = (map) => {
|
|
17
|
+
return this.fromEntries(map.entries());
|
|
18
|
+
};
|
|
19
|
+
fromMaybeMap = (map) => {
|
|
20
|
+
return this.fromMap(map ?? new Map());
|
|
21
|
+
};
|
|
22
|
+
getValue = (map, tokenType) => {
|
|
23
|
+
return map.get(tokenType) ?? 0n;
|
|
24
|
+
};
|
|
25
|
+
typeSet = (map) => {
|
|
26
|
+
return new Set(map.keys());
|
|
27
|
+
};
|
|
28
|
+
ensureZerosFor(map, types) {
|
|
29
|
+
const out = this.fromEntries(map.entries());
|
|
30
|
+
for (const tokenType of types) {
|
|
31
|
+
const existingValue = this.getValue(out, tokenType);
|
|
32
|
+
out.set(tokenType, existingValue);
|
|
33
|
+
}
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
merge = (a, b) => {
|
|
37
|
+
const allTokenTypes = this.typeSet(a).union(this.typeSet(b));
|
|
38
|
+
return this.fromEntries(allTokenTypes
|
|
39
|
+
.values()
|
|
40
|
+
.map((tokenType) => {
|
|
41
|
+
const aValue = this.getValue(a, tokenType);
|
|
42
|
+
const bValue = this.getValue(b, tokenType);
|
|
43
|
+
return [tokenType, aValue + bValue];
|
|
44
|
+
})
|
|
45
|
+
.filter(([, value]) => value !== 0n)
|
|
46
|
+
.toArray());
|
|
47
|
+
};
|
|
48
|
+
})();
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// This file is part of MIDNIGHT-WALLET-SDK.
|
|
2
|
+
// Copyright (C) 2025 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
|
+
export * from './Balancer.js';
|
|
14
|
+
export * from './CounterOffer.js';
|
|
15
|
+
export * from './Imbalances.js';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,38 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
interface CoinRecipe {
|
|
4
|
-
type: string;
|
|
5
|
-
value: bigint;
|
|
6
|
-
}
|
|
7
|
-
type Imbalance = [string, bigint];
|
|
8
|
-
type Imbalances = Map<string, bigint>;
|
|
9
|
-
declare const emptyImbalances: Imbalances;
|
|
10
|
-
declare const createImbalances: (imbalances: [string, bigint][]) => Imbalances;
|
|
11
|
-
declare const mergeImbalances: (a: Imbalances, b: Imbalances) => Imbalances;
|
|
12
|
-
|
|
13
|
-
interface TransactionCostModel {
|
|
14
|
-
inputFeeOverhead: bigint;
|
|
15
|
-
outputFeeOverhead: bigint;
|
|
16
|
-
}
|
|
17
|
-
declare class CounterOffer {
|
|
18
|
-
readonly imbalances: Imbalances;
|
|
19
|
-
readonly transactionCostModel: TransactionCostModel;
|
|
20
|
-
readonly feeTokenType: string;
|
|
21
|
-
readonly inputsRecipe: QualifiedCoinInfo[];
|
|
22
|
-
readonly outputsRecipe: CoinInfo[];
|
|
23
|
-
constructor(imbalances: Imbalances, transactionCostModel: TransactionCostModel, feeTokenType: string);
|
|
24
|
-
findNonNativeImbalance(): Imbalance | undefined;
|
|
25
|
-
findNativeImbalance(): Imbalance | undefined;
|
|
26
|
-
addInput(input: QualifiedCoinInfo): void;
|
|
27
|
-
addOutput(output: CoinRecipe): void;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
interface BalanceRecipe {
|
|
31
|
-
inputs: QualifiedCoinInfo[];
|
|
32
|
-
outputs: CoinInfo[];
|
|
33
|
-
}
|
|
34
|
-
declare const getBalanceRecipe: (coins: QualifiedCoinInfo[], targetImbalances: Imbalances, transactionCostModel: TransactionCostModel, feeTokenType: string) => BalanceRecipe;
|
|
35
|
-
declare const createCounterOffer: (coins: QualifiedCoinInfo[], targetImbalances: Imbalances, transactionCostModel: TransactionCostModel, feeTokenType: string) => CounterOffer;
|
|
36
|
-
|
|
37
|
-
export { CounterOffer, createCounterOffer, createImbalances, emptyImbalances, getBalanceRecipe, mergeImbalances };
|
|
38
|
-
export type { BalanceRecipe, CoinRecipe, Imbalance, Imbalances, TransactionCostModel };
|
|
1
|
+
export * from './balancer/index.js';
|
package/dist/index.js
CHANGED
|
@@ -1,124 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
this.outputsRecipe = [];
|
|
15
|
-
}
|
|
16
|
-
findNonNativeImbalance() {
|
|
17
|
-
for (const [tokenType, value] of this.imbalances) {
|
|
18
|
-
if (tokenType !== this.feeTokenType && value !== 0n) {
|
|
19
|
-
return [tokenType, value];
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
return undefined;
|
|
23
|
-
}
|
|
24
|
-
findNativeImbalance() {
|
|
25
|
-
const nativeImbalance = this.imbalances.get(this.feeTokenType);
|
|
26
|
-
if (nativeImbalance !== undefined && nativeImbalance !== 0n) {
|
|
27
|
-
return [this.feeTokenType, nativeImbalance];
|
|
28
|
-
}
|
|
29
|
-
return undefined;
|
|
30
|
-
}
|
|
31
|
-
addInput(input) {
|
|
32
|
-
this.inputsRecipe.push(input);
|
|
33
|
-
const imbalance = this.imbalances.get(input.type) || 0n;
|
|
34
|
-
this.imbalances.set(input.type, imbalance + input.value);
|
|
35
|
-
const nativeImbalance = this.imbalances.get(this.feeTokenType) || 0n;
|
|
36
|
-
this.imbalances.set(this.feeTokenType, nativeImbalance - this.transactionCostModel.inputFeeOverhead);
|
|
37
|
-
}
|
|
38
|
-
addOutput(output) {
|
|
39
|
-
const imbalance = this.imbalances.get(output.type) || 0n;
|
|
40
|
-
const subtractFee = output.type === this.feeTokenType ? this.transactionCostModel.outputFeeOverhead : 0n;
|
|
41
|
-
const absoluteCoinValue = output.value < 0n ? -output.value : output.value;
|
|
42
|
-
this.outputsRecipe.push(createCoinInfo(output.type, absoluteCoinValue - subtractFee));
|
|
43
|
-
this.imbalances.set(output.type, imbalance - absoluteCoinValue);
|
|
44
|
-
if (output.type !== this.feeTokenType) {
|
|
45
|
-
const nativeImbalance = this.imbalances.get(this.feeTokenType) || 0n;
|
|
46
|
-
this.imbalances.set(this.feeTokenType, nativeImbalance - this.transactionCostModel.outputFeeOverhead);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const getBalanceRecipe = (coins, targetImbalances, transactionCostModel, feeTokenType) => {
|
|
52
|
-
const counterOffer = createCounterOffer(coins, targetImbalances, transactionCostModel, feeTokenType);
|
|
53
|
-
return {
|
|
54
|
-
inputs: counterOffer.inputsRecipe,
|
|
55
|
-
outputs: counterOffer.outputsRecipe,
|
|
56
|
-
};
|
|
57
|
-
};
|
|
58
|
-
const createCounterOffer = (coins, targetImbalances, transactionCostModel, feeTokenType) => {
|
|
59
|
-
// 1. Create an empty offer
|
|
60
|
-
// 2. Calculate total fees to be paid for the unbalanced transaction and the offer
|
|
61
|
-
// 3. Calculate resulting imbalances by merging ones from the unbalanced transaction, the offer and target imbalances
|
|
62
|
-
const counterOffer = new CounterOffer(targetImbalances, transactionCostModel, feeTokenType);
|
|
63
|
-
let imbalance;
|
|
64
|
-
// 4. Verify if target imbalances are met. If they are, create transaction from the offer, merge with the unbalanced transaction, and return
|
|
65
|
-
// 5. Sort token types present in result imbalances in a way, that DUST is left last and select first token type
|
|
66
|
-
while ((imbalance = counterOffer.findNonNativeImbalance())) {
|
|
67
|
-
coins = doBalance(imbalance, coins, counterOffer);
|
|
68
|
-
}
|
|
69
|
-
while ((imbalance = counterOffer.findNativeImbalance())) {
|
|
70
|
-
coins = doBalance(imbalance, coins, counterOffer);
|
|
71
|
-
}
|
|
72
|
-
return counterOffer;
|
|
73
|
-
};
|
|
74
|
-
const doBalance = (imbalance, coins, counterOffer) => {
|
|
75
|
-
const [tokenType, imbalanceAmount] = imbalance;
|
|
76
|
-
// 6a. If the imbalance is positive and greater than the output fee,
|
|
77
|
-
// create an output for self with the amount equal to imbalance
|
|
78
|
-
const shouldAddOutput = (tokenType === counterOffer.feeTokenType &&
|
|
79
|
-
imbalanceAmount >= counterOffer.transactionCostModel.outputFeeOverhead) ||
|
|
80
|
-
(tokenType !== counterOffer.feeTokenType && imbalanceAmount > 0n);
|
|
81
|
-
if (shouldAddOutput) {
|
|
82
|
-
counterOffer.addOutput({ type: tokenType, value: imbalanceAmount });
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
// 6b. If the imbalance is negative, select a single coin of the selected type, and create an input
|
|
86
|
-
const coin = chooseCoin(coins, { type: tokenType});
|
|
87
|
-
if (typeof coin === 'undefined') {
|
|
88
|
-
throw new Error(tokenType);
|
|
89
|
-
}
|
|
90
|
-
counterOffer.addInput(coin);
|
|
91
|
-
coins = coins.filter((c) => c !== coin);
|
|
92
|
-
}
|
|
93
|
-
return coins;
|
|
94
|
-
};
|
|
95
|
-
const chooseCoin = (coins, coinToChoose) => {
|
|
96
|
-
const filteredAndSortedCoins = coins
|
|
97
|
-
.filter((coin) => coin.type === coinToChoose.type)
|
|
98
|
-
.sort((a, b) => Number(a.value - b.value));
|
|
99
|
-
return filteredAndSortedCoins[0] ?? undefined;
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const emptyImbalances = new Map();
|
|
103
|
-
const createImbalances = (imbalances) => {
|
|
104
|
-
const mappedImbalances = new Map();
|
|
105
|
-
imbalances.forEach(([tokenType, value]) => {
|
|
106
|
-
if (mappedImbalances.has(tokenType)) {
|
|
107
|
-
mappedImbalances.set(tokenType, mappedImbalances.get(tokenType) + value);
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
mappedImbalances.set(tokenType, value);
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
return mappedImbalances;
|
|
114
|
-
};
|
|
115
|
-
const mergeImbalances = (a, b) => {
|
|
116
|
-
b.forEach((valueB, tokenType) => {
|
|
117
|
-
const valueA = a.get(tokenType) || 0n;
|
|
118
|
-
a.set(tokenType, valueA + valueB);
|
|
119
|
-
});
|
|
120
|
-
return a;
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
export { CounterOffer, createCounterOffer, createImbalances, emptyImbalances, getBalanceRecipe, mergeImbalances };
|
|
124
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
// This file is part of MIDNIGHT-WALLET-SDK.
|
|
2
|
+
// Copyright (C) 2025 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
|
+
export * from './balancer/index.js';
|
package/package.json
CHANGED
|
@@ -1,37 +1,52 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@midnight-ntwrk/wallet-sdk-capabilities",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0-beta.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.js",
|
|
6
7
|
"types": "./dist/index.d.ts",
|
|
7
|
-
"author": "
|
|
8
|
+
"author": "Midnight Foundation",
|
|
8
9
|
"license": "Apache-2.0",
|
|
9
10
|
"publishConfig": {
|
|
10
11
|
"registry": "https://npm.pkg.github.com/"
|
|
11
12
|
},
|
|
12
|
-
"repository": {
|
|
13
|
-
"type": "git",
|
|
14
|
-
"url": "https://github.com/midnight-ntwrk/artifacts.git"
|
|
15
|
-
},
|
|
16
13
|
"files": [
|
|
17
14
|
"dist/"
|
|
18
15
|
],
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/midnight-ntwrk/artifacts.git"
|
|
19
|
+
},
|
|
19
20
|
"exports": {
|
|
20
21
|
".": {
|
|
21
|
-
"
|
|
22
|
-
"
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"import": "./dist/index.js"
|
|
23
24
|
}
|
|
24
25
|
},
|
|
25
|
-
"
|
|
26
|
-
"@midnight-ntwrk/
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@midnight-ntwrk/wallet-sdk-address-format": "3.0.0-beta.10",
|
|
28
|
+
"@midnight-ntwrk/wallet-sdk-hd": "3.0.0-beta.8",
|
|
29
|
+
"effect": "^3.17.3"
|
|
27
30
|
},
|
|
28
31
|
"devDependencies": {
|
|
29
|
-
"@midnight-ntwrk/
|
|
32
|
+
"@midnight-ntwrk/ledger-v7": "7.0.0-alpha.1",
|
|
33
|
+
"eslint": "^9.37.0",
|
|
34
|
+
"fast-check": "^4.2.0",
|
|
35
|
+
"prettier": "^3.7.0",
|
|
36
|
+
"publint": "~0.3.14",
|
|
37
|
+
"rimraf": "^6.0.1",
|
|
38
|
+
"typescript": "^5.9.3",
|
|
39
|
+
"vitest": "^4.0.16"
|
|
30
40
|
},
|
|
31
41
|
"scripts": {
|
|
32
42
|
"typecheck": "tsc -b ./tsconfig.json --noEmit",
|
|
33
|
-
"test": "
|
|
34
|
-
"lint": "eslint",
|
|
35
|
-
"
|
|
43
|
+
"test": "vitest run",
|
|
44
|
+
"lint": "eslint --max-warnings 0",
|
|
45
|
+
"format": "prettier --write \"**/*.{ts,js,json,yaml,yml,md}\"",
|
|
46
|
+
"format:check": "prettier --check \"**/*.{ts,js,json,yaml,yml,md}\"",
|
|
47
|
+
"dist": "tsc -b ./tsconfig.build.json",
|
|
48
|
+
"dist:publish": "tsc -b ./tsconfig.publish.json",
|
|
49
|
+
"clean": "rimraf --glob dist 'tsconfig.*.tsbuildinfo' && date +%s > .clean-timestamp",
|
|
50
|
+
"publint": "publint --strict"
|
|
36
51
|
}
|
|
37
|
-
}
|
|
52
|
+
}
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC"}
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/balancer/CounterOffer.ts","../src/balancer/Balancer.ts","../src/balancer/Imbalances.ts"],"sourcesContent":[null,null,null],"names":[],"mappings":"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"}
|