@tezos-x/octez.js-sapling 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +95 -0
  2. package/dist/lib/constants.js +7 -0
  3. package/dist/lib/errors.js +83 -0
  4. package/dist/lib/octez.js-sapling.js +274 -0
  5. package/dist/lib/sapling-forger/sapling-forger.js +100 -0
  6. package/dist/lib/sapling-keys/helpers.js +46 -0
  7. package/dist/lib/sapling-keys/in-memory-proving-key.js +81 -0
  8. package/dist/lib/sapling-keys/in-memory-spending-key.js +139 -0
  9. package/dist/lib/sapling-keys/in-memory-viewing-key.js +99 -0
  10. package/dist/lib/sapling-module-wrapper.js +82 -0
  11. package/dist/lib/sapling-state/sapling-state.js +171 -0
  12. package/dist/lib/sapling-state/utils.js +59 -0
  13. package/dist/lib/sapling-tx-builder/sapling-transactions-builder.js +293 -0
  14. package/dist/lib/sapling-tx-viewer/helpers.js +29 -0
  15. package/dist/lib/sapling-tx-viewer/sapling-transaction-viewer.js +227 -0
  16. package/dist/lib/types.js +2 -0
  17. package/dist/lib/version.js +8 -0
  18. package/dist/octez.js-sapling.es6.js +1482 -0
  19. package/dist/octez.js-sapling.es6.js.map +1 -0
  20. package/dist/octez.js-sapling.umd.js +1501 -0
  21. package/dist/octez.js-sapling.umd.js.map +1 -0
  22. package/dist/types/constants.d.ts +4 -0
  23. package/dist/types/errors.d.ts +52 -0
  24. package/dist/types/octez.js-sapling.d.ts +81 -0
  25. package/dist/types/sapling-forger/sapling-forger.d.ts +29 -0
  26. package/dist/types/sapling-keys/helpers.d.ts +1 -0
  27. package/dist/types/sapling-keys/in-memory-proving-key.d.ts +35 -0
  28. package/dist/types/sapling-keys/in-memory-spending-key.d.ts +53 -0
  29. package/dist/types/sapling-keys/in-memory-viewing-key.d.ts +47 -0
  30. package/dist/types/sapling-module-wrapper.d.ts +18 -0
  31. package/dist/types/sapling-state/sapling-state.d.ts +56 -0
  32. package/dist/types/sapling-state/utils.d.ts +22 -0
  33. package/dist/types/sapling-tx-builder/sapling-transactions-builder.d.ts +31 -0
  34. package/dist/types/sapling-tx-viewer/helpers.d.ts +10 -0
  35. package/dist/types/sapling-tx-viewer/sapling-transaction-viewer.d.ts +50 -0
  36. package/dist/types/types.d.ts +146 -0
  37. package/dist/types/version.d.ts +4 -0
  38. package/fetch-sapling-params.js +39 -0
  39. package/package.json +108 -0
package/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # octez.js Sapling package
2
+
3
+ _Documentation can be found [here](https://octez.js.io/docs/next/sapling)_
4
+ _TypeDoc style documentation is available on-line [here](https://octez.js.io/typedoc/modules/_octez.js_sapling.html)_
5
+
6
+ **Maintained by [Nomadic Labs](https://www.nomadic-labs.com/).**
7
+
8
+ ## General Information
9
+
10
+ Sapling is a protocol allowing to perform private transactions in a decentralized environment. This package allows to read from a sapling state (retrieve the balance and transaction history) and prepare sapling transactions.
11
+
12
+ ## Install
13
+
14
+ Install the package as follows
15
+
16
+ ```
17
+ npm install @tezos-x/octez.js-sapling
18
+ ```
19
+ ## Usage
20
+
21
+ **Retrieve a balance in the Sapling shielded set**
22
+
23
+ The returned balance is in mutez.
24
+
25
+ ```ts
26
+ import { TezosToolkit, RpcReadAdapter } from '@tezos-x/octez.js';
27
+ import { SaplingToolkit, InMemorySpendingKey } from '@tezos-x/octez.js-sapling';
28
+
29
+ const tezos = new TezosToolkit('https://YOUR_PREFERRED_RPC_URL');
30
+
31
+ const saplingContract = await tezos.contract.at('KT1UYwMR6Q6LZnwQEi77DSBrAjKT1tEJb245');
32
+
33
+ const inMemorySpendingKey = await InMemorySpendingKey.fromMnemonic('YOUR_MNEMONIC');
34
+
35
+ const readProvider = new RpcReadAdapter(tezos.rpc);
36
+
37
+ const saplingToolkit = new SaplingToolkit(
38
+ { saplingSigner: inMemorySpendingKey },
39
+ { contractAddress: saplingContract.address, memoSize: 8 },
40
+ readProvider
41
+ )
42
+
43
+ const txViewer = await saplingToolkit.getSaplingTransactionViewer();
44
+ const initialBalance = await txViewer.getBalance();
45
+ ```
46
+
47
+ **Prepare a shielded transaction**
48
+
49
+ A shielded transaction allows sending tokens from a Tezos account (tz1, tz2, tz3) to a Sapling address (zet).
50
+
51
+ ```ts
52
+ import { TezosToolkit, RpcReadAdapter } from '@tezos-x/octez.js';
53
+ import { SaplingToolkit, InMemorySpendingKey } from '@tezos-x/octez.js-sapling';
54
+
55
+ const tezos = new TezosToolkit('https://YOUR_PREFERRED_RPC_URL');
56
+ // set up your signer on the TezosToolkit as usual
57
+ const saplingContract = await tezos.contract.at('KT1UYwMR6Q6LZnwQEi77DSBrAjKT1tEJb245');
58
+
59
+ const inMemorySpendingKey = await InMemorySpendingKey.fromMnemonic('YOUR_MNEMONIC');
60
+
61
+ const readProvider = new RpcReadAdapter(tezos.rpc);
62
+
63
+ const saplingToolkit = new SaplingToolkit(
64
+ { saplingSigner: inMemorySpendingKey },
65
+ { contractAddress: saplingContract.address, memoSize: 8 },
66
+ readProvider
67
+ )
68
+
69
+ // Fetch a payment address (zet)
70
+ const inMemoryViewingKey = await inMemorySpendingKey.getSaplingViewingKeyProvider();
71
+ const paymentAddress = (await inMemoryViewingKey.getAddress()).address;
72
+
73
+ // prepare the shielded transaction
74
+ const shieldedTx = await saplingToolkit.prepareShieldedTransaction([{
75
+ to: paymentAddress,
76
+ amount: 3,
77
+ memo: 'test',
78
+ mutez: false // set to false by default
79
+ }])
80
+
81
+ // Inject the sapling transaction using the ContractAbstraction
82
+ // The amount MUST be specified in the send method to transfer the 3 tez to the shielded set
83
+ const op = await saplingContract.methodsObject.default([shieldedTx]).send({ amount: 3 });
84
+ await op.confirmation();
85
+ ```
86
+
87
+ Refer to the website documentation for further examples and information: https://octez.js.io/docs/next/sapling
88
+
89
+ ## Additional info
90
+
91
+ See the top-level project [https://github.com/trilitech/octez.js](https://github.com/trilitech/octez.js) for details on reporting issues, contributing and versioning.
92
+
93
+ ## Disclaimer
94
+
95
+ THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_BOUND_DATA = exports.DEFAULT_MEMO = exports.OCK_KEY = exports.KDF_KEY = void 0;
4
+ exports.KDF_KEY = 'KDFSaplingForTezosV1';
5
+ exports.OCK_KEY = 'OCK_keystringderivation_TEZOS';
6
+ exports.DEFAULT_MEMO = '';
7
+ exports.DEFAULT_BOUND_DATA = Buffer.from('', 'hex');
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SaplingTransactionViewerError = exports.InsufficientBalance = exports.InvalidMemo = exports.TreeConstructionFailure = exports.InvalidMerkleTreeError = exports.InvalidSpendingKey = void 0;
4
+ const octez_js_core_1 = require("@tezos-x/octez.js-core");
5
+ /**
6
+ * @category Error
7
+ * @description Error indicates the spending key is invalid
8
+ */
9
+ class InvalidSpendingKey extends octez_js_core_1.ParameterValidationError {
10
+ constructor(sk, errorDetail) {
11
+ super();
12
+ this.sk = sk;
13
+ this.errorDetail = errorDetail;
14
+ this.name = 'InvalidSpendingKey';
15
+ this.message = `Invalid spending key "${sk}" ${errorDetail}.`;
16
+ }
17
+ }
18
+ exports.InvalidSpendingKey = InvalidSpendingKey;
19
+ /**
20
+ * @category Error
21
+ * @description Error indicates an invalid Merkle tree being passed
22
+ */
23
+ class InvalidMerkleTreeError extends octez_js_core_1.ParameterValidationError {
24
+ constructor(root) {
25
+ super();
26
+ this.root = root;
27
+ this.name = 'InvalidMerkleTreeError';
28
+ this.message = `Invalid merkle tree has root "${JSON.stringify(root)}" different from expected root.`;
29
+ }
30
+ }
31
+ exports.InvalidMerkleTreeError = InvalidMerkleTreeError;
32
+ /**
33
+ * @category Error
34
+ * @description Error indicates a failure when trying to construct the Merkle tree
35
+ */
36
+ class TreeConstructionFailure extends octez_js_core_1.TaquitoError {
37
+ constructor(message) {
38
+ super();
39
+ this.message = message;
40
+ this.name = 'TreeConstructionFailure';
41
+ }
42
+ }
43
+ exports.TreeConstructionFailure = TreeConstructionFailure;
44
+ /**
45
+ * @category Error
46
+ * @description Error indicates the memo is invalid
47
+ */
48
+ class InvalidMemo extends octez_js_core_1.ParameterValidationError {
49
+ constructor(memo, errorDetails) {
50
+ super();
51
+ this.memo = memo;
52
+ this.errorDetails = errorDetails;
53
+ this.name = 'InvalidMemo';
54
+ this.message = `Invalid memo "${memo}" with length ${memo.length} ${errorDetails}`;
55
+ }
56
+ }
57
+ exports.InvalidMemo = InvalidMemo;
58
+ /**
59
+ * @category Error
60
+ * @description Error indicates not enough balance to prepare the sapling transaction
61
+ */
62
+ class InsufficientBalance extends octez_js_core_1.TaquitoError {
63
+ constructor(realBalance, amountToSpend) {
64
+ super();
65
+ this.realBalance = realBalance;
66
+ this.amountToSpend = amountToSpend;
67
+ this.name = 'InsufficientBalance';
68
+ this.message = `Unable to spend "${amountToSpend}" mutez while the balance is only ${realBalance} mutez.`;
69
+ }
70
+ }
71
+ exports.InsufficientBalance = InsufficientBalance;
72
+ /**
73
+ * @category Error
74
+ * @description Error indicates SaplingTransactionViewer failure
75
+ */
76
+ class SaplingTransactionViewerError extends octez_js_core_1.TaquitoError {
77
+ constructor(message) {
78
+ super();
79
+ this.message = message;
80
+ this.name = 'SaplingTransactionViewerError';
81
+ }
82
+ }
83
+ exports.SaplingTransactionViewerError = SaplingTransactionViewerError;
@@ -0,0 +1,274 @@
1
+ "use strict";
2
+ /**
3
+ * @packageDocumentation
4
+ * @module @tezos-x/octez.js-sapling
5
+ */
6
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
7
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
8
+ return new (P || (P = Promise))(function (resolve, reject) {
9
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
10
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
11
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
12
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
13
+ });
14
+ };
15
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
16
+ if (kind === "m") throw new TypeError("Private method is not writable");
17
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
18
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
19
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
20
+ };
21
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
22
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
23
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
24
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
25
+ };
26
+ var __rest = (this && this.__rest) || function (s, e) {
27
+ var t = {};
28
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
29
+ t[p] = s[p];
30
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
31
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
32
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
33
+ t[p[i]] = s[p[i]];
34
+ }
35
+ return t;
36
+ };
37
+ var _SaplingToolkit_inMemorySpendingKey, _SaplingToolkit_saplingId, _SaplingToolkit_contractAddress, _SaplingToolkit_memoSize, _SaplingToolkit_readProvider, _SaplingToolkit_packer, _SaplingToolkit_saplingForger, _SaplingToolkit_saplingTxBuilder, _SaplingToolkit_saplingTransactionViewer;
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.SaplingToolkit = exports.InMemoryProvingKey = exports.InMemorySpendingKey = exports.InMemoryViewingKey = exports.SaplingTransactionViewer = void 0;
40
+ const bignumber_js_1 = require("bignumber.js");
41
+ const octez_js_1 = require("@tezos-x/octez.js");
42
+ const octez_js_utils_1 = require("@tezos-x/octez.js-utils");
43
+ const errors_1 = require("./errors");
44
+ const helpers_1 = require("./sapling-tx-viewer/helpers");
45
+ const sapling_forger_1 = require("./sapling-forger/sapling-forger");
46
+ const sapling_transaction_viewer_1 = require("./sapling-tx-viewer/sapling-transaction-viewer");
47
+ const sapling_transactions_builder_1 = require("./sapling-tx-builder/sapling-transactions-builder");
48
+ const constants_1 = require("./constants");
49
+ const octez_js_core_1 = require("@tezos-x/octez.js-core");
50
+ var sapling_transaction_viewer_2 = require("./sapling-tx-viewer/sapling-transaction-viewer");
51
+ Object.defineProperty(exports, "SaplingTransactionViewer", { enumerable: true, get: function () { return sapling_transaction_viewer_2.SaplingTransactionViewer; } });
52
+ var in_memory_viewing_key_1 = require("./sapling-keys/in-memory-viewing-key");
53
+ Object.defineProperty(exports, "InMemoryViewingKey", { enumerable: true, get: function () { return in_memory_viewing_key_1.InMemoryViewingKey; } });
54
+ var in_memory_spending_key_1 = require("./sapling-keys/in-memory-spending-key");
55
+ Object.defineProperty(exports, "InMemorySpendingKey", { enumerable: true, get: function () { return in_memory_spending_key_1.InMemorySpendingKey; } });
56
+ var in_memory_proving_key_1 = require("./sapling-keys/in-memory-proving-key");
57
+ Object.defineProperty(exports, "InMemoryProvingKey", { enumerable: true, get: function () { return in_memory_proving_key_1.InMemoryProvingKey; } });
58
+ /**
59
+ * @description Class that surfaces all of the sapling capability allowing to read from a sapling state and prepare transactions
60
+ *
61
+ * @param keys.saplingSigner Holds the sapling spending key
62
+ * @param keys.saplingProver (Optional) Allows to generate the proofs with the proving key rather than the spending key
63
+ * @param saplingContractDetails Contains the address of the sapling contract, the memo size, and an optional sapling id that must be defined if the sapling contract contains more than one sapling state
64
+ * @param readProvider Allows to read data from the blockchain
65
+ * @param packer (Optional) Allows packing data. Use the `MichelCodecPacker` by default.
66
+ * @param saplingForger (Optional) Allows serializing the sapling transactions. Use the `SaplingForger` by default.
67
+ * @param saplingTxBuilder (Optional) Allows to prepare the sapling transactions. Use the `SaplingTransactionBuilder` by default.
68
+ * @example
69
+ * ```
70
+ * const inMemorySpendingKey = await InMemorySpendingKey.fromMnemonic('YOUR_MNEMONIC');
71
+ * const readProvider = new RpcReadAdapter(new RpcClient('https://YOUR_PREFERRED_RPC_URL'))
72
+ *
73
+ * const saplingToolkit = new SaplingToolkit(
74
+ * { saplingSigner: inMemorySpendingKey },
75
+ * { contractAddress: SAPLING_CONTRACT_ADDRESS, memoSize: 8 },
76
+ * readProvider
77
+ * )
78
+ * ```
79
+ */
80
+ class SaplingToolkit {
81
+ constructor(keys, saplingContractDetails, readProvider, packer = new octez_js_1.MichelCodecPacker(), saplingForger = new sapling_forger_1.SaplingForger(), saplingTxBuilder = new sapling_transactions_builder_1.SaplingTransactionBuilder(keys, saplingForger, saplingContractDetails, readProvider)) {
82
+ _SaplingToolkit_inMemorySpendingKey.set(this, void 0);
83
+ _SaplingToolkit_saplingId.set(this, void 0);
84
+ _SaplingToolkit_contractAddress.set(this, void 0);
85
+ _SaplingToolkit_memoSize.set(this, void 0);
86
+ _SaplingToolkit_readProvider.set(this, void 0);
87
+ _SaplingToolkit_packer.set(this, void 0);
88
+ _SaplingToolkit_saplingForger.set(this, void 0);
89
+ _SaplingToolkit_saplingTxBuilder.set(this, void 0);
90
+ _SaplingToolkit_saplingTransactionViewer.set(this, void 0);
91
+ __classPrivateFieldSet(this, _SaplingToolkit_inMemorySpendingKey, keys.saplingSigner, "f");
92
+ __classPrivateFieldSet(this, _SaplingToolkit_saplingId, saplingContractDetails.saplingId, "f");
93
+ __classPrivateFieldSet(this, _SaplingToolkit_contractAddress, saplingContractDetails.contractAddress, "f");
94
+ __classPrivateFieldSet(this, _SaplingToolkit_memoSize, saplingContractDetails.memoSize, "f");
95
+ __classPrivateFieldSet(this, _SaplingToolkit_readProvider, readProvider, "f");
96
+ __classPrivateFieldSet(this, _SaplingToolkit_packer, packer, "f");
97
+ __classPrivateFieldSet(this, _SaplingToolkit_saplingForger, saplingForger, "f");
98
+ __classPrivateFieldSet(this, _SaplingToolkit_saplingTxBuilder, saplingTxBuilder, "f");
99
+ }
100
+ /**
101
+ * @description Get an instance of `SaplingTransactionViewer` which allows to retrieve and decrypt sapling transactions and calculate the unspent balance.
102
+ */
103
+ getSaplingTransactionViewer() {
104
+ return __awaiter(this, void 0, void 0, function* () {
105
+ let saplingTransactionViewer;
106
+ if (!__classPrivateFieldGet(this, _SaplingToolkit_saplingTransactionViewer, "f")) {
107
+ const saplingViewingKey = yield __classPrivateFieldGet(this, _SaplingToolkit_inMemorySpendingKey, "f").getSaplingViewingKeyProvider();
108
+ saplingTransactionViewer = new sapling_transaction_viewer_1.SaplingTransactionViewer(saplingViewingKey, this.getSaplingContractId(), __classPrivateFieldGet(this, _SaplingToolkit_readProvider, "f"));
109
+ __classPrivateFieldSet(this, _SaplingToolkit_saplingTransactionViewer, saplingTransactionViewer, "f");
110
+ }
111
+ return __classPrivateFieldGet(this, _SaplingToolkit_saplingTransactionViewer, "f");
112
+ });
113
+ }
114
+ /**
115
+ * @description Prepare a shielded transaction
116
+ * @param shieldedTxParams `to` is the payment address that will receive the shielded tokens (zet).
117
+ * `amount` is the amount of shielded tokens in tez by default.
118
+ * `mutez` needs to be set to true if the amount of shielded tokens is in mutez.
119
+ * `memo` is an empty string by default.
120
+ * @returns a string representing the sapling transaction
121
+ */
122
+ prepareShieldedTransaction(shieldedTxParams) {
123
+ return __awaiter(this, void 0, void 0, function* () {
124
+ const { formatedParams, totalAmount } = this.formatTransactionParams(shieldedTxParams, this.validateDestinationSaplingAddress);
125
+ const root = yield this.getRoot();
126
+ const { inputs, outputs, signature, balance } = yield __classPrivateFieldGet(this, _SaplingToolkit_saplingTxBuilder, "f").createShieldedTx(formatedParams, totalAmount, constants_1.DEFAULT_BOUND_DATA);
127
+ const forgedSaplingTx = __classPrivateFieldGet(this, _SaplingToolkit_saplingForger, "f").forgeSaplingTransaction({
128
+ inputs,
129
+ outputs,
130
+ balance,
131
+ root,
132
+ boundData: constants_1.DEFAULT_BOUND_DATA,
133
+ signature,
134
+ });
135
+ return forgedSaplingTx.toString('hex');
136
+ });
137
+ }
138
+ /**
139
+ * @description Prepare an unshielded transaction
140
+ * @param unshieldedTxParams `to` is the Tezos address that will receive the unshielded tokens (tz1, tz2 or tz3).
141
+ * `amount` is the amount of unshielded tokens in tez by default.
142
+ * `mutez` needs to be set to true if the amount of unshielded tokens is in mutez.
143
+ * @returns a string representing the sapling transaction.
144
+ */
145
+ prepareUnshieldedTransaction(unshieldedTxParams) {
146
+ return __awaiter(this, void 0, void 0, function* () {
147
+ const { formatedParams, totalAmount } = this.formatTransactionParams([unshieldedTxParams], this.validateDestinationImplicitAddress);
148
+ const boundData = yield this.createBoundData(formatedParams[0].to);
149
+ const root = yield this.getRoot();
150
+ const chosenInputs = yield this.selectInputsToSpend(new bignumber_js_1.default(formatedParams[0].amount));
151
+ const { inputs, outputs, signature, balance } = yield __classPrivateFieldGet(this, _SaplingToolkit_saplingTxBuilder, "f").createSaplingTx([], totalAmount, boundData, chosenInputs);
152
+ const forgedSaplingTx = __classPrivateFieldGet(this, _SaplingToolkit_saplingForger, "f").forgeSaplingTransaction({
153
+ inputs,
154
+ outputs,
155
+ balance,
156
+ root,
157
+ boundData,
158
+ signature,
159
+ });
160
+ return forgedSaplingTx.toString('hex');
161
+ });
162
+ }
163
+ /**
164
+ * @description Prepare a sapling transaction (zet to zet)
165
+ * @param saplingTxParams `to` is the payment address that will receive the shielded tokens (zet).
166
+ * `amount` is the amount of unshielded tokens in tez by default.
167
+ * `mutez` needs to be set to true if the amount of unshielded tokens is in mutez.
168
+ * `memo` is an empty string by default.
169
+ * @returns a string representing the sapling transaction.
170
+ */
171
+ prepareSaplingTransaction(saplingTxParams) {
172
+ return __awaiter(this, void 0, void 0, function* () {
173
+ const { formatedParams, totalAmount } = this.formatTransactionParams(saplingTxParams, this.validateDestinationSaplingAddress);
174
+ const root = yield this.getRoot();
175
+ const chosenInputs = yield this.selectInputsToSpend(totalAmount);
176
+ const { inputs, outputs, signature, balance } = yield __classPrivateFieldGet(this, _SaplingToolkit_saplingTxBuilder, "f").createSaplingTx(formatedParams, totalAmount, constants_1.DEFAULT_BOUND_DATA, chosenInputs);
177
+ const forgedSaplingTx = __classPrivateFieldGet(this, _SaplingToolkit_saplingForger, "f").forgeSaplingTransaction({
178
+ inputs,
179
+ outputs,
180
+ balance,
181
+ root,
182
+ boundData: constants_1.DEFAULT_BOUND_DATA,
183
+ signature,
184
+ });
185
+ return forgedSaplingTx.toString('hex');
186
+ });
187
+ }
188
+ formatTransactionParams(txParams, validateDestination) {
189
+ const formatedParams = [];
190
+ let totalAmount = new bignumber_js_1.default(0);
191
+ txParams.forEach((param) => {
192
+ var _a;
193
+ validateDestination(param.to);
194
+ const amountMutez = param.mutez
195
+ ? param.amount.toString()
196
+ : (0, octez_js_utils_1.format)('tz', 'mutez', param.amount).toString();
197
+ totalAmount = totalAmount.plus(new bignumber_js_1.default(amountMutez));
198
+ const memo = (_a = param.memo) !== null && _a !== void 0 ? _a : constants_1.DEFAULT_MEMO;
199
+ if (memo.length > __classPrivateFieldGet(this, _SaplingToolkit_memoSize, "f")) {
200
+ throw new errors_1.InvalidMemo(memo, `expecting length to be less than ${__classPrivateFieldGet(this, _SaplingToolkit_memoSize, "f")}`);
201
+ }
202
+ formatedParams.push({ to: param.to, amount: amountMutez, memo });
203
+ });
204
+ return { formatedParams, totalAmount };
205
+ }
206
+ getRoot() {
207
+ return __awaiter(this, void 0, void 0, function* () {
208
+ if (__classPrivateFieldGet(this, _SaplingToolkit_saplingId, "f")) {
209
+ const { root } = yield __classPrivateFieldGet(this, _SaplingToolkit_readProvider, "f").getSaplingDiffById({ id: __classPrivateFieldGet(this, _SaplingToolkit_saplingId, "f") }, 'head');
210
+ return root;
211
+ }
212
+ else {
213
+ const { root } = yield __classPrivateFieldGet(this, _SaplingToolkit_readProvider, "f").getSaplingDiffByContract(__classPrivateFieldGet(this, _SaplingToolkit_contractAddress, "f"), 'head');
214
+ return root;
215
+ }
216
+ });
217
+ }
218
+ createBoundData(destination) {
219
+ return __awaiter(this, void 0, void 0, function* () {
220
+ const bytes = (0, octez_js_utils_1.b58DecodePublicKeyHash)(destination, 'hex');
221
+ const packedDestination = yield __classPrivateFieldGet(this, _SaplingToolkit_packer, "f").packData({
222
+ data: { bytes },
223
+ type: { prim: 'bytes' },
224
+ });
225
+ return Buffer.from(packedDestination.packed, 'hex');
226
+ });
227
+ }
228
+ validateDestinationImplicitAddress(to) {
229
+ const toValidation = (0, octez_js_utils_1.validateKeyHash)(to);
230
+ if (toValidation !== octez_js_utils_1.ValidationResult.VALID) {
231
+ throw new octez_js_core_1.InvalidKeyHashError(to, toValidation);
232
+ }
233
+ }
234
+ validateDestinationSaplingAddress(to) {
235
+ try {
236
+ (0, octez_js_utils_1.b58DecodeAndCheckPrefix)(to, [octez_js_utils_1.PrefixV2.SaplingAddress]);
237
+ }
238
+ catch (_a) {
239
+ throw new octez_js_core_1.InvalidAddressError(to, `expecting prefix ${octez_js_utils_1.PrefixV2.SaplingAddress}.`);
240
+ }
241
+ }
242
+ getSaplingContractId() {
243
+ let saplingContractId;
244
+ if (__classPrivateFieldGet(this, _SaplingToolkit_saplingId, "f")) {
245
+ saplingContractId = { saplingId: __classPrivateFieldGet(this, _SaplingToolkit_saplingId, "f") };
246
+ }
247
+ else {
248
+ saplingContractId = { contractAddress: __classPrivateFieldGet(this, _SaplingToolkit_contractAddress, "f") };
249
+ }
250
+ return saplingContractId;
251
+ }
252
+ selectInputsToSpend(amountMutez) {
253
+ return __awaiter(this, void 0, void 0, function* () {
254
+ const saplingTxViewer = yield this.getSaplingTransactionViewer();
255
+ const { incoming } = yield saplingTxViewer.getIncomingAndOutgoingTransactionsRaw();
256
+ const inputsToSpend = [];
257
+ let sumSelectedInputs = new bignumber_js_1.default(0);
258
+ incoming.forEach((input) => {
259
+ if (!input.isSpent && sumSelectedInputs.isLessThan(amountMutez)) {
260
+ const txAmount = (0, helpers_1.convertValueToBigNumber)(input.value);
261
+ sumSelectedInputs = sumSelectedInputs.plus(txAmount);
262
+ const { isSpent: _isSpent } = input, rest = __rest(input, ["isSpent"]);
263
+ inputsToSpend.push(rest);
264
+ }
265
+ });
266
+ if (sumSelectedInputs.isLessThan(new bignumber_js_1.default(amountMutez))) {
267
+ throw new errors_1.InsufficientBalance(sumSelectedInputs.toString(), amountMutez.toString());
268
+ }
269
+ return { inputsToSpend, sumSelectedInputs };
270
+ });
271
+ }
272
+ }
273
+ exports.SaplingToolkit = SaplingToolkit;
274
+ _SaplingToolkit_inMemorySpendingKey = new WeakMap(), _SaplingToolkit_saplingId = new WeakMap(), _SaplingToolkit_contractAddress = new WeakMap(), _SaplingToolkit_memoSize = new WeakMap(), _SaplingToolkit_readProvider = new WeakMap(), _SaplingToolkit_packer = new WeakMap(), _SaplingToolkit_saplingForger = new WeakMap(), _SaplingToolkit_saplingTxBuilder = new WeakMap(), _SaplingToolkit_saplingTransactionViewer = new WeakMap();
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SaplingForger = void 0;
4
+ const octez_js_utils_1 = require("@tezos-x/octez.js-utils");
5
+ const bignumber_js_1 = require("bignumber.js");
6
+ class SaplingForger {
7
+ /**
8
+ * @description Forge sapling transactions
9
+ * @param spendDescriptions the list of spend descriptions
10
+ * @param outputDescriptions the list of output descriptions
11
+ * @param signature signature hash
12
+ * @param balance balance of the Sapling contract (input/output difference)
13
+ * @param root root of the merkle tree
14
+ * @returns Forged sapling transaction of type Buffer
15
+ */
16
+ forgeSaplingTransaction(tx) {
17
+ const spendBuf = this.forgeSpendDescriptions(tx.inputs);
18
+ const spend = Buffer.concat([(0, octez_js_utils_1.toHexBuf)(spendBuf.length, 32), spendBuf]);
19
+ const outputBuf = this.forgeOutputDescriptions(tx.outputs);
20
+ const output = Buffer.concat([(0, octez_js_utils_1.toHexBuf)(outputBuf.length, 32), outputBuf]);
21
+ const root = Buffer.from(tx.root, 'hex');
22
+ return Buffer.concat([
23
+ spend,
24
+ output,
25
+ tx.signature,
26
+ (0, octez_js_utils_1.toHexBuf)(tx.balance, 64),
27
+ root,
28
+ (0, octez_js_utils_1.toHexBuf)(tx.boundData.length, 32),
29
+ tx.boundData,
30
+ ]);
31
+ }
32
+ /**
33
+ * @description Forge list of spend descriptions
34
+ * @param spendDescriptions list of spend descriptions
35
+ * @returns concatenated forged bytes of type Buffer
36
+ */
37
+ forgeSpendDescriptions(spendDescriptions) {
38
+ const descriptions = [];
39
+ for (const i of spendDescriptions) {
40
+ const buff = this.forgeSpendDescription(i);
41
+ descriptions.push(buff);
42
+ }
43
+ return Buffer.concat(descriptions);
44
+ }
45
+ forgeSpendDescription(desc) {
46
+ return Buffer.concat([
47
+ desc.commitmentValue,
48
+ desc.nullifier,
49
+ desc.publicKeyReRandomization,
50
+ desc.proof,
51
+ desc.signature,
52
+ ]);
53
+ }
54
+ /**
55
+ * @description Forge list of output descriptions
56
+ * @param outputDescriptions list of output descriptions
57
+ * @returns concatenated forged bytes of type Buffer
58
+ */
59
+ forgeOutputDescriptions(outputDescriptions) {
60
+ const descriptions = [];
61
+ for (const i of outputDescriptions) {
62
+ const buff = this.forgeOutputDescription(i);
63
+ descriptions.push(buff);
64
+ }
65
+ return Buffer.concat(descriptions);
66
+ }
67
+ forgeOutputDescription(desc) {
68
+ const ct = desc.ciphertext;
69
+ return Buffer.concat([
70
+ desc.commitment,
71
+ desc.proof,
72
+ ct.commitmentValue,
73
+ ct.ephemeralPublicKey,
74
+ (0, octez_js_utils_1.toHexBuf)(ct.payloadEnc.length, 32),
75
+ ct.payloadEnc,
76
+ ct.nonceEnc,
77
+ ct.payloadOut,
78
+ ct.nonceOut,
79
+ ]);
80
+ }
81
+ forgeUnsignedTxInput(unsignedSpendDescription) {
82
+ return Buffer.concat([
83
+ unsignedSpendDescription.commitmentValue,
84
+ unsignedSpendDescription.nullifier,
85
+ unsignedSpendDescription.publicKeyReRandomization,
86
+ unsignedSpendDescription.proof,
87
+ ]);
88
+ }
89
+ forgeTransactionPlaintext(txPlainText) {
90
+ const encodedMemo = Buffer.from((0, octez_js_utils_1.stringToBytes)(txPlainText.memo).padEnd(txPlainText.memoSize, '0'), 'hex');
91
+ return Buffer.concat([
92
+ txPlainText.diversifier,
93
+ (0, octez_js_utils_1.toHexBuf)(new bignumber_js_1.default(txPlainText.amount), 64),
94
+ txPlainText.randomCommitmentTrapdoor,
95
+ (0, octez_js_utils_1.toHexBuf)(txPlainText.memoSize, 32),
96
+ encodedMemo,
97
+ ]);
98
+ }
99
+ }
100
+ exports.SaplingForger = SaplingForger;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.decryptKey = decryptKey;
4
+ const errors_1 = require("../errors");
5
+ const typedarray_to_buffer_1 = require("typedarray-to-buffer");
6
+ const nacl_1 = require("@stablelib/nacl");
7
+ const pbkdf2_1 = require("pbkdf2");
8
+ const octez_js_utils_1 = require("@tezos-x/octez.js-utils");
9
+ const octez_js_core_1 = require("@tezos-x/octez.js-core");
10
+ function decryptKey(spendingKey, password) {
11
+ const [keyArr, pre] = (() => {
12
+ try {
13
+ return (0, octez_js_utils_1.b58DecodeAndCheckPrefix)(spendingKey, [
14
+ octez_js_utils_1.PrefixV2.SaplingSpendingKey,
15
+ octez_js_utils_1.PrefixV2.EncryptedSaplingSpendingKey,
16
+ ]);
17
+ }
18
+ catch (err) {
19
+ if (err instanceof octez_js_core_1.ParameterValidationError) {
20
+ throw new errors_1.InvalidSpendingKey(spendingKey, 'invalid spending key');
21
+ }
22
+ else {
23
+ throw err;
24
+ }
25
+ }
26
+ })();
27
+ if (pre === octez_js_utils_1.PrefixV2.EncryptedSaplingSpendingKey) {
28
+ if (!password) {
29
+ throw new errors_1.InvalidSpendingKey(spendingKey, 'no password provided to decrypt');
30
+ }
31
+ const salt = (0, typedarray_to_buffer_1.default)(keyArr.slice(0, 8));
32
+ const encryptedSk = (0, typedarray_to_buffer_1.default)(keyArr.slice(8));
33
+ const encryptionKey = pbkdf2_1.default.pbkdf2Sync(password, salt, 32768, 32, 'sha512');
34
+ // Zero nonce is safe: fresh random salt per encryption produces unique PBKDF2-derived key.
35
+ // See: https://gitlab.com/tezos/tezos/-/blob/master/src/lib_signer_backends/encrypted.ml
36
+ const decrypted = (0, nacl_1.openSecretBox)(new Uint8Array(encryptionKey), new Uint8Array(24), // zero nonce - uniqueness provided by per-encryption derived key
37
+ new Uint8Array(encryptedSk));
38
+ if (!decrypted) {
39
+ throw new errors_1.InvalidSpendingKey(spendingKey, 'incorrect password or unable to decrypt');
40
+ }
41
+ return (0, typedarray_to_buffer_1.default)(decrypted);
42
+ }
43
+ else {
44
+ return (0, typedarray_to_buffer_1.default)(keyArr);
45
+ }
46
+ }