@arkade-os/sdk 0.3.10 → 0.3.11
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/cjs/arkfee/celenv.js +43 -0
- package/dist/cjs/arkfee/estimator.js +143 -0
- package/dist/cjs/arkfee/index.js +5 -0
- package/dist/cjs/arkfee/types.js +25 -0
- package/dist/cjs/index.js +15 -0
- package/dist/cjs/intent/index.js +21 -0
- package/dist/cjs/providers/ark.js +2 -9
- package/dist/cjs/providers/indexer.js +1 -0
- package/dist/cjs/utils/transactionHistory.js +118 -156
- package/dist/cjs/wallet/ramps.js +96 -11
- package/dist/cjs/wallet/serviceWorker/worker.js +4 -31
- package/dist/cjs/wallet/wallet.js +51 -34
- package/dist/esm/arkfee/celenv.js +40 -0
- package/dist/esm/arkfee/estimator.js +139 -0
- package/dist/esm/arkfee/index.js +1 -0
- package/dist/esm/arkfee/types.js +21 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/intent/index.js +21 -0
- package/dist/esm/providers/ark.js +2 -9
- package/dist/esm/providers/indexer.js +1 -0
- package/dist/esm/utils/transactionHistory.js +117 -155
- package/dist/esm/wallet/ramps.js +96 -11
- package/dist/esm/wallet/serviceWorker/worker.js +4 -31
- package/dist/esm/wallet/wallet.js +51 -34
- package/dist/types/arkfee/celenv.d.ts +25 -0
- package/dist/types/arkfee/estimator.d.ts +49 -0
- package/dist/types/arkfee/index.d.ts +2 -0
- package/dist/types/arkfee/types.d.ts +37 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/intent/index.d.ts +1 -0
- package/dist/types/providers/ark.d.ts +3 -8
- package/dist/types/utils/transactionHistory.d.ts +12 -5
- package/dist/types/wallet/index.d.ts +1 -0
- package/dist/types/wallet/ramps.d.ts +2 -1
- package/dist/types/wallet/serviceWorker/worker.d.ts +0 -1
- package/package.json +3 -2
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.IntentOnchainInputEnv = exports.IntentOffchainInputEnv = exports.IntentOutputEnv = exports.OutputScriptVariableName = exports.InputTypeVariableName = exports.WeightVariableName = exports.BirthVariableName = exports.ExpiryVariableName = exports.AmountVariableName = void 0;
|
|
4
|
+
const cel_js_1 = require("@marcbachmann/cel-js");
|
|
5
|
+
/**
|
|
6
|
+
* Variable names used in CEL expressions
|
|
7
|
+
*/
|
|
8
|
+
exports.AmountVariableName = "amount";
|
|
9
|
+
exports.ExpiryVariableName = "expiry";
|
|
10
|
+
exports.BirthVariableName = "birth";
|
|
11
|
+
exports.WeightVariableName = "weight";
|
|
12
|
+
exports.InputTypeVariableName = "inputType";
|
|
13
|
+
exports.OutputScriptVariableName = "script";
|
|
14
|
+
const nowFunction = {
|
|
15
|
+
signature: "now(): double",
|
|
16
|
+
implementation: () => Math.floor(Date.now() / 1000),
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* IntentOutputEnv is the CEL environment for output fee calculation
|
|
20
|
+
* Variables: amount, script
|
|
21
|
+
*/
|
|
22
|
+
exports.IntentOutputEnv = new cel_js_1.Environment()
|
|
23
|
+
.registerVariable(exports.AmountVariableName, "double")
|
|
24
|
+
.registerVariable(exports.OutputScriptVariableName, "string")
|
|
25
|
+
.registerFunction(nowFunction.signature, nowFunction.implementation);
|
|
26
|
+
/**
|
|
27
|
+
* IntentOffchainInputEnv is the CEL environment for offchain input fee calculation
|
|
28
|
+
* Variables: amount, expiry, birth, weight, inputType
|
|
29
|
+
*/
|
|
30
|
+
exports.IntentOffchainInputEnv = new cel_js_1.Environment()
|
|
31
|
+
.registerVariable(exports.AmountVariableName, "double")
|
|
32
|
+
.registerVariable(exports.ExpiryVariableName, "double")
|
|
33
|
+
.registerVariable(exports.BirthVariableName, "double")
|
|
34
|
+
.registerVariable(exports.WeightVariableName, "double")
|
|
35
|
+
.registerVariable(exports.InputTypeVariableName, "string")
|
|
36
|
+
.registerFunction(nowFunction.signature, nowFunction.implementation);
|
|
37
|
+
/**
|
|
38
|
+
* IntentOnchainInputEnv is the CEL environment for onchain input fee calculation
|
|
39
|
+
* Variables: amount
|
|
40
|
+
*/
|
|
41
|
+
exports.IntentOnchainInputEnv = new cel_js_1.Environment()
|
|
42
|
+
.registerVariable(exports.AmountVariableName, "double")
|
|
43
|
+
.registerFunction(nowFunction.signature, nowFunction.implementation);
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Estimator = void 0;
|
|
4
|
+
const celenv_js_1 = require("./celenv.js");
|
|
5
|
+
const types_js_1 = require("./types.js");
|
|
6
|
+
/**
|
|
7
|
+
* Estimator evaluates CEL expressions to calculate fees for Ark intents
|
|
8
|
+
*/
|
|
9
|
+
class Estimator {
|
|
10
|
+
/**
|
|
11
|
+
* Creates a new Estimator with the given config
|
|
12
|
+
* @param config - Configuration containing CEL programs for fee calculation
|
|
13
|
+
*/
|
|
14
|
+
constructor(config) {
|
|
15
|
+
this.config = config;
|
|
16
|
+
this.intentOffchainInput = config.offchainInput
|
|
17
|
+
? parseProgram(config.offchainInput, celenv_js_1.IntentOffchainInputEnv)
|
|
18
|
+
: undefined;
|
|
19
|
+
this.intentOnchainInput = config.onchainInput
|
|
20
|
+
? parseProgram(config.onchainInput, celenv_js_1.IntentOnchainInputEnv)
|
|
21
|
+
: undefined;
|
|
22
|
+
this.intentOffchainOutput = config.offchainOutput
|
|
23
|
+
? parseProgram(config.offchainOutput, celenv_js_1.IntentOutputEnv)
|
|
24
|
+
: undefined;
|
|
25
|
+
this.intentOnchainOutput = config.onchainOutput
|
|
26
|
+
? parseProgram(config.onchainOutput, celenv_js_1.IntentOutputEnv)
|
|
27
|
+
: undefined;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Evaluates the fee for a given vtxo input
|
|
31
|
+
* @param input - The offchain input to evaluate
|
|
32
|
+
* @returns The fee amount for this input
|
|
33
|
+
*/
|
|
34
|
+
evalOffchainInput(input) {
|
|
35
|
+
if (!this.intentOffchainInput) {
|
|
36
|
+
return types_js_1.FeeAmount.ZERO;
|
|
37
|
+
}
|
|
38
|
+
const args = inputToArgs(input);
|
|
39
|
+
return new types_js_1.FeeAmount(this.intentOffchainInput.program(args));
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Evaluates the fee for a given boarding input
|
|
43
|
+
* @param input - The onchain input to evaluate
|
|
44
|
+
* @returns The fee amount for this input
|
|
45
|
+
*/
|
|
46
|
+
evalOnchainInput(input) {
|
|
47
|
+
if (!this.intentOnchainInput) {
|
|
48
|
+
return types_js_1.FeeAmount.ZERO;
|
|
49
|
+
}
|
|
50
|
+
const args = {
|
|
51
|
+
amount: Number(input.amount),
|
|
52
|
+
};
|
|
53
|
+
return new types_js_1.FeeAmount(this.intentOnchainInput.program(args));
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Evaluates the fee for a given vtxo output
|
|
57
|
+
* @param output - The output to evaluate
|
|
58
|
+
* @returns The fee amount for this output
|
|
59
|
+
*/
|
|
60
|
+
evalOffchainOutput(output) {
|
|
61
|
+
if (!this.intentOffchainOutput) {
|
|
62
|
+
return types_js_1.FeeAmount.ZERO;
|
|
63
|
+
}
|
|
64
|
+
const args = outputToArgs(output);
|
|
65
|
+
return new types_js_1.FeeAmount(this.intentOffchainOutput.program(args));
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Evaluates the fee for a given collaborative exit output
|
|
69
|
+
* @param output - The output to evaluate
|
|
70
|
+
* @returns The fee amount for this output
|
|
71
|
+
*/
|
|
72
|
+
evalOnchainOutput(output) {
|
|
73
|
+
if (!this.intentOnchainOutput) {
|
|
74
|
+
return types_js_1.FeeAmount.ZERO;
|
|
75
|
+
}
|
|
76
|
+
const args = outputToArgs(output);
|
|
77
|
+
return new types_js_1.FeeAmount(this.intentOnchainOutput.program(args));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Evaluates the fee for a given set of inputs and outputs
|
|
81
|
+
* @param offchainInputs - Array of offchain inputs to evaluate
|
|
82
|
+
* @param onchainInputs - Array of onchain inputs to evaluate
|
|
83
|
+
* @param offchainOutputs - Array of offchain outputs to evaluate
|
|
84
|
+
* @param onchainOutputs - Array of onchain outputs to evaluate
|
|
85
|
+
* @returns The total fee amount
|
|
86
|
+
*/
|
|
87
|
+
eval(offchainInputs, onchainInputs, offchainOutputs, onchainOutputs) {
|
|
88
|
+
let fee = types_js_1.FeeAmount.ZERO;
|
|
89
|
+
for (const input of offchainInputs) {
|
|
90
|
+
fee = fee.add(this.evalOffchainInput(input));
|
|
91
|
+
}
|
|
92
|
+
for (const input of onchainInputs) {
|
|
93
|
+
fee = fee.add(this.evalOnchainInput(input));
|
|
94
|
+
}
|
|
95
|
+
for (const output of offchainOutputs) {
|
|
96
|
+
fee = fee.add(this.evalOffchainOutput(output));
|
|
97
|
+
}
|
|
98
|
+
for (const output of onchainOutputs) {
|
|
99
|
+
fee = fee.add(this.evalOnchainOutput(output));
|
|
100
|
+
}
|
|
101
|
+
return fee;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
exports.Estimator = Estimator;
|
|
105
|
+
function inputToArgs(input) {
|
|
106
|
+
const args = {
|
|
107
|
+
amount: Number(input.amount),
|
|
108
|
+
inputType: input.type,
|
|
109
|
+
weight: input.weight,
|
|
110
|
+
};
|
|
111
|
+
if (input.expiry) {
|
|
112
|
+
args.expiry = Math.floor(input.expiry.getTime() / 1000);
|
|
113
|
+
}
|
|
114
|
+
if (input.birth) {
|
|
115
|
+
args.birth = Math.floor(input.birth.getTime() / 1000);
|
|
116
|
+
}
|
|
117
|
+
return args;
|
|
118
|
+
}
|
|
119
|
+
function outputToArgs(output) {
|
|
120
|
+
return {
|
|
121
|
+
amount: Number(output.amount),
|
|
122
|
+
script: output.script,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Parses a CEL program and validates its return type
|
|
127
|
+
* @param text - The CEL program text to parse
|
|
128
|
+
* @param env - The CEL environment to use
|
|
129
|
+
* @returns parsed and validated program
|
|
130
|
+
*/
|
|
131
|
+
function parseProgram(text, env) {
|
|
132
|
+
const program = env.parse(text);
|
|
133
|
+
// Type check the program
|
|
134
|
+
const checkResult = program.check();
|
|
135
|
+
if (!checkResult.valid) {
|
|
136
|
+
throw new Error(`type check failed: ${checkResult.error?.message ?? "unknown error"}`);
|
|
137
|
+
}
|
|
138
|
+
// Verify return type is double
|
|
139
|
+
if (checkResult.type !== "double") {
|
|
140
|
+
throw new Error(`expected return type double, got ${checkResult.type}`);
|
|
141
|
+
}
|
|
142
|
+
return { program, text };
|
|
143
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Estimator = void 0;
|
|
4
|
+
var estimator_1 = require("./estimator");
|
|
5
|
+
Object.defineProperty(exports, "Estimator", { enumerable: true, get: function () { return estimator_1.Estimator; } });
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FeeAmount = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* FeeAmount is a wrapper around a number that represents a fee amount in satoshis floating point.
|
|
6
|
+
* @param value - The fee amount in floating point.
|
|
7
|
+
* @method satoshis - Returns the fee amount in satoshis as a integer.
|
|
8
|
+
* @example
|
|
9
|
+
* const fee = new FeeAmount(1.23456789);
|
|
10
|
+
* console.log(fee.value); // 1.23456789
|
|
11
|
+
* console.log(fee.satoshis); // 2
|
|
12
|
+
*/
|
|
13
|
+
class FeeAmount {
|
|
14
|
+
constructor(value) {
|
|
15
|
+
this.value = value;
|
|
16
|
+
}
|
|
17
|
+
get satoshis() {
|
|
18
|
+
return this.value ? Math.ceil(this.value) : 0;
|
|
19
|
+
}
|
|
20
|
+
add(other) {
|
|
21
|
+
return new FeeAmount(this.value + other.value);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.FeeAmount = FeeAmount;
|
|
25
|
+
FeeAmount.ZERO = new FeeAmount(0);
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
2
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
17
|
exports.ContractRepositoryImpl = exports.WalletRepositoryImpl = exports.networks = exports.ArkNote = exports.isVtxoExpiringSoon = exports.combineTapscriptSigs = exports.hasBoardingTxExpired = exports.waitForIncomingFunds = exports.verifyTapscriptSignatures = exports.buildOffchainTx = exports.ConditionWitness = exports.VtxoTaprootTree = exports.VtxoTreeExpiry = exports.CosignerPublicKey = exports.getArkPsbtFields = exports.setArkPsbtField = exports.ArkPsbtFieldKeyType = exports.ArkPsbtFieldKey = exports.TapTreeCoder = exports.CLTVMultisigTapscript = exports.ConditionMultisigTapscript = exports.ConditionCSVMultisigTapscript = exports.CSVMultisigTapscript = exports.MultisigTapscript = exports.decodeTapscript = exports.Response = exports.Request = exports.ServiceWorkerReadonlyWallet = exports.ServiceWorkerWallet = exports.Worker = exports.setupServiceWorker = exports.SettlementEventType = exports.ChainTxType = exports.IndexerTxType = exports.TxType = exports.VHTLC = exports.VtxoScript = exports.DefaultVtxo = exports.ArkAddress = exports.RestIndexerProvider = exports.RestArkProvider = exports.EsploraProvider = exports.ESPLORA_URL = exports.VtxoManager = exports.Ramps = exports.OnchainWallet = exports.ReadonlySingleKey = exports.SingleKey = exports.ReadonlyWallet = exports.Wallet = void 0;
|
|
4
18
|
exports.getSequence = exports.isExpired = exports.isSubdust = exports.isSpendable = exports.isRecoverable = exports.buildForfeitTx = exports.validateConnectorsTxGraph = exports.validateVtxoTxGraph = exports.Batch = exports.maybeArkError = exports.ArkError = exports.Transaction = exports.Unroll = exports.P2A = exports.TxTree = exports.Intent = void 0;
|
|
@@ -102,3 +116,4 @@ Object.defineProperty(exports, "validateVtxoTxGraph", { enumerable: true, get: f
|
|
|
102
116
|
Object.defineProperty(exports, "validateConnectorsTxGraph", { enumerable: true, get: function () { return validation_1.validateConnectorsTxGraph; } });
|
|
103
117
|
const forfeit_1 = require("./forfeit");
|
|
104
118
|
Object.defineProperty(exports, "buildForfeitTx", { enumerable: true, get: function () { return forfeit_1.buildForfeitTx; } });
|
|
119
|
+
__exportStar(require("./arkfee"), exports);
|
package/dist/cjs/intent/index.js
CHANGED
|
@@ -57,6 +57,27 @@ var Intent;
|
|
|
57
57
|
return craftToSignTx(toSpend, inputs, outputs);
|
|
58
58
|
}
|
|
59
59
|
Intent.create = create;
|
|
60
|
+
function fee(proof) {
|
|
61
|
+
let sumOfInputs = 0n;
|
|
62
|
+
for (let i = 0; i < proof.inputsLength; i++) {
|
|
63
|
+
const input = proof.getInput(i);
|
|
64
|
+
if (input.witnessUtxo === undefined)
|
|
65
|
+
throw new Error("intent proof input requires witness utxo");
|
|
66
|
+
sumOfInputs += input.witnessUtxo.amount;
|
|
67
|
+
}
|
|
68
|
+
let sumOfOutputs = 0n;
|
|
69
|
+
for (let i = 0; i < proof.outputsLength; i++) {
|
|
70
|
+
const output = proof.getOutput(i);
|
|
71
|
+
if (output.amount === undefined)
|
|
72
|
+
throw new Error("intent proof output requires amount");
|
|
73
|
+
sumOfOutputs += output.amount;
|
|
74
|
+
}
|
|
75
|
+
if (sumOfOutputs > sumOfInputs) {
|
|
76
|
+
throw new Error(`intent proof output amount is greater than input amount: ${sumOfOutputs} > ${sumOfInputs}`);
|
|
77
|
+
}
|
|
78
|
+
return Number(sumOfInputs - sumOfOutputs);
|
|
79
|
+
}
|
|
80
|
+
Intent.fee = fee;
|
|
60
81
|
function encodeMessage(message) {
|
|
61
82
|
switch (message.type) {
|
|
62
83
|
case "register":
|
|
@@ -48,15 +48,7 @@ class RestArkProvider {
|
|
|
48
48
|
digest: fromServer.digest ?? "",
|
|
49
49
|
dust: BigInt(fromServer.dust ?? 0),
|
|
50
50
|
fees: {
|
|
51
|
-
intentFee: {
|
|
52
|
-
...fromServer.fees?.intentFee,
|
|
53
|
-
onchainInput: BigInt(
|
|
54
|
-
// split(".")[0] to remove the decimal part
|
|
55
|
-
(fromServer.fees?.intentFee?.onchainInput ?? "0").split(".")[0] ?? 0),
|
|
56
|
-
onchainOutput: BigInt(
|
|
57
|
-
// split(".")[0] to remove the decimal part
|
|
58
|
-
(fromServer.fees?.intentFee?.onchainOutput ?? "0").split(".")[0] ?? 0),
|
|
59
|
-
},
|
|
51
|
+
intentFee: fromServer.fees?.intentFee ?? {},
|
|
60
52
|
txFeeRate: fromServer?.fees?.txFeeRate ?? "",
|
|
61
53
|
},
|
|
62
54
|
forfeitAddress: fromServer.forfeitAddress ?? "",
|
|
@@ -69,6 +61,7 @@ class RestArkProvider {
|
|
|
69
61
|
nextStartTime: BigInt(fromServer.scheduledSession.nextStartTime ?? 0),
|
|
70
62
|
nextEndTime: BigInt(fromServer.scheduledSession.nextEndTime ?? 0),
|
|
71
63
|
period: BigInt(fromServer.scheduledSession.period ?? 0),
|
|
64
|
+
fees: fromServer.scheduledSession.fees ?? {},
|
|
72
65
|
}
|
|
73
66
|
: undefined,
|
|
74
67
|
serviceStatus: fromServer.serviceStatus ?? {},
|
|
@@ -1,168 +1,130 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.buildTransactionHistory = buildTransactionHistory;
|
|
4
4
|
const wallet_1 = require("../wallet");
|
|
5
|
+
const txKey = {
|
|
6
|
+
commitmentTxid: "",
|
|
7
|
+
boardingTxid: "",
|
|
8
|
+
arkTxid: "",
|
|
9
|
+
};
|
|
5
10
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* @
|
|
11
|
+
* Builds the transaction history by analyzing virtual coins (VTXOs), boarding transactions, and ignored commitments.
|
|
12
|
+
* History is sorted from newest to oldest and is composed only of SENT and RECEIVED transactions.
|
|
13
|
+
*
|
|
14
|
+
* @param {VirtualCoin[]} vtxos - An array of virtual coins representing the user's transactions and balances.
|
|
15
|
+
* @param {ArkTransaction[]} allBoardingTxs - An array of boarding transactions to include in the history.
|
|
16
|
+
* @param {Set<string>} commitmentsToIgnore - A set of commitment IDs that should be excluded from processing.
|
|
17
|
+
* @return {ExtendedArkTransaction[]} A sorted array of extended Ark transactions, representing the transaction history.
|
|
10
18
|
*/
|
|
11
|
-
function
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const spentAmount = reduceVtxosAmount(spentVtxos);
|
|
33
|
-
if (vtxo.value <= spentAmount) {
|
|
34
|
-
continue; // settlement or change, ignore
|
|
35
|
-
}
|
|
36
|
-
const txKey = {
|
|
37
|
-
commitmentTxid: "",
|
|
38
|
-
boardingTxid: "",
|
|
39
|
-
arkTxid: "",
|
|
40
|
-
};
|
|
41
|
-
let settled = vtxo.virtualStatus.state !== "preconfirmed";
|
|
42
|
-
if (vtxo.virtualStatus.state === "preconfirmed") {
|
|
43
|
-
txKey.arkTxid = vtxo.txid;
|
|
44
|
-
if (vtxo.spentBy) {
|
|
45
|
-
settled = true;
|
|
19
|
+
function buildTransactionHistory(vtxos, allBoardingTxs, commitmentsToIgnore) {
|
|
20
|
+
const fromOldestVtxo = [...vtxos].sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
|
|
21
|
+
const sent = [];
|
|
22
|
+
let received = [];
|
|
23
|
+
for (const vtxo of fromOldestVtxo) {
|
|
24
|
+
if (vtxo.status.isLeaf) {
|
|
25
|
+
// If this vtxo is a leaf and it's not the settlement of a boarding or there's no vtxo refreshed by it,
|
|
26
|
+
// it's translated into a received batch transaction
|
|
27
|
+
if (!commitmentsToIgnore.has(vtxo.virtualStatus.commitmentTxIds[0]) &&
|
|
28
|
+
fromOldestVtxo.filter((v) => v.settledBy === vtxo.virtualStatus.commitmentTxIds[0]).length === 0) {
|
|
29
|
+
received.push({
|
|
30
|
+
key: {
|
|
31
|
+
...txKey,
|
|
32
|
+
commitmentTxid: vtxo.virtualStatus.commitmentTxIds[0],
|
|
33
|
+
},
|
|
34
|
+
tag: "batch",
|
|
35
|
+
type: wallet_1.TxType.TxReceived,
|
|
36
|
+
amount: vtxo.value,
|
|
37
|
+
settled: vtxo.status.isLeaf || vtxo.isSpent,
|
|
38
|
+
createdAt: vtxo.createdAt.getTime(),
|
|
39
|
+
});
|
|
46
40
|
}
|
|
47
41
|
}
|
|
48
|
-
else {
|
|
49
|
-
|
|
50
|
-
|
|
42
|
+
else if (fromOldestVtxo.filter((v) => v.arkTxId === vtxo.txid).length === 0) {
|
|
43
|
+
// If this vtxo is preconfirmed and does not spend any other vtxos,
|
|
44
|
+
// it's translated into a received offchain transaction
|
|
45
|
+
received.push({
|
|
46
|
+
key: { ...txKey, arkTxid: vtxo.txid },
|
|
47
|
+
tag: "offchain",
|
|
48
|
+
type: wallet_1.TxType.TxReceived,
|
|
49
|
+
amount: vtxo.value,
|
|
50
|
+
settled: vtxo.status.isLeaf || vtxo.isSpent,
|
|
51
|
+
createdAt: vtxo.createdAt.getTime(),
|
|
52
|
+
});
|
|
51
53
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
54
|
+
// If the vtxo is spent, it's translated into a sent transaction unless:
|
|
55
|
+
// - it's been refreshed (we don't want to add any record in this case)
|
|
56
|
+
// - a sent transaction has been already added to avoid duplicates (can happen if many vtxos have been spent in the same tx or forfeited in the same batch)
|
|
57
|
+
if (vtxo.isSpent) {
|
|
58
|
+
// If the vtxo is spent offchain, it's translated into offchain sent tx
|
|
59
|
+
if (vtxo.arkTxId &&
|
|
60
|
+
!sent.some((s) => s.key.arkTxid === vtxo.arkTxId)) {
|
|
61
|
+
const changes = fromOldestVtxo.filter((_) => _.txid === vtxo.arkTxId);
|
|
62
|
+
let txAmount = 0;
|
|
63
|
+
let txTime = 0;
|
|
64
|
+
if (changes.length > 0) {
|
|
65
|
+
const changeAmount = changes.reduce((acc, v) => acc + v.value, 0);
|
|
66
|
+
// We want to find all the other VTXOs spent by the same transaction to
|
|
67
|
+
// calculate the full amount of the change.
|
|
68
|
+
const allSpent = fromOldestVtxo.filter((v) => v.arkTxId === vtxo.arkTxId);
|
|
69
|
+
const spentAmount = allSpent.reduce((acc, v) => acc + v.value, 0);
|
|
70
|
+
txAmount = spentAmount - changeAmount;
|
|
71
|
+
txTime = changes[0].createdAt.getTime();
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
txAmount = vtxo.value;
|
|
75
|
+
// TODO: fetch the vtxo with /v1/indexer/vtxos?outpoints=<vtxo.arkTxid:0> to know when the tx was made
|
|
76
|
+
txTime = vtxo.createdAt.getTime() + 1;
|
|
77
|
+
}
|
|
78
|
+
sent.push({
|
|
79
|
+
key: { ...txKey, arkTxid: vtxo.arkTxId },
|
|
80
|
+
tag: "offchain",
|
|
81
|
+
type: wallet_1.TxType.TxSent,
|
|
82
|
+
amount: txAmount,
|
|
83
|
+
settled: true,
|
|
84
|
+
createdAt: txTime,
|
|
85
|
+
});
|
|
66
86
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
createdAt: vtxo.createdAt.getTime(),
|
|
104
|
-
settled: true,
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
return txs;
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Helper function to find vtxos that were spent in a settlement
|
|
111
|
-
*/
|
|
112
|
-
function findVtxosSpentInSettlement(vtxos, vtxo) {
|
|
113
|
-
if (vtxo.virtualStatus.state === "preconfirmed") {
|
|
114
|
-
return [];
|
|
115
|
-
}
|
|
116
|
-
return vtxos.filter((v) => {
|
|
117
|
-
if (!v.settledBy)
|
|
118
|
-
return false;
|
|
119
|
-
return (vtxo.virtualStatus.commitmentTxIds?.includes(v.settledBy) ?? false);
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Helper function to find vtxos that were spent in a payment
|
|
124
|
-
*/
|
|
125
|
-
function findVtxosSpentInPayment(vtxos, vtxo) {
|
|
126
|
-
return vtxos.filter((v) => {
|
|
127
|
-
if (!v.arkTxId)
|
|
128
|
-
return false;
|
|
129
|
-
return v.arkTxId === vtxo.txid;
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Helper function to find vtxos that resulted from a spentBy transaction
|
|
134
|
-
*/
|
|
135
|
-
function findVtxosResultedFromTxid(vtxos, txid) {
|
|
136
|
-
return vtxos.filter((v) => {
|
|
137
|
-
if (v.virtualStatus.state !== "preconfirmed" &&
|
|
138
|
-
v.virtualStatus.commitmentTxIds?.includes(txid)) {
|
|
139
|
-
return true;
|
|
140
|
-
}
|
|
141
|
-
return v.txid === txid;
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* Helper function to reduce vtxos to their total amount
|
|
146
|
-
*/
|
|
147
|
-
function reduceVtxosAmount(vtxos) {
|
|
148
|
-
return vtxos.reduce((sum, v) => sum + v.value, 0);
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Helper function to get a vtxo from a list of vtxos
|
|
152
|
-
*/
|
|
153
|
-
function getVtxo(resultedVtxos, spentVtxos) {
|
|
154
|
-
if (resultedVtxos.length === 0) {
|
|
155
|
-
return spentVtxos[0];
|
|
156
|
-
}
|
|
157
|
-
return resultedVtxos[0];
|
|
158
|
-
}
|
|
159
|
-
function removeVtxosFromList(vtxos, vtxosToRemove) {
|
|
160
|
-
return vtxos.filter((v) => {
|
|
161
|
-
for (const vtxoToRemove of vtxosToRemove) {
|
|
162
|
-
if (v.txid === vtxoToRemove.txid && v.vout === vtxoToRemove.vout) {
|
|
163
|
-
return false;
|
|
87
|
+
// If the vtxo is forfeited in a batch and the total sum of forfeited vtxos is bigger than the sum of new vtxos,
|
|
88
|
+
// it's translated into an exit sent tx
|
|
89
|
+
if (vtxo.settledBy &&
|
|
90
|
+
!commitmentsToIgnore.has(vtxo.settledBy) &&
|
|
91
|
+
!sent.some((s) => s.key.commitmentTxid === vtxo.settledBy)) {
|
|
92
|
+
const changes = fromOldestVtxo.filter((v) => v.status.isLeaf &&
|
|
93
|
+
v.virtualStatus.commitmentTxIds?.every((_) => vtxo.settledBy === _));
|
|
94
|
+
const forfeitVtxos = fromOldestVtxo.filter((v) => v.settledBy === vtxo.settledBy);
|
|
95
|
+
const forfeitAmount = forfeitVtxos.reduce((acc, v) => acc + v.value, 0);
|
|
96
|
+
if (changes.length > 0) {
|
|
97
|
+
const settledAmount = changes.reduce((acc, v) => acc + v.value, 0);
|
|
98
|
+
// forfeitAmount > settledAmount --> collaborative exit with offchain change
|
|
99
|
+
// TODO: make this support fees!
|
|
100
|
+
if (forfeitAmount > settledAmount) {
|
|
101
|
+
sent.push({
|
|
102
|
+
key: { ...txKey, commitmentTxid: vtxo.settledBy },
|
|
103
|
+
tag: "exit",
|
|
104
|
+
type: wallet_1.TxType.TxSent,
|
|
105
|
+
amount: forfeitAmount - settledAmount,
|
|
106
|
+
settled: true,
|
|
107
|
+
createdAt: changes[0].createdAt.getTime(),
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
// forfeitAmount > 0 && settledAmount == 0 --> collaborative exit without any offchain change
|
|
113
|
+
sent.push({
|
|
114
|
+
key: { ...txKey, commitmentTxid: vtxo.settledBy },
|
|
115
|
+
tag: "exit",
|
|
116
|
+
type: wallet_1.TxType.TxSent,
|
|
117
|
+
amount: forfeitAmount,
|
|
118
|
+
settled: true,
|
|
119
|
+
// TODO: fetch commitment tx with /v1/indexer/commitmentTx/<commitmentTxid> to know when the tx was made
|
|
120
|
+
createdAt: vtxo.createdAt.getTime() + 1,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
164
123
|
}
|
|
165
124
|
}
|
|
166
|
-
|
|
167
|
-
|
|
125
|
+
}
|
|
126
|
+
// Boardings are always inbound amounts, and we only hide the ones to ignore.
|
|
127
|
+
const boardingTx = allBoardingTxs.map((tx) => ({ ...tx, tag: "boarding" }));
|
|
128
|
+
const sorted = [...boardingTx, ...sent, ...received].sort((a, b) => b.createdAt - a.createdAt);
|
|
129
|
+
return sorted;
|
|
168
130
|
}
|