shield-bridge-sdk
Version:
195 lines (194 loc) • 7.9 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { expose } from 'threads/worker';
import { RpcReadAdapter } from '@taquito/taquito';
import { SaplingToolkit, InMemorySpendingKey, InMemoryViewingKey, SaplingTransactionViewer, } from '@taquito/sapling';
import { RpcClient } from '@taquito/rpc';
import { PrefixV2, b58Encode } from '@taquito/utils';
import * as sapling from '@airgap/sapling-wasm';
import * as bip39 from 'bip39';
const SECRET_KEY_METHOD = 'secretKey';
const MNEMONIC_METHOD = 'mnemonic';
const VIEWING_KEY_METHOD = 'viewingKey';
let iMSK;
let iMVK;
let sTk;
let isViewOnly = false;
let currentSaplingDetails = null;
let currentRpcAdapter = null;
const createExtendedSpendingKey = (mnemonic) => __awaiter(void 0, void 0, void 0, function* () {
const fullSeed = yield bip39.mnemonicToSeed(mnemonic);
const first32 = fullSeed.subarray(0, 32);
const second32 = fullSeed.subarray(32);
const seed = Buffer.from(
// eslint-disable-next-line no-bitwise
first32.map((byte, index) => byte ^ second32[index]));
const spendingKeyArr = new Uint8Array(yield sapling.getExtendedSpendingKey(seed, 'm/'));
return b58Encode(spendingKeyArr, PrefixV2.SaplingSpendingKey);
});
const loadSaplingSecret = (_a) => __awaiter(void 0, [_a], void 0, function* ({ sk, saplingDetails, rpcUrl, skType = MNEMONIC_METHOD, }) {
try {
const secretKey = sk;
const loadAccountMethod = skType;
// Reset previous state
iMSK = null;
iMVK = null;
isViewOnly = false;
if (loadAccountMethod === SECRET_KEY_METHOD) {
iMSK = new InMemorySpendingKey(secretKey);
}
else if (loadAccountMethod === MNEMONIC_METHOD) {
iMSK = yield InMemorySpendingKey.fromMnemonic(secretKey);
}
else if (loadAccountMethod === VIEWING_KEY_METHOD) {
iMVK = new InMemoryViewingKey(secretKey);
isViewOnly = true;
}
else {
throw new Error('Invalid account loading method provided');
}
// Store sapling details and RPC adapter for later use
currentSaplingDetails = saplingDetails;
currentRpcAdapter = new RpcReadAdapter(new RpcClient(rpcUrl));
}
catch (err) {
iMSK = null;
iMVK = null;
sTk = null;
isViewOnly = false;
currentSaplingDetails = null;
currentRpcAdapter = null;
throw err;
}
try {
// Create toolkit - only works with spending key for transactions
if (!isViewOnly) {
sTk = new SaplingToolkit({ saplingSigner: iMSK }, saplingDetails, currentRpcAdapter);
}
else {
// For view-only mode, we don't need SaplingToolkit for transactions
// The viewing key will be used directly for balance and transaction queries
sTk = null;
}
}
catch (err) {
iMSK = null;
iMVK = null;
sTk = null;
isViewOnly = false;
currentSaplingDetails = null;
currentRpcAdapter = null;
throw err;
}
});
const getViewingKey = () => __awaiter(void 0, void 0, void 0, function* () {
if (isViewOnly && iMVK) {
// Already have viewing key, return it as hex string
const fvk = iMVK.getFullViewingKey();
return Buffer.from(fvk).toString('hex');
}
if (iMSK) {
// Get viewing key from spending key
const viewingKeyProvider = yield iMSK.getSaplingViewingKeyProvider();
const fvk = viewingKeyProvider.getFullViewingKey();
return Buffer.from(fvk).toString('hex');
}
throw new Error('No spending key or viewing key loaded');
});
const getPaymentAddress = () => __awaiter(void 0, void 0, void 0, function* () {
if (isViewOnly && iMVK) {
return iMVK.getAddress();
}
if (iMSK) {
const viewingKeyProvider = yield iMSK.getSaplingViewingKeyProvider();
return viewingKeyProvider.getAddress();
}
throw new Error('No spending key or viewing key loaded');
});
const prepareShieldedTransaction = (shieldTransactions) => {
if (isViewOnly) {
throw new Error('Cannot prepare transactions with a viewing key. A spending key is required.');
}
return sTk.prepareShieldedTransaction(shieldTransactions);
};
const prepareUnshieldedTransaction = (unshieldTransaction) => {
if (isViewOnly) {
throw new Error('Cannot prepare transactions with a viewing key. A spending key is required.');
}
return sTk.prepareUnshieldedTransaction(unshieldTransaction);
};
const prepareSaplingTransaction = (saplingTransactions = []) => {
if (isViewOnly) {
throw new Error('Cannot prepare transactions with a viewing key. A spending key is required.');
}
return sTk.prepareSaplingTransaction(saplingTransactions);
};
const getSaplingBalance = () => __awaiter(void 0, void 0, void 0, function* () {
let txViewer;
if (isViewOnly && iMVK) {
// Create transaction viewer from viewing key
if (!currentSaplingDetails || !currentRpcAdapter) {
throw new Error('Sapling details not initialized');
}
const saplingContractId = currentSaplingDetails.saplingId
? { saplingId: currentSaplingDetails.saplingId }
: { contractAddress: currentSaplingDetails.contractAddress };
txViewer = new SaplingTransactionViewer(iMVK, saplingContractId, currentRpcAdapter);
}
else if (sTk) {
txViewer = yield sTk.getSaplingTransactionViewer();
}
else {
throw new Error('No sapling toolkit or viewing key available');
}
const balance = yield txViewer.getBalance();
return balance.toNumber();
});
const getSaplingTransactions = () => __awaiter(void 0, void 0, void 0, function* () {
let txViewer;
if (isViewOnly && iMVK) {
// Create transaction viewer from viewing key
if (!currentSaplingDetails || !currentRpcAdapter) {
throw new Error('Sapling details not initialized');
}
const saplingContractId = currentSaplingDetails.saplingId
? { saplingId: currentSaplingDetails.saplingId }
: { contractAddress: currentSaplingDetails.contractAddress };
txViewer = new SaplingTransactionViewer(iMVK, saplingContractId, currentRpcAdapter);
}
else if (sTk) {
txViewer = yield sTk.getSaplingTransactionViewer();
}
else {
throw new Error('No sapling toolkit or viewing key available');
}
const transactionHistory = yield txViewer.getIncomingAndOutgoingTransactions();
return {
incoming: transactionHistory.incoming.map((tx) => (Object.assign(Object.assign({}, tx), { value: tx.value.toNumber() }))),
outgoing: transactionHistory.outgoing.map((tx) => (Object.assign(Object.assign({}, tx), { value: tx.value.toNumber() }))),
};
});
const reInitializeSapling = () => {
iMSK = null;
sTk = null;
};
const saplingWorker = {
createExtendedSpendingKey,
loadSaplingSecret,
getPaymentAddress,
getViewingKey,
prepareShieldedTransaction,
prepareUnshieldedTransaction,
prepareSaplingTransaction,
getSaplingBalance,
getSaplingTransactions,
reInitializeSapling,
};
expose(saplingWorker);