forked from tornadocash/tornado-cli
Added Encrypted Note Service
This commit is contained in:
parent
3f55d5ca99
commit
195da66ce2
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,3 +4,4 @@ node_modules
|
||||
/trees
|
||||
backup-tornado-*
|
||||
backup-tornadoInvoice-*
|
||||
backup-note-account-*
|
@ -26,6 +26,8 @@
|
||||
"compliance": "ts-node src/cli.ts compliance",
|
||||
"syncEvents": "ts-node src/cli.ts syncEvents",
|
||||
"relayers": "ts-node src/cli.ts relayers",
|
||||
"createNoteAccount": "ts-node src/cli.ts createNoteAccount",
|
||||
"decryptNotes": "ts-node src/cli.ts decryptNotes",
|
||||
"send": "ts-node src/cli.ts send",
|
||||
"balance": "ts-node src/cli.ts balance",
|
||||
"sign": "ts-node src/cli.ts sign",
|
||||
@ -50,6 +52,7 @@
|
||||
"yarn.lock"
|
||||
],
|
||||
"dependencies": {
|
||||
"@metamask/eth-sig-util": "^7.0.1",
|
||||
"@tornado/contracts": "1.0.0",
|
||||
"@tornado/fixed-merkle-tree": "0.7.3",
|
||||
"@tornado/snarkjs": "0.1.20",
|
||||
|
214
src/program.ts
214
src/program.ts
@ -73,10 +73,13 @@ import {
|
||||
fetchData,
|
||||
fetchDataOptions,
|
||||
networkConfig,
|
||||
getInstanceByAddress,
|
||||
subdomains,
|
||||
Config,
|
||||
enabledChains,
|
||||
substring,
|
||||
NoteAccount,
|
||||
parseRecoveryKey,
|
||||
} from './services';
|
||||
|
||||
const DEFAULT_GAS_LIMIT = 600_000;
|
||||
@ -109,6 +112,7 @@ export type commonProgramOptions = {
|
||||
graph?: string;
|
||||
ethGraph?: string;
|
||||
disableGraph?: boolean;
|
||||
accountKey?: string;
|
||||
relayer?: string;
|
||||
walletWithdrawal?: boolean;
|
||||
torPort?: number;
|
||||
@ -163,6 +167,7 @@ export async function getProgramOptions(options: commonProgramOptions): Promise<
|
||||
graph: options.graph || (process.env.GRAPH_URL ? parseUrl(process.env.GRAPH_URL) : undefined),
|
||||
ethGraph: options.ethGraph || (process.env.ETHGRAPH_URL ? parseUrl(process.env.ETHGRAPH_URL) : undefined),
|
||||
disableGraph: Boolean(options.disableGraph) || (process.env.DISABLE_GRAPH === 'true' ? true : undefined),
|
||||
accountKey: options.accountKey || (process.env.ACCOUNT_KEY ? parseRecoveryKey(process.env.ACCOUNT_KEY) : undefined),
|
||||
relayer: options.relayer || (process.env.RELAYER ? parseRelayer(process.env.RELAYER) : undefined),
|
||||
walletWithdrawal:
|
||||
Boolean(options.walletWithdrawal) || (process.env.WALLET_WITHDRAWAL === 'true' ? true : undefined),
|
||||
@ -851,9 +856,9 @@ export function tornadoProgram() {
|
||||
// If we have MERKLE_WORKER_PATH run worker at background otherwise resolve it here
|
||||
const depositTreeInitiator = await (async () => {
|
||||
if (MERKLE_WORKER_PATH) {
|
||||
return () => merkleTreeService.verifyTree({ events: depositEvents }) as Promise<MerkleTree>;
|
||||
return () => merkleTreeService.verifyTree(depositEvents) as Promise<MerkleTree>;
|
||||
}
|
||||
return (await merkleTreeService.verifyTree({ events: depositEvents })) as MerkleTree;
|
||||
return (await merkleTreeService.verifyTree(depositEvents)) as MerkleTree;
|
||||
})();
|
||||
|
||||
let depositTreePromise: Promise<MerkleTree> | MerkleTree;
|
||||
@ -1178,9 +1183,9 @@ export function tornadoProgram() {
|
||||
// If we have MERKLE_WORKER_PATH run worker at background otherwise resolve it here
|
||||
const depositTreePromise = await (async () => {
|
||||
if (MERKLE_WORKER_PATH) {
|
||||
return () => merkleTreeService.verifyTree({ events: depositEvents }) as Promise<MerkleTree>;
|
||||
return () => merkleTreeService.verifyTree(depositEvents) as Promise<MerkleTree>;
|
||||
}
|
||||
return (await merkleTreeService.verifyTree({ events: depositEvents })) as MerkleTree;
|
||||
return (await merkleTreeService.verifyTree(depositEvents)) as MerkleTree;
|
||||
})();
|
||||
|
||||
const [withdrawalEvents] = await Promise.all([
|
||||
@ -1381,12 +1386,9 @@ export function tornadoProgram() {
|
||||
// If we have MERKLE_WORKER_PATH run worker at background otherwise resolve it here
|
||||
const depositTreePromise = await (async () => {
|
||||
if (MERKLE_WORKER_PATH) {
|
||||
return () =>
|
||||
merkleTreeService.verifyTree({ events: depositEvents as DepositsEvents[] }) as Promise<MerkleTree>;
|
||||
return () => merkleTreeService.verifyTree(depositEvents as DepositsEvents[]) as Promise<MerkleTree>;
|
||||
}
|
||||
return (await merkleTreeService.verifyTree({
|
||||
events: depositEvents as DepositsEvents[],
|
||||
})) as MerkleTree;
|
||||
return (await merkleTreeService.verifyTree(depositEvents as DepositsEvents[])) as MerkleTree;
|
||||
})();
|
||||
|
||||
await Promise.all([
|
||||
@ -1463,6 +1465,199 @@ export function tornadoProgram() {
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
program
|
||||
.command('createNoteAccount')
|
||||
.description(
|
||||
'Creates and save on-chain account that would store encrypted notes. \n\n' +
|
||||
'Would first lookup on on-chain records to see if the notes are stored. \n\n' +
|
||||
'Requires a valid signable wallet (mnemonic or a private key) to work (Since they would encrypt or encrypted)',
|
||||
)
|
||||
.argument('<netId>', 'Network Chain ID to connect with (see https://chainlist.org for examples)', parseNumber)
|
||||
.action(async (netId: string | number, cmdOptions: commonProgramOptions) => {
|
||||
const { options, fetchDataOptions } = await getProgramOptions(cmdOptions);
|
||||
const { rpc } = options;
|
||||
|
||||
const config = networkConfig[`netId${netId}`];
|
||||
|
||||
const {
|
||||
echoContract,
|
||||
tornadoSubgraph,
|
||||
constants: { ['NOTE_ACCOUNT_BLOCK']: deployedBlock },
|
||||
} = config;
|
||||
|
||||
const provider = getProgramProvider(netId, rpc, config, {
|
||||
...fetchDataOptions,
|
||||
});
|
||||
|
||||
const signer = getProgramSigner({
|
||||
options,
|
||||
provider,
|
||||
});
|
||||
|
||||
const graphApi = getProgramGraphAPI(options, config);
|
||||
|
||||
if (!signer || signer instanceof VoidSigner) {
|
||||
throw new Error(
|
||||
'No wallet found, make your you have supplied a valid mnemonic or private key before using this command',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find for any existing note accounts
|
||||
*/
|
||||
const walletPublicKey = NoteAccount.getWalletPublicKey(signer);
|
||||
|
||||
const Echoer = Echoer__factory.connect(echoContract, provider);
|
||||
|
||||
const newAccount = new NoteAccount({
|
||||
netId,
|
||||
Echoer,
|
||||
});
|
||||
|
||||
const echoService = new NodeEchoService({
|
||||
netId,
|
||||
provider,
|
||||
graphApi,
|
||||
subgraphName: tornadoSubgraph,
|
||||
Echoer,
|
||||
deployedBlock,
|
||||
fetchDataOptions,
|
||||
cacheDirectory: EVENTS_DIR,
|
||||
userDirectory: SAVED_DIR,
|
||||
});
|
||||
|
||||
console.log('Getting historic note accounts would take a while\n');
|
||||
|
||||
const echoEvents = (await echoService.updateEvents()).events;
|
||||
|
||||
const userEvents = echoEvents.filter(({ address }) => address === signer.address);
|
||||
|
||||
const existingAccounts = userEvents.map((e) => newAccount.decryptAccountWithWallet(signer, e));
|
||||
|
||||
const accountsTable = new Table();
|
||||
|
||||
if (existingAccounts.length) {
|
||||
accountsTable.push(
|
||||
[{ colSpan: 2, content: `Note Accounts (${netId})`, hAlign: 'center' }],
|
||||
[{ colSpan: 2, content: `Backed up by: ${signer.address}`, hAlign: 'center' }],
|
||||
['blockNumber', 'noteAccount'].map((content) => ({ content: colors.red.bold(content) })),
|
||||
...existingAccounts.map(({ blockNumber, recoveryKey }) => {
|
||||
return [blockNumber, recoveryKey];
|
||||
}),
|
||||
);
|
||||
|
||||
console.log(accountsTable.toString() + '\n');
|
||||
} else {
|
||||
accountsTable.push(
|
||||
[{ colSpan: 1, content: `New Note Account (${netId})`, hAlign: 'center' }],
|
||||
['noteAccount'].map((content) => ({ content: colors.red.bold(content) })),
|
||||
[newAccount.recoveryKey],
|
||||
[{ colSpan: 1, content: `Would be backed up by: ${signer.address}`, hAlign: 'center' }],
|
||||
);
|
||||
|
||||
const fileName = `backup-note-account-key-0x${newAccount.recoveryKey.slice(0, 8)}.txt`;
|
||||
|
||||
console.log('\n' + accountsTable.toString() + '\n');
|
||||
|
||||
console.log(`Writing backup to ${fileName}\n`);
|
||||
|
||||
await writeFile(fileName, newAccount.recoveryKey + '\n');
|
||||
|
||||
console.log('Backup encrypted account on-chain to use on UI?\n');
|
||||
|
||||
await promptConfirmation(options.nonInteractive);
|
||||
|
||||
const { data } = newAccount.getEncryptedAccount(walletPublicKey);
|
||||
|
||||
console.log('Sending encrypted note account backup transaction through wallet\n');
|
||||
|
||||
await programSendTransaction({
|
||||
signer: signer as TornadoVoidSigner | TornadoWallet,
|
||||
options,
|
||||
populatedTransaction: await Echoer.echo.populateTransaction(data),
|
||||
});
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
program
|
||||
.command('decryptNotes')
|
||||
.description('Fetch notes from deposit events and decrypt them. \n\n' + 'Requires a valid account key to work')
|
||||
.argument('<netId>', 'Network Chain ID to connect with (see https://chainlist.org for examples)', parseNumber)
|
||||
.argument(
|
||||
'[accountKey]',
|
||||
'Account key generated from UI or the createNoteAccount to store encrypted notes on-chain',
|
||||
parseRecoveryKey,
|
||||
)
|
||||
.action(async (netId: string | number, accountKey: string | undefined, cmdOptions: commonProgramOptions) => {
|
||||
const { options, fetchDataOptions } = await getProgramOptions(cmdOptions);
|
||||
const { rpc } = options;
|
||||
if (!accountKey) {
|
||||
accountKey = options.accountKey;
|
||||
}
|
||||
|
||||
const config = networkConfig[`netId${netId}`];
|
||||
|
||||
const {
|
||||
routerContract,
|
||||
echoContract,
|
||||
tornadoSubgraph,
|
||||
constants: { ENCRYPTED_NOTES_BLOCK },
|
||||
} = config;
|
||||
|
||||
const provider = getProgramProvider(netId, rpc, config, {
|
||||
...fetchDataOptions,
|
||||
});
|
||||
|
||||
const graphApi = getProgramGraphAPI(options, config);
|
||||
|
||||
if (!accountKey) {
|
||||
throw new Error(
|
||||
'No account key find! Please supply correct account key from either UI or find one with createNoteAccount command',
|
||||
);
|
||||
}
|
||||
|
||||
const Echoer = Echoer__factory.connect(echoContract, provider);
|
||||
|
||||
const noteAccount = new NoteAccount({
|
||||
netId,
|
||||
recoveryKey: accountKey,
|
||||
Echoer,
|
||||
});
|
||||
|
||||
const encryptedNotesService = new NodeEncryptedNotesService({
|
||||
netId,
|
||||
provider,
|
||||
graphApi,
|
||||
subgraphName: tornadoSubgraph,
|
||||
Router: TornadoRouter__factory.connect(routerContract, provider),
|
||||
deployedBlock: ENCRYPTED_NOTES_BLOCK,
|
||||
fetchDataOptions,
|
||||
cacheDirectory: EVENTS_DIR,
|
||||
userDirectory: SAVED_DIR,
|
||||
});
|
||||
|
||||
const encryptedNoteEvents = (await encryptedNotesService.updateEvents()).events;
|
||||
|
||||
const accountsTable = new Table();
|
||||
|
||||
accountsTable.push(
|
||||
[{ colSpan: 2, content: `Note Accounts (${netId})`, hAlign: 'center' }],
|
||||
[{ colSpan: 2, content: `Account key: ${accountKey}`, hAlign: 'center' }],
|
||||
['blockNumber', 'note'].map((content) => ({ content: colors.red.bold(content) })),
|
||||
...noteAccount.decryptNotes(encryptedNoteEvents).map(({ blockNumber, address, noteHex }) => {
|
||||
const { amount, currency } = getInstanceByAddress({ netId, address }) as { amount: string; currency: string };
|
||||
|
||||
return [blockNumber, `tornado-${currency}-${amount}-${netId}-${noteHex}`];
|
||||
}),
|
||||
);
|
||||
|
||||
console.log('\n' + accountsTable.toString() + '\n');
|
||||
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
program
|
||||
.command('send')
|
||||
.description('Send ETH or ERC20 token to address.\n\n')
|
||||
@ -1736,6 +1931,7 @@ export function tornadoProgram() {
|
||||
cmd.option('-g, --graph <GRAPH_URL>', 'The Subgraph API that CLI should interact with', parseUrl);
|
||||
cmd.option('-G, --eth-graph <ETHGRAPH_URL>', 'The Ethereum Mainnet Subgraph API that CLI should interact with', parseUrl);
|
||||
cmd.option('-d, --disable-graph', 'Disable Graph API - Does not enable Subgraph API and use only local RPC as an event source');
|
||||
cmd.option('-a, --account-key <ACCOUNT_KEY>', 'Account key generated from UI or the createNoteAccount to store encrypted notes on-chain', parseRecoveryKey);
|
||||
cmd.option('-R, --relayer <RELAYER>', 'Withdraw via relayer (Should be either .eth name or URL)', parseRelayer);
|
||||
cmd.option('-w, --wallet-withdrawal', 'Withdrawal via wallet (Should not be linked with deposits)');
|
||||
cmd.option('-T, --tor-port <TOR_PORT>', 'Optional tor port', parseNumber);
|
||||
|
160
src/services/encryptedNotes.ts
Normal file
160
src/services/encryptedNotes.ts
Normal file
@ -0,0 +1,160 @@
|
||||
import { getEncryptionPublicKey, encrypt, decrypt, EthEncryptedData } from '@metamask/eth-sig-util';
|
||||
import { Echoer } from '@tornado/contracts';
|
||||
import { Wallet, computeAddress } from 'ethers';
|
||||
import { crypto, base64ToBytes, bytesToBase64, bytesToHex, hexToBytes, toFixedHex, concatBytes } from './utils';
|
||||
import { EchoEvents, EncryptedNotesEvents } from './events';
|
||||
|
||||
export interface DecryptedNotes {
|
||||
blockNumber: number;
|
||||
address: string;
|
||||
noteHex: string;
|
||||
}
|
||||
|
||||
export function packEncryptedMessage({ nonce, ephemPublicKey, ciphertext }: EthEncryptedData) {
|
||||
const nonceBuf = toFixedHex(bytesToHex(base64ToBytes(nonce)), 24);
|
||||
const ephemPublicKeyBuf = toFixedHex(bytesToHex(base64ToBytes(ephemPublicKey)), 32);
|
||||
const ciphertextBuf = bytesToHex(base64ToBytes(ciphertext));
|
||||
|
||||
const messageBuff = concatBytes(hexToBytes(nonceBuf), hexToBytes(ephemPublicKeyBuf), hexToBytes(ciphertextBuf));
|
||||
|
||||
return bytesToHex(messageBuff);
|
||||
}
|
||||
|
||||
export function unpackEncryptedMessage(encryptedMessage: string) {
|
||||
const messageBuff = hexToBytes(encryptedMessage);
|
||||
const nonceBuf = bytesToBase64(messageBuff.slice(0, 24));
|
||||
const ephemPublicKeyBuf = bytesToBase64(messageBuff.slice(24, 56));
|
||||
const ciphertextBuf = bytesToBase64(messageBuff.slice(56));
|
||||
|
||||
return {
|
||||
messageBuff: bytesToHex(messageBuff),
|
||||
version: 'x25519-xsalsa20-poly1305',
|
||||
nonce: nonceBuf,
|
||||
ephemPublicKey: ephemPublicKeyBuf,
|
||||
ciphertext: ciphertextBuf,
|
||||
} as EthEncryptedData & {
|
||||
messageBuff: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface NoteAccountConstructor {
|
||||
netId: string | number;
|
||||
blockNumber?: number;
|
||||
// hex
|
||||
recoveryKey?: string;
|
||||
Echoer: Echoer;
|
||||
}
|
||||
|
||||
export class NoteAccount {
|
||||
netId: number;
|
||||
blockNumber?: number;
|
||||
// Dedicated 32 bytes private key only used for note encryption, backed up to an Echoer and local for future derivation
|
||||
// Note that unlike the private key it shouldn't have the 0x prefix
|
||||
recoveryKey: string;
|
||||
// Address derived from recoveryKey, only used for frontend UI
|
||||
recoveryAddress: string;
|
||||
// Note encryption public key derived from recoveryKey
|
||||
recoveryPublicKey: string;
|
||||
Echoer: Echoer;
|
||||
|
||||
constructor({ netId, blockNumber, recoveryKey, Echoer }: NoteAccountConstructor) {
|
||||
if (!recoveryKey) {
|
||||
recoveryKey = bytesToHex(crypto.getRandomValues(new Uint8Array(32))).slice(2);
|
||||
}
|
||||
|
||||
this.netId = Math.floor(Number(netId));
|
||||
this.blockNumber = blockNumber;
|
||||
this.recoveryKey = recoveryKey;
|
||||
this.recoveryAddress = computeAddress('0x' + recoveryKey);
|
||||
this.recoveryPublicKey = getEncryptionPublicKey(recoveryKey);
|
||||
this.Echoer = Echoer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intends to mock eth_getEncryptionPublicKey behavior from MetaMask
|
||||
* In order to make the recoveryKey retrival from Echoer possible from the bare private key
|
||||
*/
|
||||
static getWalletPublicKey(wallet: Wallet) {
|
||||
let { privateKey } = wallet;
|
||||
|
||||
if (privateKey.startsWith('0x')) {
|
||||
privateKey = privateKey.replace('0x', '');
|
||||
}
|
||||
|
||||
// Should return base64 encoded public key
|
||||
return getEncryptionPublicKey(privateKey);
|
||||
}
|
||||
|
||||
// This function intends to provide an encrypted value of recoveryKey for an on-chain Echoer backup purpose
|
||||
// Thus, the pubKey should be derived by a Wallet instance or from Web3 wallets
|
||||
// pubKey: base64 encoded 32 bytes key from https://docs.metamask.io/wallet/reference/eth_getencryptionpublickey/
|
||||
getEncryptedAccount(walletPublicKey: string) {
|
||||
const encryptedData = encrypt({
|
||||
publicKey: walletPublicKey,
|
||||
data: this.recoveryKey,
|
||||
version: 'x25519-xsalsa20-poly1305',
|
||||
});
|
||||
|
||||
const data = packEncryptedMessage(encryptedData);
|
||||
|
||||
return {
|
||||
// Use this later to save hexPrivateKey generated with
|
||||
// Buffer.from(JSON.stringify(encryptedData)).toString('hex')
|
||||
// As we don't use buffer with this library we should leave UI to do the rest
|
||||
encryptedData,
|
||||
// Data that could be used as an echo(data) params
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt Echoer backuped note encryption account with private keys
|
||||
*/
|
||||
decryptAccountWithWallet(wallet: Wallet, event: EchoEvents): NoteAccount {
|
||||
let { privateKey } = wallet;
|
||||
|
||||
if (privateKey.startsWith('0x')) {
|
||||
privateKey = privateKey.replace('0x', '');
|
||||
}
|
||||
|
||||
const unpackedMessage = unpackEncryptedMessage(event.encryptedAccount);
|
||||
|
||||
const recoveryKey = decrypt({
|
||||
encryptedData: unpackedMessage,
|
||||
privateKey,
|
||||
});
|
||||
|
||||
return new NoteAccount({
|
||||
netId: this.netId,
|
||||
blockNumber: event.blockNumber,
|
||||
recoveryKey,
|
||||
Echoer: this.Echoer,
|
||||
});
|
||||
}
|
||||
|
||||
decryptNotes(events: EncryptedNotesEvents[]): DecryptedNotes[] {
|
||||
const decryptedEvents = [];
|
||||
|
||||
for (const event of events) {
|
||||
try {
|
||||
const unpackedMessage = unpackEncryptedMessage(event.encryptedNote);
|
||||
|
||||
const [address, noteHex] = decrypt({
|
||||
encryptedData: unpackedMessage,
|
||||
privateKey: this.recoveryKey,
|
||||
}).split('-');
|
||||
|
||||
decryptedEvents.push({
|
||||
blockNumber: event.blockNumber,
|
||||
address,
|
||||
noteHex,
|
||||
});
|
||||
} catch {
|
||||
// decryption may fail for foreign notes
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return decryptedEvents;
|
||||
}
|
||||
}
|
@ -769,7 +769,7 @@ export async function getAllGraphEchoEvents({
|
||||
blockNumber: Number(e.blockNumber),
|
||||
logIndex: Number(logIndex),
|
||||
transactionHash: transactionHash,
|
||||
address: e.address,
|
||||
address: getAddress(e.address),
|
||||
encryptedAccount: e.encryptedAccount,
|
||||
};
|
||||
});
|
||||
|
@ -4,6 +4,7 @@ export * from './schemas';
|
||||
export * from './batch';
|
||||
export * from './data';
|
||||
export * from './deposits';
|
||||
export * from './encryptedNotes';
|
||||
export * from './fees';
|
||||
export * from './merkleTree';
|
||||
export * from './mimc';
|
||||
|
@ -52,7 +52,7 @@ export class MerkleTreeService {
|
||||
this.merkleWorkerPath = merkleWorkerPath;
|
||||
}
|
||||
|
||||
async createTree({ events }: { events: Element[] }) {
|
||||
async createTree(events: Element[]) {
|
||||
const { hash: hashFunction } = await mimc.getHash();
|
||||
|
||||
if (this.merkleWorkerPath) {
|
||||
@ -113,13 +113,13 @@ export class MerkleTreeService {
|
||||
});
|
||||
}
|
||||
|
||||
async verifyTree({ events }: { events: DepositsEvents[] }) {
|
||||
async verifyTree(events: DepositsEvents[]) {
|
||||
console.log(
|
||||
`\nCreating deposit tree for ${this.netId} ${this.amount} ${this.currency.toUpperCase()} would take a while\n`,
|
||||
);
|
||||
|
||||
console.time('Created tree in');
|
||||
const tree = await this.createTree({ events: events.map(({ commitment }) => BigInt(commitment).toString()) });
|
||||
const tree = await this.createTree(events.map(({ commitment }) => BigInt(commitment).toString()));
|
||||
console.timeEnd('Created tree in');
|
||||
console.log('');
|
||||
|
||||
|
@ -95,6 +95,21 @@ export type networkConfig = {
|
||||
export const blockSyncInterval = 10000;
|
||||
export const enabledChains = ['1', '10', '56', '100', '137', '42161', '43114', '11155111'];
|
||||
|
||||
export function getInstanceByAddress({ netId, address }: { netId: number | string; address: string }) {
|
||||
const { tokens } = networkConfig[`netId${netId}`];
|
||||
|
||||
for (const [currency, { instanceAddress }] of Object.entries(tokens)) {
|
||||
for (const [amount, instance] of Object.entries(instanceAddress)) {
|
||||
if (instance === address) {
|
||||
return {
|
||||
amount,
|
||||
currency,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const theGraph = {
|
||||
name: 'Hosted Graph',
|
||||
url: 'https://api.thegraph.com',
|
||||
|
@ -60,3 +60,18 @@ export function parseKey(value?: string): string {
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recovery key shouldn't have a 0x prefix (Also this is how the UI generates)
|
||||
*/
|
||||
export function parseRecoveryKey(value?: string): string {
|
||||
if (!value) {
|
||||
throw new InvalidArgumentError('Invalid Recovery Key');
|
||||
}
|
||||
try {
|
||||
computeAddress('0x' + value);
|
||||
} catch {
|
||||
throw new InvalidArgumentError('Invalid Recovery Key');
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { URL } from 'url';
|
||||
import { webcrypto } from 'crypto';
|
||||
import BN from 'bn.js';
|
||||
import type { BigNumberish } from 'ethers';
|
||||
|
||||
@ -16,6 +17,8 @@ export const isNode =
|
||||
}
|
||||
).browser && typeof globalThis.window === 'undefined';
|
||||
|
||||
export const crypto = isNode ? webcrypto : (globalThis.crypto as typeof webcrypto);
|
||||
|
||||
export const chunk = <T>(arr: T[], size: number): T[][] =>
|
||||
[...Array(Math.ceil(arr.length / size))].map((_, i) => arr.slice(size * i, size + size * i));
|
||||
|
||||
@ -35,26 +38,28 @@ export function validateUrl(url: string, protocols?: string[]) {
|
||||
}
|
||||
}
|
||||
|
||||
export function concatBytes(...arrays: Uint8Array[]): Uint8Array {
|
||||
const totalSize = arrays.reduce((acc, e) => acc + e.length, 0);
|
||||
const merged = new Uint8Array(totalSize);
|
||||
|
||||
arrays.forEach((array, i, arrays) => {
|
||||
const offset = arrays.slice(0, i).reduce((acc, e) => acc + e.length, 0);
|
||||
merged.set(array, offset);
|
||||
});
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
export function bufferToBytes(b: Buffer) {
|
||||
return new Uint8Array(b.buffer);
|
||||
}
|
||||
|
||||
export function bytesToBase64(bytes: Uint8Array) {
|
||||
let binary = '';
|
||||
const len = bytes.byteLength;
|
||||
for (let i = 0; i < len; ++i) {
|
||||
binary += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
return btoa(binary);
|
||||
return btoa(String.fromCharCode.apply(null, Array.from(bytes)));
|
||||
}
|
||||
|
||||
export function base64ToBytes(base64: string) {
|
||||
const binaryString = atob(base64);
|
||||
const bytes = new Uint8Array(binaryString.length);
|
||||
for (let i = 0; i < binaryString.length; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
return bytes;
|
||||
return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
|
||||
}
|
||||
|
||||
export function bytesToHex(bytes: Uint8Array) {
|
||||
@ -66,6 +71,16 @@ export function bytesToHex(bytes: Uint8Array) {
|
||||
);
|
||||
}
|
||||
|
||||
export function hexToBytes(hexString: string) {
|
||||
if (hexString.slice(0, 2) === '0x') {
|
||||
hexString = hexString.replace('0x', '');
|
||||
}
|
||||
if (hexString.length % 2 !== 0) {
|
||||
hexString = '0' + hexString;
|
||||
}
|
||||
return Uint8Array.from((hexString.match(/.{1,2}/g) as string[]).map((byte) => parseInt(byte, 16)));
|
||||
}
|
||||
|
||||
// Convert BE encoded bytes (Buffer | Uint8Array) array to BigInt
|
||||
export function bytesToBN(bytes: Uint8Array) {
|
||||
return BigInt(bytesToHex(bytes));
|
||||
|
165
yarn.lock
165
yarn.lock
@ -199,6 +199,38 @@
|
||||
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f"
|
||||
integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==
|
||||
|
||||
"@ethereumjs/common@^3.2.0":
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-3.2.0.tgz#b71df25845caf5456449163012074a55f048e0a0"
|
||||
integrity sha512-pksvzI0VyLgmuEF2FA/JR/4/y6hcPq8OUail3/AvycBaW1d5VSauOZzqGvJ3RTmR4MU35lWE8KseKOsEhrFRBA==
|
||||
dependencies:
|
||||
"@ethereumjs/util" "^8.1.0"
|
||||
crc-32 "^1.2.0"
|
||||
|
||||
"@ethereumjs/rlp@^4.0.1":
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-4.0.1.tgz#626fabfd9081baab3d0a3074b0c7ecaf674aaa41"
|
||||
integrity sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==
|
||||
|
||||
"@ethereumjs/tx@^4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-4.2.0.tgz#5988ae15daf5a3b3c815493bc6b495e76009e853"
|
||||
integrity sha512-1nc6VO4jtFd172BbSnTnDQVr9IYBFl1y4xPzZdtkrkKIncBCkdbgfdRV+MiTkJYAtTxvV12GRZLqBFT1PNK6Yw==
|
||||
dependencies:
|
||||
"@ethereumjs/common" "^3.2.0"
|
||||
"@ethereumjs/rlp" "^4.0.1"
|
||||
"@ethereumjs/util" "^8.1.0"
|
||||
ethereum-cryptography "^2.0.0"
|
||||
|
||||
"@ethereumjs/util@^8.1.0":
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@ethereumjs/util/-/util-8.1.0.tgz#299df97fb6b034e0577ce9f94c7d9d1004409ed4"
|
||||
integrity sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==
|
||||
dependencies:
|
||||
"@ethereumjs/rlp" "^4.0.1"
|
||||
ethereum-cryptography "^2.0.0"
|
||||
micro-ftch "^0.3.1"
|
||||
|
||||
"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.7.0":
|
||||
version "5.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449"
|
||||
@ -608,6 +640,41 @@
|
||||
"@jridgewell/resolve-uri" "^3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||
|
||||
"@metamask/abi-utils@^2.0.2":
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@metamask/abi-utils/-/abi-utils-2.0.2.tgz#ad394e9cb8a95ac177cad942daadd88a246c0de8"
|
||||
integrity sha512-B/A1dY/w4F/t6cDHUscklO6ovb/ztFsrsTXFd8QlqSByk/vyy+QbPE3VVpmmyI/7RX+PA1AJcvBdzCIz+r9dVQ==
|
||||
dependencies:
|
||||
"@metamask/utils" "^8.0.0"
|
||||
superstruct "^1.0.3"
|
||||
|
||||
"@metamask/eth-sig-util@^7.0.1":
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-7.0.1.tgz#ad3227d6120f15f9293478de7dd9685a5c329586"
|
||||
integrity sha512-59GSrMyFH2fPfu7nKeIQdZ150zxXNNhAQIUaFRUW+MGtVA4w/ONbiQobcRBLi+jQProfIyss51G8pfLPcQ0ylg==
|
||||
dependencies:
|
||||
"@ethereumjs/util" "^8.1.0"
|
||||
"@metamask/abi-utils" "^2.0.2"
|
||||
"@metamask/utils" "^8.1.0"
|
||||
ethereum-cryptography "^2.1.2"
|
||||
tweetnacl "^1.0.3"
|
||||
tweetnacl-util "^0.15.1"
|
||||
|
||||
"@metamask/utils@^8.0.0", "@metamask/utils@^8.1.0":
|
||||
version "8.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@metamask/utils/-/utils-8.4.0.tgz#f44812c96467a4e1b70b2edff6ee89a9caa4e354"
|
||||
integrity sha512-dbIc3C7alOe0agCuBHM1h71UaEaEqOk2W8rAtEn8QGz4haH2Qq7MoK6i7v2guzvkJVVh79c+QCzIqphC3KvrJg==
|
||||
dependencies:
|
||||
"@ethereumjs/tx" "^4.2.0"
|
||||
"@noble/hashes" "^1.3.1"
|
||||
"@scure/base" "^1.1.3"
|
||||
"@types/debug" "^4.1.7"
|
||||
debug "^4.3.4"
|
||||
pony-cause "^2.1.10"
|
||||
semver "^7.5.4"
|
||||
superstruct "^1.0.3"
|
||||
uuid "^9.0.1"
|
||||
|
||||
"@noble/curves@1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35"
|
||||
@ -615,11 +682,28 @@
|
||||
dependencies:
|
||||
"@noble/hashes" "1.3.2"
|
||||
|
||||
"@noble/curves@1.3.0", "@noble/curves@~1.3.0":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.3.0.tgz#01be46da4fd195822dab821e72f71bf4aeec635e"
|
||||
integrity sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==
|
||||
dependencies:
|
||||
"@noble/hashes" "1.3.3"
|
||||
|
||||
"@noble/hashes@1.3.2":
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39"
|
||||
integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==
|
||||
|
||||
"@noble/hashes@1.3.3", "@noble/hashes@~1.3.2":
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699"
|
||||
integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==
|
||||
|
||||
"@noble/hashes@^1.3.1":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426"
|
||||
integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==
|
||||
|
||||
"@nodelib/fs.scandir@2.1.5":
|
||||
version "2.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
|
||||
@ -771,6 +855,28 @@
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.1.tgz#299eee74b7d87e116083ac5b1ce8dd9434668294"
|
||||
integrity sha512-wQGI+LY/Py20zdUPq+XCem7JcPOyzIJBm3dli+56DJsQOHbnXZFEwgmnC6el1TPAfC8lBT3m+z69RmLykNUbew==
|
||||
|
||||
"@scure/base@^1.1.3", "@scure/base@~1.1.4":
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.6.tgz#8ce5d304b436e4c84f896e0550c83e4d88cb917d"
|
||||
integrity sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g==
|
||||
|
||||
"@scure/bip32@1.3.3":
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.3.tgz#a9624991dc8767087c57999a5d79488f48eae6c8"
|
||||
integrity sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ==
|
||||
dependencies:
|
||||
"@noble/curves" "~1.3.0"
|
||||
"@noble/hashes" "~1.3.2"
|
||||
"@scure/base" "~1.1.4"
|
||||
|
||||
"@scure/bip39@1.2.2":
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.2.tgz#f3426813f4ced11a47489cbcf7294aa963966527"
|
||||
integrity sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA==
|
||||
dependencies:
|
||||
"@noble/hashes" "~1.3.2"
|
||||
"@scure/base" "~1.1.4"
|
||||
|
||||
"@tornado/contracts@1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://git.tornado.ws/api/packages/tornado-packages/npm/%40tornado%2Fcontracts/-/1.0.0/contracts-1.0.0.tgz#4ee8aada3d12bca94b641fbb3d6f552ec3838cbe"
|
||||
@ -845,6 +951,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/circomlibjs/-/circomlibjs-0.1.6.tgz#dba1b9cc68ae4f75da045b8b14c50f3444b31d7f"
|
||||
integrity sha512-yF174bPDaiKgejlZzCSqKwZaqXhlxMcVEHrAtstFohwP05OjtvHXOdxO6HQeTg8WwIdgMg7MJb1WyWZdUCGlPQ==
|
||||
|
||||
"@types/debug@^4.1.7":
|
||||
version "4.1.12"
|
||||
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917"
|
||||
integrity sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==
|
||||
dependencies:
|
||||
"@types/ms" "*"
|
||||
|
||||
"@types/eslint-scope@^3.7.3":
|
||||
version "3.7.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5"
|
||||
@ -881,6 +994,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
||||
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
|
||||
|
||||
"@types/ms@*":
|
||||
version "0.7.34"
|
||||
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433"
|
||||
integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==
|
||||
|
||||
"@types/node-fetch@^2.6.11":
|
||||
version "2.6.11"
|
||||
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.11.tgz#9b39b78665dae0e82a08f02f4967d62c66f95d24"
|
||||
@ -1846,6 +1964,11 @@ core-util-is@~1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
|
||||
integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
|
||||
|
||||
crc-32@^1.2.0:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff"
|
||||
integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==
|
||||
|
||||
create-ecdh@^4.0.0:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e"
|
||||
@ -2540,6 +2663,16 @@ esutils@^2.0.2:
|
||||
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
|
||||
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
|
||||
|
||||
ethereum-cryptography@^2.0.0, ethereum-cryptography@^2.1.2:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz#1352270ed3b339fe25af5ceeadcf1b9c8e30768a"
|
||||
integrity sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA==
|
||||
dependencies:
|
||||
"@noble/curves" "1.3.0"
|
||||
"@noble/hashes" "1.3.3"
|
||||
"@scure/bip32" "1.3.3"
|
||||
"@scure/bip39" "1.2.2"
|
||||
|
||||
ethers@^5.5.1:
|
||||
version "5.7.2"
|
||||
resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e"
|
||||
@ -3654,6 +3787,11 @@ merge2@^1.3.0, merge2@^1.4.1:
|
||||
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
|
||||
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
|
||||
|
||||
micro-ftch@^0.3.1:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/micro-ftch/-/micro-ftch-0.3.1.tgz#6cb83388de4c1f279a034fb0cf96dfc050853c5f"
|
||||
integrity sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==
|
||||
|
||||
micromatch@^4.0.4:
|
||||
version "4.0.5"
|
||||
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
|
||||
@ -4119,6 +4257,11 @@ pkg-dir@^4.2.0:
|
||||
dependencies:
|
||||
find-up "^4.0.0"
|
||||
|
||||
pony-cause@^2.1.10:
|
||||
version "2.1.11"
|
||||
resolved "https://registry.yarnpkg.com/pony-cause/-/pony-cause-2.1.11.tgz#d69a20aaccdb3bdb8f74dd59e5c68d8e6772e4bd"
|
||||
integrity sha512-M7LhCsdNbNgiLYiP4WjsfLUuFmCfnjdF6jKe2R9NKl4WFN+HZPGHJZ9lnLP7f9ZnKe3U9nuWD0szirmj+migUg==
|
||||
|
||||
possible-typed-array-names@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f"
|
||||
@ -4480,7 +4623,7 @@ semver@^6.3.1:
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
||||
|
||||
semver@^7.6.0:
|
||||
semver@^7.5.4, semver@^7.6.0:
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d"
|
||||
integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==
|
||||
@ -4792,6 +4935,11 @@ strip-json-comments@^3.1.1:
|
||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
||||
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
||||
|
||||
superstruct@^1.0.3:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-1.0.4.tgz#0adb99a7578bd2f1c526220da6571b2d485d91ca"
|
||||
integrity sha512-7JpaAoX2NGyoFlI9NBh66BQXGONc+uE+MRS5i2iOBKuS4e+ccgMDjATgZldkah+33DakBxDHiss9kvUcGAO8UQ==
|
||||
|
||||
supports-color@^5.3.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||
@ -4982,6 +5130,16 @@ tty-browserify@^0.0.1:
|
||||
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811"
|
||||
integrity sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==
|
||||
|
||||
tweetnacl-util@^0.15.1:
|
||||
version "0.15.1"
|
||||
resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b"
|
||||
integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==
|
||||
|
||||
tweetnacl@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596"
|
||||
integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==
|
||||
|
||||
type-check@^0.4.0, type-check@~0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
|
||||
@ -5145,6 +5303,11 @@ util@^0.12.5:
|
||||
is-typed-array "^1.1.3"
|
||||
which-typed-array "^1.1.2"
|
||||
|
||||
uuid@^9.0.1:
|
||||
version "9.0.1"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
|
||||
integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
|
||||
|
||||
v8-compile-cache-lib@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
|
||||
|
Loading…
Reference in New Issue
Block a user