tornado-core/dist/index.js

10561 lines
269 KiB
JavaScript
Vendored

'use strict';
var ethers = require('ethers');
var contracts = require('@tornado/contracts');
var crypto$1 = require('crypto');
var BN = require('bn.js');
var contentHashUtils = require('@ensdomains/content-hash');
var crossFetch = require('cross-fetch');
var Ajv = require('ajv');
var fflate = require('fflate');
var circomlibjs = require('circomlibjs');
var ethSigUtil = require('@metamask/eth-sig-util');
var idb = require('idb');
var worker_threads = require('worker_threads');
var fixedMerkleTree = require('@tornado/fixed-merkle-tree');
var websnarkUtils = require('@tornado/websnark/src/utils');
var websnarkGroth = require('@tornado/websnark/src/groth16');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var contentHashUtils__namespace = /*#__PURE__*/_interopNamespaceDefault(contentHashUtils);
var websnarkUtils__namespace = /*#__PURE__*/_interopNamespaceDefault(websnarkUtils);
BigInt.prototype.toJSON = function() {
return this.toString();
};
const isNode = !process.browser && typeof globalThis.window === "undefined";
const crypto = isNode ? crypto$1.webcrypto : globalThis.crypto;
const chunk = (arr, size) => [...Array(Math.ceil(arr.length / size))].map((_, i) => arr.slice(size * i, size + size * i));
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function validateUrl(url, protocols) {
try {
const parsedUrl = new URL(url);
if (protocols && protocols.length) {
return protocols.map((p) => p.toLowerCase()).includes(parsedUrl.protocol);
}
return true;
} catch {
return false;
}
}
function concatBytes(...arrays) {
const totalSize = arrays.reduce((acc, e) => acc + e.length, 0);
const merged = new Uint8Array(totalSize);
arrays.forEach((array, i, arrays2) => {
const offset = arrays2.slice(0, i).reduce((acc, e) => acc + e.length, 0);
merged.set(array, offset);
});
return merged;
}
function bufferToBytes(b) {
return new Uint8Array(b.buffer);
}
function bytesToBase64(bytes) {
return btoa(bytes.reduce((data, byte) => data + String.fromCharCode(byte), ""));
}
function base64ToBytes(base64) {
return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
}
function bytesToHex(bytes) {
return "0x" + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
}
function hexToBytes(hexString) {
if (hexString.slice(0, 2) === "0x") {
hexString = hexString.slice(2);
}
if (hexString.length % 2 !== 0) {
hexString = "0" + hexString;
}
return Uint8Array.from(hexString.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
}
function bytesToBN(bytes) {
return BigInt(bytesToHex(bytes));
}
function bnToBytes(bigint) {
let hexString = typeof bigint === "bigint" ? bigint.toString(16) : bigint;
if (hexString.slice(0, 2) === "0x") {
hexString = hexString.slice(2);
}
if (hexString.length % 2 !== 0) {
hexString = "0" + hexString;
}
return Uint8Array.from(hexString.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
}
function leBuff2Int(bytes) {
return new BN(bytes, 16, "le");
}
function leInt2Buff(bigint) {
return Uint8Array.from(new BN(bigint).toArray("le", 31));
}
function toFixedHex(numberish, length = 32) {
return "0x" + BigInt(numberish).toString(16).padStart(length * 2, "0");
}
function toFixedLength(string, length = 32) {
string = string.replace("0x", "");
return "0x" + string.padStart(length * 2, "0");
}
function rBigInt(nbytes = 31) {
return bytesToBN(crypto.getRandomValues(new Uint8Array(nbytes)));
}
function rHex(nbytes = 32) {
return bytesToHex(crypto.getRandomValues(new Uint8Array(nbytes)));
}
function bigIntReplacer(key, value) {
return typeof value === "bigint" ? value.toString() : value;
}
function substring(str, length = 10) {
if (str.length < length * 2) {
return str;
}
return `${str.substring(0, length)}...${str.substring(str.length - length)}`;
}
async function digest(bytes, algo = "SHA-384") {
return new Uint8Array(await crypto.subtle.digest(algo, bytes));
}
function numberFormatter(num, digits = 3) {
const lookup = [
{ value: 1, symbol: "" },
{ value: 1e3, symbol: "K" },
{ value: 1e6, symbol: "M" },
{ value: 1e9, symbol: "G" },
{ value: 1e12, symbol: "T" },
{ value: 1e15, symbol: "P" },
{ value: 1e18, symbol: "E" }
];
const regexp = /\.0+$|(?<=\.[0-9]*[1-9])0+$/;
const item = lookup.slice().reverse().find((item2) => Number(num) >= item2.value);
return item ? (Number(num) / item.value).toFixed(digits).replace(regexp, "").concat(item.symbol) : "0";
}
function isHex(value) {
return /^0x[0-9a-fA-F]*$/.test(value);
}
function toContentHash(ipfsUrl) {
return contentHashUtils__namespace.fromIpfs(ipfsUrl);
}
function fromContentHash(contentHash) {
return contentHashUtils__namespace.decode(contentHash);
}
function isDeferred(value) {
return value && typeof value === "object" && "getTopicFilter" in value && typeof value.getTopicFilter === "function" && value.fragment;
}
async function getSubInfo(abiInterface, event) {
let topics;
let fragment = null;
if (Array.isArray(event)) {
const topicHashify = function(name) {
if (ethers.isHexString(name, 32)) {
return name;
}
const fragment2 = abiInterface.getEvent(name);
ethers.assertArgument(fragment2, "unknown fragment", "name", name);
return fragment2.topicHash;
};
topics = event.map((e) => {
if (e == null) {
return null;
}
if (Array.isArray(e)) {
return e.map(topicHashify);
}
return topicHashify(e);
});
} else if (event === "*") {
topics = [null];
} else if (typeof event === "string") {
if (ethers.isHexString(event, 32)) {
topics = [event];
} else {
fragment = abiInterface.getEvent(event);
ethers.assertArgument(fragment, "unknown fragment", "event", event);
topics = [fragment.topicHash];
}
} else if (isDeferred(event)) {
topics = await event.getTopicFilter();
} else if ("fragment" in event) {
fragment = event.fragment;
topics = [fragment.topicHash];
} else {
ethers.assertArgument(false, "unknown event name", "event", event);
}
topics = topics.map((t) => {
if (t == null) {
return null;
}
if (Array.isArray(t)) {
const items = Array.from(new Set(t.map((t2) => t2.toLowerCase())).values());
if (items.length === 1) {
return items[0];
}
items.sort();
return items;
}
return t.toLowerCase();
});
const tag = topics.map((t) => {
if (t == null) {
return "null";
}
if (Array.isArray(t)) {
return t.join("|");
}
return t;
}).join("&");
return { fragment, tag, topics };
}
async function multiQueryFilter(address, contract, event, fromBlock, toBlock) {
if (fromBlock == null) {
fromBlock = 0;
}
if (toBlock == null) {
toBlock = "latest";
}
const { fragment, topics } = await getSubInfo(contract.interface, event);
const filter = {
address: address === "*" ? void 0 : address,
topics,
fromBlock,
toBlock
};
const provider = contract.runner;
ethers.assert(provider, "contract runner does not have a provider", "UNSUPPORTED_OPERATION", { operation: "queryFilter" });
return (await provider.getLogs(filter)).map((log) => {
let foundFragment = fragment;
if (foundFragment == null) {
try {
foundFragment = contract.interface.getEvent(log.topics[0]);
} catch {
}
}
if (foundFragment) {
try {
return new ethers.EventLog(log, contract.interface, foundFragment);
} catch (error) {
return new ethers.UndecodedEventLog(log, error);
}
}
return new ethers.Log(log, provider);
});
}
class BatchBlockService {
provider;
onProgress;
concurrencySize;
batchSize;
shouldRetry;
retryMax;
retryOn;
constructor({
provider,
onProgress,
concurrencySize = 10,
batchSize = 10,
shouldRetry = true,
retryMax = 5,
retryOn = 500
}) {
this.provider = provider;
this.onProgress = onProgress;
this.concurrencySize = concurrencySize;
this.batchSize = batchSize;
this.shouldRetry = shouldRetry;
this.retryMax = retryMax;
this.retryOn = retryOn;
}
async getBlock(blockTag) {
const blockObject = await this.provider.getBlock(blockTag);
if (!blockObject) {
const errMsg = `No block for ${blockTag}`;
throw new Error(errMsg);
}
return blockObject;
}
createBatchRequest(batchArray) {
return batchArray.map(async (blocks, index) => {
await sleep(40 * index);
return (async () => {
let retries = 0;
let err;
while (!this.shouldRetry && retries === 0 || this.shouldRetry && retries < this.retryMax) {
try {
return await Promise.all(blocks.map((b) => this.getBlock(b)));
} catch (e) {
retries++;
err = e;
await sleep(this.retryOn);
}
}
throw err;
})();
});
}
async getBatchBlocks(blocks) {
let blockCount = 0;
const results = [];
for (const chunks of chunk(blocks, this.concurrencySize * this.batchSize)) {
const chunksResult = (await Promise.all(this.createBatchRequest(chunk(chunks, this.batchSize)))).flat();
results.push(...chunksResult);
blockCount += chunks.length;
if (typeof this.onProgress === "function") {
this.onProgress({
percentage: blockCount / blocks.length,
currentIndex: blockCount,
totalIndex: blocks.length
});
}
}
return results;
}
}
class BatchTransactionService {
provider;
onProgress;
concurrencySize;
batchSize;
shouldRetry;
retryMax;
retryOn;
constructor({
provider,
onProgress,
concurrencySize = 10,
batchSize = 10,
shouldRetry = true,
retryMax = 5,
retryOn = 500
}) {
this.provider = provider;
this.onProgress = onProgress;
this.concurrencySize = concurrencySize;
this.batchSize = batchSize;
this.shouldRetry = shouldRetry;
this.retryMax = retryMax;
this.retryOn = retryOn;
}
async getTransaction(txHash) {
const txObject = await this.provider.getTransaction(txHash);
if (!txObject) {
const errMsg = `No transaction for ${txHash}`;
throw new Error(errMsg);
}
return txObject;
}
async getTransactionReceipt(txHash) {
const txObject = await this.provider.getTransactionReceipt(txHash);
if (!txObject) {
const errMsg = `No transaction receipt for ${txHash}`;
throw new Error(errMsg);
}
return txObject;
}
createBatchRequest(batchArray, receipt) {
return batchArray.map(async (txs, index) => {
await sleep(40 * index);
return (async () => {
let retries = 0;
let err;
while (!this.shouldRetry && retries === 0 || this.shouldRetry && retries < this.retryMax) {
try {
if (!receipt) {
return await Promise.all(txs.map((tx) => this.getTransaction(tx)));
} else {
return await Promise.all(txs.map((tx) => this.getTransactionReceipt(tx)));
}
} catch (e) {
retries++;
err = e;
await sleep(this.retryOn);
}
}
throw err;
})();
});
}
async getBatchTransactions(txs) {
let txCount = 0;
const results = [];
for (const chunks of chunk(txs, this.concurrencySize * this.batchSize)) {
const chunksResult = (await Promise.all(this.createBatchRequest(chunk(chunks, this.batchSize)))).flat();
results.push(...chunksResult);
txCount += chunks.length;
if (typeof this.onProgress === "function") {
this.onProgress({
percentage: txCount / txs.length,
currentIndex: txCount,
totalIndex: txs.length
});
}
}
return results;
}
async getBatchReceipt(txs) {
let txCount = 0;
const results = [];
for (const chunks of chunk(txs, this.concurrencySize * this.batchSize)) {
const chunksResult = (await Promise.all(this.createBatchRequest(chunk(chunks, this.batchSize), true))).flat();
results.push(...chunksResult);
txCount += chunks.length;
if (typeof this.onProgress === "function") {
this.onProgress({
percentage: txCount / txs.length,
currentIndex: txCount,
totalIndex: txs.length
});
}
}
return results;
}
}
class BatchEventsService {
provider;
contract;
address;
onProgress;
concurrencySize;
blocksPerRequest;
shouldRetry;
retryMax;
retryOn;
constructor({
provider,
contract,
address,
onProgress,
concurrencySize = 10,
blocksPerRequest = 5e3,
shouldRetry = true,
retryMax = 5,
retryOn = 500
}) {
this.provider = provider;
this.contract = contract;
this.address = address;
this.onProgress = onProgress;
this.concurrencySize = concurrencySize;
this.blocksPerRequest = blocksPerRequest;
this.shouldRetry = shouldRetry;
this.retryMax = retryMax;
this.retryOn = retryOn;
}
async getPastEvents({ fromBlock, toBlock, type }) {
let err;
let retries = 0;
while (!this.shouldRetry && retries === 0 || this.shouldRetry && retries < this.retryMax) {
try {
if (this.address) {
return await multiQueryFilter(
this.address,
this.contract,
type,
fromBlock,
toBlock
);
}
return await this.contract.queryFilter(type, fromBlock, toBlock);
} catch (e) {
err = e;
retries++;
if (e.message.includes("after last accepted block")) {
const acceptedBlock = parseInt(e.message.split("after last accepted block ")[1]);
toBlock = acceptedBlock;
}
await sleep(this.retryOn);
}
}
throw err;
}
createBatchRequest(batchArray) {
return batchArray.map(async (event, index) => {
await sleep(10 * index);
return this.getPastEvents(event);
});
}
async getBatchEvents({ fromBlock, toBlock, type = "*" }) {
if (!toBlock) {
toBlock = await this.provider.getBlockNumber();
}
const eventsToSync = [];
for (let i = fromBlock; i < toBlock; i += this.blocksPerRequest) {
const j = i + this.blocksPerRequest - 1 > toBlock ? toBlock : i + this.blocksPerRequest - 1;
eventsToSync.push({ fromBlock: i, toBlock: j, type });
}
const events = [];
const eventChunk = chunk(eventsToSync, this.concurrencySize);
let chunkCount = 0;
for (const chunk2 of eventChunk) {
chunkCount++;
const fetchedEvents = (await Promise.all(this.createBatchRequest(chunk2))).flat();
events.push(...fetchedEvents);
if (typeof this.onProgress === "function") {
this.onProgress({
percentage: chunkCount / eventChunk.length,
type,
fromBlock: chunk2[0].fromBlock,
toBlock: chunk2[chunk2.length - 1].toBlock,
count: fetchedEvents.length
});
}
}
return events;
}
}
const defaultUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0";
function getHttpAgent({
fetchUrl,
proxyUrl,
torPort,
retry
}) {
const { HttpProxyAgent } = require("http-proxy-agent");
const { HttpsProxyAgent } = require("https-proxy-agent");
const { SocksProxyAgent } = require("socks-proxy-agent");
if (torPort) {
return new SocksProxyAgent(`socks5h://tor${retry}@127.0.0.1:${torPort}`);
}
if (!proxyUrl) {
return;
}
const isHttps = fetchUrl.includes("https://");
if (proxyUrl.includes("socks://") || proxyUrl.includes("socks4://") || proxyUrl.includes("socks5://")) {
return new SocksProxyAgent(proxyUrl);
}
if (proxyUrl.includes("http://") || proxyUrl.includes("https://")) {
if (isHttps) {
return new HttpsProxyAgent(proxyUrl);
}
return new HttpProxyAgent(proxyUrl);
}
}
async function fetchData(url, options = {}) {
const MAX_RETRY = options.maxRetry ?? 3;
const RETRY_ON = options.retryOn ?? 500;
const userAgent = options.userAgent ?? defaultUserAgent;
const fetch = globalThis.useGlobalFetch ? globalThis.fetch : crossFetch;
let retry = 0;
let errorObject;
if (!options.method) {
if (!options.body) {
options.method = "GET";
} else {
options.method = "POST";
}
}
if (!options.headers) {
options.headers = {};
}
if (isNode && !options.headers["User-Agent"]) {
options.headers["User-Agent"] = userAgent;
}
while (retry < MAX_RETRY + 1) {
let timeout;
if (!options.signal && options.timeout) {
const controller = new AbortController();
options.signal = controller.signal;
timeout = setTimeout(() => {
controller.abort();
}, options.timeout);
if (options.cancelSignal) {
if (options.cancelSignal.cancelled) {
throw new Error("request cancelled before sending");
}
options.cancelSignal.addListener(() => {
controller.abort();
});
}
}
if (!options.agent && isNode && (options.proxy || options.torPort)) {
options.agent = getHttpAgent({
fetchUrl: url,
proxyUrl: options.proxy,
torPort: options.torPort,
retry
});
}
if (options.debug && typeof options.debug === "function") {
options.debug("request", {
url,
retry,
errorObject,
options
});
}
try {
const resp = await fetch(url, {
method: options.method,
headers: options.headers,
body: options.body,
redirect: options.redirect,
signal: options.signal,
agent: options.agent
});
if (options.debug && typeof options.debug === "function") {
options.debug("response", resp);
}
if (!resp.ok) {
const errMsg = `Request to ${url} failed with error code ${resp.status}:
` + await resp.text();
throw new Error(errMsg);
}
if (options.returnResponse) {
return resp;
}
const contentType = resp.headers.get("content-type");
if (contentType?.includes("application/json")) {
return await resp.json();
}
if (contentType?.includes("text")) {
return await resp.text();
}
return resp;
} catch (error) {
if (timeout) {
clearTimeout(timeout);
}
errorObject = error;
retry++;
await sleep(RETRY_ON);
} finally {
if (timeout) {
clearTimeout(timeout);
}
}
}
if (options.debug && typeof options.debug === "function") {
options.debug("error", errorObject);
}
throw errorObject;
}
const fetchGetUrlFunc = (options = {}) => async (req, _signal) => {
const init = {
...options,
method: req.method || "POST",
headers: req.headers,
body: req.body || void 0,
timeout: options.timeout || req.timeout,
cancelSignal: _signal,
returnResponse: true
};
const resp = await fetchData(req.url, init);
const headers = {};
resp.headers.forEach((value, key) => {
headers[key.toLowerCase()] = value;
});
const respBody = await resp.arrayBuffer();
const body = respBody == null ? null : new Uint8Array(respBody);
return {
statusCode: resp.status,
statusMessage: resp.statusText,
headers,
body
};
};
async function getProvider(rpcUrl, fetchOptions) {
const fetchReq = new ethers.FetchRequest(rpcUrl);
fetchReq.getUrlFunc = fetchGetUrlFunc(fetchOptions);
const staticNetwork = await new ethers.JsonRpcProvider(fetchReq).getNetwork();
const chainId = Number(staticNetwork.chainId);
if (fetchOptions?.netId && fetchOptions.netId !== chainId) {
const errMsg = `Wrong network for ${rpcUrl}, wants ${fetchOptions.netId} got ${chainId}`;
throw new Error(errMsg);
}
return new ethers.JsonRpcProvider(fetchReq, staticNetwork, {
staticNetwork,
pollingInterval: fetchOptions?.pollingInterval || 1e3
});
}
function getProviderWithNetId(netId, rpcUrl, config, fetchOptions) {
const { networkName, reverseRecordsContract, pollInterval } = config;
const hasEns = Boolean(reverseRecordsContract);
const fetchReq = new ethers.FetchRequest(rpcUrl);
fetchReq.getUrlFunc = fetchGetUrlFunc(fetchOptions);
const staticNetwork = new ethers.Network(networkName, netId);
if (hasEns) {
staticNetwork.attachPlugin(new ethers.EnsPlugin(null, Number(netId)));
}
staticNetwork.attachPlugin(new ethers.GasCostPlugin());
const provider = new ethers.JsonRpcProvider(fetchReq, staticNetwork, {
staticNetwork,
pollingInterval: fetchOptions?.pollingInterval || pollInterval * 1e3
});
return provider;
}
const populateTransaction = async (signer, tx) => {
const provider = signer.provider;
if (!tx.from) {
tx.from = signer.address;
} else if (tx.from !== signer.address) {
const errMsg = `populateTransaction: signer mismatch for tx, wants ${tx.from} have ${signer.address}`;
throw new Error(errMsg);
}
const [feeData, nonce] = await Promise.all([
tx.maxFeePerGas || tx.gasPrice ? void 0 : provider.getFeeData(),
tx.nonce ? void 0 : provider.getTransactionCount(signer.address, "pending")
]);
if (feeData) {
if (feeData.maxFeePerGas) {
if (!tx.type) {
tx.type = 2;
}
tx.maxFeePerGas = feeData.maxFeePerGas * (BigInt(1e4) + BigInt(signer.gasPriceBump)) / BigInt(1e4);
tx.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas;
delete tx.gasPrice;
} else if (feeData.gasPrice) {
if (!tx.type) {
tx.type = 0;
}
tx.gasPrice = feeData.gasPrice;
delete tx.maxFeePerGas;
delete tx.maxPriorityFeePerGas;
}
}
if (nonce) {
tx.nonce = nonce;
}
if (!tx.gasLimit) {
try {
const gasLimit = await provider.estimateGas(tx);
tx.gasLimit = gasLimit === BigInt(21e3) ? gasLimit : gasLimit * (BigInt(1e4) + BigInt(signer.gasLimitBump)) / BigInt(1e4);
} catch (error) {
if (signer.gasFailover) {
console.log("populateTransaction: warning gas estimation failed falling back to 3M gas");
tx.gasLimit = BigInt("3000000");
} else {
throw error;
}
}
}
return tx;
};
class TornadoWallet extends ethers.Wallet {
nonce;
gasPriceBump;
gasLimitBump;
gasFailover;
bumpNonce;
constructor(key, provider, { gasPriceBump, gasLimitBump, gasFailover, bumpNonce } = {}) {
super(key, provider);
this.gasPriceBump = gasPriceBump ?? 0;
this.gasLimitBump = gasLimitBump ?? 3e3;
this.gasFailover = gasFailover ?? false;
this.bumpNonce = bumpNonce ?? false;
}
static fromMnemonic(mneomnic, provider, index = 0, options) {
const defaultPath = `m/44'/60'/0'/0/${index}`;
const { privateKey } = ethers.HDNodeWallet.fromPhrase(mneomnic, void 0, defaultPath);
return new TornadoWallet(privateKey, provider, options);
}
async populateTransaction(tx) {
const txObject = await populateTransaction(this, tx);
this.nonce = Number(txObject.nonce);
return super.populateTransaction(txObject);
}
}
class TornadoVoidSigner extends ethers.VoidSigner {
nonce;
gasPriceBump;
gasLimitBump;
gasFailover;
bumpNonce;
constructor(address, provider, { gasPriceBump, gasLimitBump, gasFailover, bumpNonce } = {}) {
super(address, provider);
this.gasPriceBump = gasPriceBump ?? 0;
this.gasLimitBump = gasLimitBump ?? 3e3;
this.gasFailover = gasFailover ?? false;
this.bumpNonce = bumpNonce ?? false;
}
async populateTransaction(tx) {
const txObject = await populateTransaction(this, tx);
this.nonce = Number(txObject.nonce);
return super.populateTransaction(txObject);
}
}
class TornadoRpcSigner extends ethers.JsonRpcSigner {
nonce;
gasPriceBump;
gasLimitBump;
gasFailover;
bumpNonce;
constructor(provider, address, { gasPriceBump, gasLimitBump, gasFailover, bumpNonce } = {}) {
super(provider, address);
this.gasPriceBump = gasPriceBump ?? 0;
this.gasLimitBump = gasLimitBump ?? 3e3;
this.gasFailover = gasFailover ?? false;
this.bumpNonce = bumpNonce ?? false;
}
async sendUncheckedTransaction(tx) {
return super.sendUncheckedTransaction(await populateTransaction(this, tx));
}
}
class TornadoBrowserProvider extends ethers.BrowserProvider {
options;
constructor(ethereum, network, options) {
super(ethereum, network);
this.options = options;
}
async getSigner(address) {
const signerAddress = (await super.getSigner(address)).address;
if (this.options?.netId && this.options?.connectWallet && Number(await super.send("net_version", [])) !== this.options?.netId) {
await this.options.connectWallet(this.options?.netId);
}
if (this.options?.handleNetworkChanges) {
window?.ethereum?.on("chainChanged", this.options.handleNetworkChanges);
}
if (this.options?.handleAccountChanges) {
window?.ethereum?.on("accountsChanged", this.options.handleAccountChanges);
}
if (this.options?.handleAccountDisconnect) {
window?.ethereum?.on("disconnect", this.options.handleAccountDisconnect);
}
return new TornadoRpcSigner(this, signerAddress, this.options);
}
}
var NetId = /* @__PURE__ */ ((NetId2) => {
NetId2[NetId2["MAINNET"] = 1] = "MAINNET";
NetId2[NetId2["BSC"] = 56] = "BSC";
NetId2[NetId2["POLYGON"] = 137] = "POLYGON";
NetId2[NetId2["OPTIMISM"] = 10] = "OPTIMISM";
NetId2[NetId2["ARBITRUM"] = 42161] = "ARBITRUM";
NetId2[NetId2["GNOSIS"] = 100] = "GNOSIS";
NetId2[NetId2["AVALANCHE"] = 43114] = "AVALANCHE";
NetId2[NetId2["SEPOLIA"] = 11155111] = "SEPOLIA";
return NetId2;
})(NetId || {});
const defaultConfig = {
[1 /* MAINNET */]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 80,
fast: 50,
standard: 25,
low: 8
},
nativeCurrency: "eth",
currencyName: "ETH",
explorerUrl: "https://etherscan.io",
merkleTreeHeight: 20,
emptyElement: "21663839004416932945382355908790599225266501822907911457504978515578255421292",
networkName: "Ethereum Mainnet",
deployedBlock: 9116966,
rpcUrls: {
mevblockerRPC: {
name: "MEV Blocker",
url: "https://rpc.mevblocker.io"
},
keydonix: {
name: "Horswap ( Keydonix )",
url: "https://ethereum.keydonix.com/v1/mainnet"
},
SecureRpc: {
name: "SecureRpc",
url: "https://api.securerpc.com/v1"
},
stackup: {
name: "Stackup",
url: "https://public.stackup.sh/api/v1/node/ethereum-mainnet"
},
oneRpc: {
name: "1RPC",
url: "https://1rpc.io/eth"
}
},
stablecoin: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
multicallContract: "0xcA11bde05977b3631167028862bE2a173976CA11",
routerContract: "0xd90e2f925DA726b50C4Ed8D0Fb90Ad053324F31b",
echoContract: "0x9B27DD5Bb15d42DC224FCD0B7caEbBe16161Df42",
offchainOracleContract: "0x00000000000D6FFc74A8feb35aF5827bf57f6786",
tornContract: "0x77777FeDdddFfC19Ff86DB637967013e6C6A116C",
governanceContract: "0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce",
stakingRewardsContract: "0x5B3f656C80E8ddb9ec01Dd9018815576E9238c29",
registryContract: "0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2",
aggregatorContract: "0xE8F47A78A6D52D317D0D2FFFac56739fE14D1b49",
reverseRecordsContract: "0x3671aE578E63FdF66ad4F3E12CC0c0d71Ac7510C",
tornadoSubgraph: "tornadocash/mainnet-tornado-subgraph",
registrySubgraph: "tornadocash/tornado-relayer-registry",
governanceSubgraph: "tornadocash/tornado-governance",
subgraphs: {},
tokens: {
eth: {
instanceAddress: {
"0.1": "0x12D66f87A04A9E220743712cE6d9bB1B5616B8Fc",
"1": "0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936",
"10": "0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF",
"100": "0xA160cdAB225685dA1d56aa342Ad8841c3b53f291"
},
symbol: "ETH",
decimals: 18
},
dai: {
instanceAddress: {
"100": "0xD4B88Df4D29F5CedD6857912842cff3b20C8Cfa3",
"1000": "0xFD8610d20aA15b7B2E3Be39B396a1bC3516c7144",
"10000": "0x07687e702b410Fa43f4cB4Af7FA097918ffD2730",
"100000": "0x23773E65ed146A459791799d01336DB287f25334"
},
tokenAddress: "0x6B175474E89094C44Da98b954EedeAC495271d0F",
tokenGasLimit: 7e4,
symbol: "DAI",
decimals: 18,
gasLimit: 7e5
},
cdai: {
instanceAddress: {
"5000": "0x22aaA7720ddd5388A3c0A3333430953C68f1849b",
"50000": "0x03893a7c7463AE47D46bc7f091665f1893656003",
"500000": "0x2717c5e28cf931547B621a5dddb772Ab6A35B701",
"5000000": "0xD21be7248e0197Ee08E0c20D4a96DEBdaC3D20Af"
},
tokenAddress: "0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643",
tokenGasLimit: 2e5,
symbol: "cDAI",
decimals: 8,
gasLimit: 7e5
},
usdc: {
instanceAddress: {
"100": "0xd96f2B1c14Db8458374d9Aca76E26c3D18364307",
"1000": "0x4736dCf1b7A3d580672CcE6E7c65cd5cc9cFBa9D"
},
tokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
tokenGasLimit: 7e4,
symbol: "USDC",
decimals: 6,
gasLimit: 7e5
},
usdt: {
instanceAddress: {
"100": "0x169AD27A470D064DEDE56a2D3ff727986b15D52B",
"1000": "0x0836222F2B2B24A3F36f98668Ed8F0B38D1a872f"
},
tokenAddress: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
tokenGasLimit: 7e4,
symbol: "USDT",
decimals: 6,
gasLimit: 7e5
},
wbtc: {
instanceAddress: {
"0.1": "0x178169B423a011fff22B9e3F3abeA13414dDD0F1",
"1": "0x610B717796ad172B316836AC95a2ffad065CeaB4",
"10": "0xbB93e510BbCD0B7beb5A853875f9eC60275CF498"
},
tokenAddress: "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
tokenGasLimit: 7e4,
symbol: "WBTC",
decimals: 8,
gasLimit: 7e5
}
},
// Inactive tokens to filter from schema verification and syncing events
disabledTokens: ["cdai", "usdt", "usdc"],
relayerEnsSubdomain: "mainnet-tornado",
pollInterval: 15,
constants: {
GOVERNANCE_BLOCK: 11474695,
NOTE_ACCOUNT_BLOCK: 11842486,
ENCRYPTED_NOTES_BLOCK: 12143762,
REGISTRY_BLOCK: 14173129,
MINING_BLOCK_TIME: 15
}
},
[56 /* BSC */]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 5,
fast: 5,
standard: 5,
low: 5
},
nativeCurrency: "bnb",
currencyName: "BNB",
explorerUrl: "https://bscscan.com",
merkleTreeHeight: 20,
emptyElement: "21663839004416932945382355908790599225266501822907911457504978515578255421292",
networkName: "Binance Smart Chain",
deployedBlock: 8158799,
stablecoin: "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d",
multicallContract: "0xcA11bde05977b3631167028862bE2a173976CA11",
routerContract: "0x0D5550d52428E7e3175bfc9550207e4ad3859b17",
echoContract: "0xa75BF2815618872f155b7C4B0C81bF990f5245E4",
offchainOracleContract: "0x00000000000D6FFc74A8feb35aF5827bf57f6786",
tornadoSubgraph: "tornadocash/bsc-tornado-subgraph",
subgraphs: {},
rpcUrls: {
bnbchain: {
name: "BNB Chain",
url: "https://bsc-dataseed.bnbchain.org"
},
ninicoin: {
name: "BNB Chain 2",
url: "https://bsc-dataseed1.ninicoin.io"
},
nodereal: {
name: "NodeReal",
url: "https://binance.nodereal.io"
},
stackup: {
name: "Stackup",
url: "https://public.stackup.sh/api/v1/node/bsc-mainnet"
},
oneRpc: {
name: "1RPC",
url: "https://1rpc.io/bnb"
}
},
tokens: {
bnb: {
instanceAddress: {
"0.1": "0x84443CFd09A48AF6eF360C6976C5392aC5023a1F",
"1": "0xd47438C816c9E7f2E2888E060936a499Af9582b3",
"10": "0x330bdFADE01eE9bF63C209Ee33102DD334618e0a",
"100": "0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD"
},
symbol: "BNB",
decimals: 18
}
},
relayerEnsSubdomain: "bsc-tornado",
pollInterval: 10,
constants: {
NOTE_ACCOUNT_BLOCK: 8159269,
ENCRYPTED_NOTES_BLOCK: 8159269
}
},
[137 /* POLYGON */]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 100,
fast: 75,
standard: 50,
low: 30
},
nativeCurrency: "matic",
currencyName: "MATIC",
explorerUrl: "https://polygonscan.com",
merkleTreeHeight: 20,
emptyElement: "21663839004416932945382355908790599225266501822907911457504978515578255421292",
networkName: "Polygon (Matic) Network",
deployedBlock: 16257962,
stablecoin: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
multicallContract: "0xcA11bde05977b3631167028862bE2a173976CA11",
routerContract: "0x0D5550d52428E7e3175bfc9550207e4ad3859b17",
echoContract: "0xa75BF2815618872f155b7C4B0C81bF990f5245E4",
offchainOracleContract: "0x00000000000D6FFc74A8feb35aF5827bf57f6786",
tornadoSubgraph: "tornadocash/matic-tornado-subgraph",
subgraphs: {},
rpcUrls: {
oneRpc: {
name: "1RPC",
url: "https://1rpc.io/matic"
},
stackup: {
name: "Stackup",
url: "https://public.stackup.sh/api/v1/node/polygon-mainnet"
}
},
tokens: {
matic: {
instanceAddress: {
"100": "0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD",
"1000": "0xdf231d99Ff8b6c6CBF4E9B9a945CBAcEF9339178",
"10000": "0xaf4c0B70B2Ea9FB7487C7CbB37aDa259579fe040",
"100000": "0xa5C2254e4253490C54cef0a4347fddb8f75A4998"
},
symbol: "MATIC",
decimals: 18
}
},
relayerEnsSubdomain: "polygon-tornado",
pollInterval: 10,
constants: {
NOTE_ACCOUNT_BLOCK: 16257996,
ENCRYPTED_NOTES_BLOCK: 16257996
}
},
[10 /* OPTIMISM */]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 1e-3,
fast: 1e-3,
standard: 1e-3,
low: 1e-3
},
nativeCurrency: "eth",
currencyName: "ETH",
explorerUrl: "https://optimistic.etherscan.io",
merkleTreeHeight: 20,
emptyElement: "21663839004416932945382355908790599225266501822907911457504978515578255421292",
networkName: "Optimism",
deployedBlock: 2243689,
stablecoin: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
multicallContract: "0xcA11bde05977b3631167028862bE2a173976CA11",
routerContract: "0x0D5550d52428E7e3175bfc9550207e4ad3859b17",
echoContract: "0xa75BF2815618872f155b7C4B0C81bF990f5245E4",
offchainOracleContract: "0x00000000000D6FFc74A8feb35aF5827bf57f6786",
ovmGasPriceOracleContract: "0x420000000000000000000000000000000000000F",
tornadoSubgraph: "tornadocash/optimism-tornado-subgraph",
subgraphs: {},
rpcUrls: {
oneRpc: {
name: "1RPC",
url: "https://1rpc.io/op"
},
stackup: {
name: "Stackup",
url: "https://public.stackup.sh/api/v1/node/optimism-mainnet"
}
},
tokens: {
eth: {
instanceAddress: {
"0.1": "0x84443CFd09A48AF6eF360C6976C5392aC5023a1F",
"1": "0xd47438C816c9E7f2E2888E060936a499Af9582b3",
"10": "0x330bdFADE01eE9bF63C209Ee33102DD334618e0a",
"100": "0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD"
},
symbol: "ETH",
decimals: 18
}
},
relayerEnsSubdomain: "optimism-tornado",
pollInterval: 15,
constants: {
NOTE_ACCOUNT_BLOCK: 2243694,
ENCRYPTED_NOTES_BLOCK: 2243694
}
},
[42161 /* ARBITRUM */]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 4,
fast: 3,
standard: 2.52,
low: 2.29
},
nativeCurrency: "eth",
currencyName: "ETH",
explorerUrl: "https://arbiscan.io",
merkleTreeHeight: 20,
emptyElement: "21663839004416932945382355908790599225266501822907911457504978515578255421292",
networkName: "Arbitrum One",
deployedBlock: 3430648,
stablecoin: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
multicallContract: "0xcA11bde05977b3631167028862bE2a173976CA11",
routerContract: "0x0D5550d52428E7e3175bfc9550207e4ad3859b17",
echoContract: "0xa75BF2815618872f155b7C4B0C81bF990f5245E4",
offchainOracleContract: "0x00000000000D6FFc74A8feb35aF5827bf57f6786",
tornadoSubgraph: "tornadocash/arbitrum-tornado-subgraph",
subgraphs: {},
rpcUrls: {
Arbitrum: {
name: "Arbitrum",
url: "https://arb1.arbitrum.io/rpc"
},
stackup: {
name: "Stackup",
url: "https://public.stackup.sh/api/v1/node/arbitrum-one"
},
oneRpc: {
name: "1RPC",
url: "https://1rpc.io/arb"
}
},
tokens: {
eth: {
instanceAddress: {
"0.1": "0x84443CFd09A48AF6eF360C6976C5392aC5023a1F",
"1": "0xd47438C816c9E7f2E2888E060936a499Af9582b3",
"10": "0x330bdFADE01eE9bF63C209Ee33102DD334618e0a",
"100": "0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD"
},
symbol: "ETH",
decimals: 18
}
},
relayerEnsSubdomain: "arbitrum-tornado",
pollInterval: 15,
constants: {
NOTE_ACCOUNT_BLOCK: 3430605,
ENCRYPTED_NOTES_BLOCK: 3430605
}
},
[100 /* GNOSIS */]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 6,
fast: 5,
standard: 4,
low: 1
},
nativeCurrency: "xdai",
currencyName: "xDAI",
explorerUrl: "https://gnosisscan.io",
merkleTreeHeight: 20,
emptyElement: "21663839004416932945382355908790599225266501822907911457504978515578255421292",
networkName: "Gnosis Chain",
deployedBlock: 17754561,
stablecoin: "0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83",
multicallContract: "0xcA11bde05977b3631167028862bE2a173976CA11",
routerContract: "0x0D5550d52428E7e3175bfc9550207e4ad3859b17",
echoContract: "0xa75BF2815618872f155b7C4B0C81bF990f5245E4",
offchainOracleContract: "0x00000000000D6FFc74A8feb35aF5827bf57f6786",
tornadoSubgraph: "tornadocash/xdai-tornado-subgraph",
subgraphs: {},
rpcUrls: {
gnosis: {
name: "Gnosis",
url: "https://rpc.gnosischain.com"
},
oneRpc: {
name: "1RPC",
url: "https://1rpc.io/gnosis"
}
},
tokens: {
xdai: {
instanceAddress: {
"100": "0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD",
"1000": "0xdf231d99Ff8b6c6CBF4E9B9a945CBAcEF9339178",
"10000": "0xaf4c0B70B2Ea9FB7487C7CbB37aDa259579fe040",
"100000": "0xa5C2254e4253490C54cef0a4347fddb8f75A4998"
},
symbol: "xDAI",
decimals: 18
}
},
relayerEnsSubdomain: "gnosis-tornado",
pollInterval: 15,
constants: {
NOTE_ACCOUNT_BLOCK: 17754564,
ENCRYPTED_NOTES_BLOCK: 17754564
}
},
[43114 /* AVALANCHE */]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 225,
fast: 35,
standard: 25,
low: 25
},
nativeCurrency: "avax",
currencyName: "AVAX",
explorerUrl: "https://snowtrace.io",
merkleTreeHeight: 20,
emptyElement: "21663839004416932945382355908790599225266501822907911457504978515578255421292",
networkName: "Avalanche Mainnet",
deployedBlock: 4429818,
stablecoin: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
multicallContract: "0xcA11bde05977b3631167028862bE2a173976CA11",
routerContract: "0x0D5550d52428E7e3175bfc9550207e4ad3859b17",
echoContract: "0xa75BF2815618872f155b7C4B0C81bF990f5245E4",
offchainOracleContract: "0x00000000000D6FFc74A8feb35aF5827bf57f6786",
tornadoSubgraph: "tornadocash/avalanche-tornado-subgraph",
subgraphs: {},
rpcUrls: {
oneRpc: {
name: "1RPC",
url: "https://1rpc.io/avax/c"
},
stackup: {
name: "Stackup",
url: "https://public.stackup.sh/api/v1/node/avalanche-mainnet"
}
},
tokens: {
avax: {
instanceAddress: {
"10": "0x330bdFADE01eE9bF63C209Ee33102DD334618e0a",
"100": "0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD",
"500": "0xaf8d1839c3c67cf571aa74B5c12398d4901147B3"
},
symbol: "AVAX",
decimals: 18
}
},
relayerEnsSubdomain: "avalanche-tornado",
pollInterval: 10,
constants: {
NOTE_ACCOUNT_BLOCK: 4429813,
ENCRYPTED_NOTES_BLOCK: 4429813
}
},
[11155111 /* SEPOLIA */]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 2,
fast: 2,
standard: 2,
low: 2
},
nativeCurrency: "eth",
currencyName: "SepoliaETH",
explorerUrl: "https://sepolia.etherscan.io",
merkleTreeHeight: 20,
emptyElement: "21663839004416932945382355908790599225266501822907911457504978515578255421292",
networkName: "Ethereum Sepolia",
deployedBlock: 5594395,
stablecoin: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
multicallContract: "0xcA11bde05977b3631167028862bE2a173976CA11",
routerContract: "0x1572AFE6949fdF51Cb3E0856216670ae9Ee160Ee",
echoContract: "0xcDD1fc3F5ac2782D83449d3AbE80D6b7B273B0e5",
offchainOracleContract: "0x1f89EAF03E5b260Bc6D4Ae3c3334b1B750F3e127",
tornContract: "0x3AE6667167C0f44394106E197904519D808323cA",
governanceContract: "0xe5324cD7602eeb387418e594B87aCADee08aeCAD",
stakingRewardsContract: "0x6d0018890751Efd31feb8166711B16732E2b496b",
registryContract: "0x1428e5d2356b13778A13108b10c440C83011dfB8",
aggregatorContract: "0x4088712AC9fad39ea133cdb9130E465d235e9642",
reverseRecordsContract: "0xEc29700C0283e5Be64AcdFe8077d6cC95dE23C23",
tornadoSubgraph: "tornadocash/sepolia-tornado-subgraph",
subgraphs: {},
rpcUrls: {
sepolia: {
name: "Sepolia RPC",
url: "https://rpc.sepolia.org"
},
stackup: {
name: "Stackup",
url: "https://public.stackup.sh/api/v1/node/ethereum-sepolia"
},
oneRpc: {
name: "1RPC",
url: "https://1rpc.io/sepolia"
},
ethpandaops: {
name: "ethpandaops",
url: "https://rpc.sepolia.ethpandaops.io"
}
},
tokens: {
eth: {
instanceAddress: {
"0.1": "0x8C4A04d872a6C1BE37964A21ba3a138525dFF50b",
"1": "0x8cc930096B4Df705A007c4A039BDFA1320Ed2508",
"10": "0x8D10d506D29Fc62ABb8A290B99F66dB27Fc43585",
"100": "0x44c5C92ed73dB43888210264f0C8b36Fd68D8379"
},
symbol: "ETH",
decimals: 18
},
dai: {
instanceAddress: {
"100": "0x6921fd1a97441dd603a997ED6DDF388658daf754",
"1000": "0x50a637770F5d161999420F7d70d888DE47207145",
"10000": "0xecD649870407cD43923A816Cc6334a5bdf113621",
"100000": "0x73B4BD04bF83206B6e979BE2507098F92EDf4F90"
},
tokenAddress: "0xFF34B3d4Aee8ddCd6F9AFFFB6Fe49bD371b8a357",
tokenGasLimit: 7e4,
symbol: "DAI",
decimals: 18,
gasLimit: 7e5
}
},
relayerEnsSubdomain: "sepolia-tornado",
pollInterval: 15,
constants: {
GOVERNANCE_BLOCK: 5594395,
NOTE_ACCOUNT_BLOCK: 5594395,
ENCRYPTED_NOTES_BLOCK: 5594395,
REGISTRY_BLOCK: 5594395,
MINING_BLOCK_TIME: 15
}
}
};
const enabledChains = Object.values(NetId).filter((n) => typeof n === "number");
exports.customConfig = {};
function addNetwork(newConfig) {
enabledChains.push(
...Object.keys(newConfig).map((netId) => Number(netId)).filter((netId) => !enabledChains.includes(netId))
);
exports.customConfig = {
...exports.customConfig,
...newConfig
};
}
function getNetworkConfig() {
const allConfig = {
...defaultConfig,
...exports.customConfig
};
return enabledChains.reduce((acc, curr) => {
acc[curr] = allConfig[curr];
return acc;
}, {});
}
function getConfig(netId) {
const allConfig = getNetworkConfig();
const chainConfig = allConfig[netId];
if (!chainConfig) {
const errMsg = `No config found for network ${netId}!`;
throw new Error(errMsg);
}
return chainConfig;
}
function getActiveTokens(config) {
const { tokens, disabledTokens } = config;
return Object.keys(tokens).filter((t) => !disabledTokens?.includes(t));
}
function getActiveTokenInstances(config) {
const { tokens, disabledTokens } = config;
return Object.entries(tokens).reduce((acc, [token, instances]) => {
if (!disabledTokens?.includes(token)) {
acc[token] = instances;
}
return acc;
}, {});
}
function getInstanceByAddress(config, address) {
const { tokens, disabledTokens } = config;
for (const [currency, { instanceAddress }] of Object.entries(tokens)) {
if (disabledTokens?.includes(currency)) {
continue;
}
for (const [amount, instance] of Object.entries(instanceAddress)) {
if (instance === address) {
return {
amount,
currency
};
}
}
}
}
function getRelayerEnsSubdomains() {
const allConfig = getNetworkConfig();
return enabledChains.reduce((acc, chain) => {
acc[chain] = allConfig[chain].relayerEnsSubdomain;
return acc;
}, {});
}
const ajv = new Ajv({ allErrors: true });
ajv.addKeyword({
keyword: "BN",
// eslint-disable-next-line @typescript-eslint/no-explicit-any
validate: (schema, data) => {
try {
BigInt(data);
return true;
} catch {
return false;
}
},
errors: true
});
ajv.addKeyword({
keyword: "isAddress",
// eslint-disable-next-line @typescript-eslint/no-explicit-any
validate: (schema, data) => {
try {
return ethers.isAddress(data);
} catch {
return false;
}
},
errors: true
});
const addressSchemaType = {
type: "string",
pattern: "^0x[a-fA-F0-9]{40}$",
isAddress: true
};
const bnSchemaType = { type: "string", BN: true };
const proofSchemaType = {
type: "string",
pattern: "^0x[a-fA-F0-9]{512}$"
};
const bytes32SchemaType = {
type: "string",
pattern: "^0x[a-fA-F0-9]{64}$"
};
const bytes32BNSchemaType = { ...bytes32SchemaType, BN: true };
const baseEventsSchemaProperty = {
blockNumber: {
type: "number"
},
logIndex: {
type: "number"
},
transactionHash: bytes32SchemaType
};
const baseEventsSchemaRequired = Object.keys(baseEventsSchemaProperty);
const governanceEventsSchema = {
type: "array",
items: {
anyOf: [
{
type: "object",
properties: {
...baseEventsSchemaProperty,
event: { type: "string" },
id: { type: "number" },
proposer: addressSchemaType,
target: addressSchemaType,
startTime: { type: "number" },
endTime: { type: "number" },
description: { type: "string" }
},
required: [
...baseEventsSchemaRequired,
"event",
"id",
"proposer",
"target",
"startTime",
"endTime",
"description"
],
additionalProperties: false
},
{
type: "object",
properties: {
...baseEventsSchemaProperty,
event: { type: "string" },
proposalId: { type: "number" },
voter: addressSchemaType,
support: { type: "boolean" },
votes: { type: "string" },
from: addressSchemaType,
input: { type: "string" }
},
required: [
...baseEventsSchemaRequired,
"event",
"proposalId",
"voter",
"support",
"votes",
"from",
"input"
],
additionalProperties: false
},
{
type: "object",
properties: {
...baseEventsSchemaProperty,
event: { type: "string" },
account: addressSchemaType,
delegateTo: addressSchemaType
},
required: [...baseEventsSchemaRequired, "account", "delegateTo"],
additionalProperties: false
},
{
type: "object",
properties: {
...baseEventsSchemaProperty,
event: { type: "string" },
account: addressSchemaType,
delegateFrom: addressSchemaType
},
required: [...baseEventsSchemaRequired, "account", "delegateFrom"],
additionalProperties: false
}
]
}
};
const relayerRegistryEventsSchema = {
type: "array",
items: {
anyOf: [
// RelayerRegisteredEvents
{
type: "object",
properties: {
...baseEventsSchemaProperty,
event: { type: "string" },
ensName: { type: "string" },
relayerAddress: addressSchemaType,
ensHash: { type: "string" },
stakedAmount: { type: "string" }
},
required: [
...baseEventsSchemaRequired,
"event",
"ensName",
"relayerAddress",
"ensHash",
"stakedAmount"
],
additionalProperties: false
},
// RelayerUnregisteredEvents
{
type: "object",
properties: {
...baseEventsSchemaProperty,
event: { type: "string" },
relayerAddress: addressSchemaType
},
required: [...baseEventsSchemaRequired, "event", "relayerAddress"],
additionalProperties: false
},
// WorkerRegisteredEvents & WorkerUnregisteredEvents
{
type: "object",
properties: {
...baseEventsSchemaProperty,
event: { type: "string" },
relayerAddress: addressSchemaType,
workerAddress: addressSchemaType
},
required: [...baseEventsSchemaRequired, "event", "relayerAddress", "workerAddress"],
additionalProperties: false
}
]
}
};
const stakeBurnedEventsSchema = {
type: "array",
items: {
type: "object",
properties: {
...baseEventsSchemaProperty,
relayerAddress: addressSchemaType,
amountBurned: bnSchemaType,
instanceAddress: addressSchemaType,
gasFee: bnSchemaType,
relayerFee: bnSchemaType,
timestamp: { type: "number" }
},
required: [
...baseEventsSchemaRequired,
"relayerAddress",
"amountBurned",
"instanceAddress",
"gasFee",
"relayerFee",
"timestamp"
],
additionalProperties: false
}
};
const depositsEventsSchema = {
type: "array",
items: {
type: "object",
properties: {
...baseEventsSchemaProperty,
commitment: bytes32SchemaType,
leafIndex: { type: "number" },
timestamp: { type: "number" },
from: addressSchemaType
},
required: [...baseEventsSchemaRequired, "commitment", "leafIndex", "timestamp", "from"],
additionalProperties: false
}
};
const withdrawalsEventsSchema = {
type: "array",
items: {
type: "object",
properties: {
...baseEventsSchemaProperty,
nullifierHash: bytes32SchemaType,
to: addressSchemaType,
fee: bnSchemaType,
timestamp: { type: "number" }
},
required: [...baseEventsSchemaRequired, "nullifierHash", "to", "fee", "timestamp"],
additionalProperties: false
}
};
const echoEventsSchema = {
type: "array",
items: {
type: "object",
properties: {
...baseEventsSchemaProperty,
address: addressSchemaType,
encryptedAccount: { type: "string" }
},
required: [...baseEventsSchemaRequired, "address", "encryptedAccount"],
additionalProperties: false
}
};
const encryptedNotesSchema = {
type: "array",
items: {
type: "object",
properties: {
...baseEventsSchemaProperty,
encryptedNote: { type: "string" }
},
required: [...baseEventsSchemaRequired, "encryptedNote"],
additionalProperties: false
}
};
function getEventsSchemaValidator(type) {
if (type === "deposit") {
return ajv.compile(depositsEventsSchema);
}
if (type === "withdrawal") {
return ajv.compile(withdrawalsEventsSchema);
}
if (type === "governance") {
return ajv.compile(governanceEventsSchema);
}
if (type === "registry") {
return ajv.compile(relayerRegistryEventsSchema);
}
if (type === "revenue") {
return ajv.compile(stakeBurnedEventsSchema);
}
if (type === "echo") {
return ajv.compile(echoEventsSchema);
}
if (type === "encrypted_notes") {
return ajv.compile(encryptedNotesSchema);
}
throw new Error("Unsupported event type for schema validation");
}
const statusSchema = {
type: "object",
properties: {
rewardAccount: addressSchemaType,
gasPrices: {
type: "object",
properties: {
fast: { type: "number" },
additionalProperties: { type: "number" }
},
required: ["fast"]
},
netId: { type: "integer" },
tornadoServiceFee: { type: "number", maximum: 20, minimum: 0 },
latestBlock: { type: "number" },
latestBalance: bnSchemaType,
version: { type: "string" },
health: {
type: "object",
properties: {
status: { const: "true" },
error: { type: "string" }
},
required: ["status"]
},
syncStatus: {
type: "object",
properties: {
events: { type: "boolean" },
tokenPrice: { type: "boolean" },
gasPrice: { type: "boolean" }
},
required: ["events", "tokenPrice", "gasPrice"]
},
onSyncEvents: { type: "boolean" },
currentQueue: { type: "number" }
},
required: ["rewardAccount", "instances", "netId", "tornadoServiceFee", "version", "health", "currentQueue"]
};
function getStatusSchema(netId, config, tovarish) {
const { tokens, optionalTokens, disabledTokens, nativeCurrency } = config;
const schema = JSON.parse(JSON.stringify(statusSchema));
const instances = Object.keys(tokens).reduce(
(acc, token) => {
const { instanceAddress, tokenAddress, symbol, decimals, optionalInstances = [] } = tokens[token];
const amounts = Object.keys(instanceAddress);
const instanceProperties = {
type: "object",
properties: {
instanceAddress: {
type: "object",
properties: amounts.reduce(
(acc2, cur) => {
acc2[cur] = addressSchemaType;
return acc2;
},
{}
),
required: amounts.filter((amount) => !optionalInstances.includes(amount))
},
decimals: { enum: [decimals] }
},
required: ["instanceAddress", "decimals"].concat(
tokenAddress ? ["tokenAddress"] : [],
symbol ? ["symbol"] : []
)
};
if (tokenAddress) {
instanceProperties.properties.tokenAddress = addressSchemaType;
}
if (symbol) {
instanceProperties.properties.symbol = { enum: [symbol] };
}
acc.properties[token] = instanceProperties;
if (!optionalTokens?.includes(token) && !disabledTokens?.includes(token)) {
acc.required.push(token);
}
return acc;
},
{
type: "object",
properties: {},
required: []
}
);
schema.properties.instances = instances;
const _tokens = Object.keys(tokens).filter(
(t) => t !== nativeCurrency && !config.optionalTokens?.includes(t) && !config.disabledTokens?.includes(t)
);
if (netId === NetId.MAINNET) {
_tokens.push("torn");
}
if (_tokens.length) {
const ethPrices = {
type: "object",
properties: _tokens.reduce((acc, token) => {
acc[token] = bnSchemaType;
return acc;
}, {}),
required: _tokens
};
schema.properties.ethPrices = ethPrices;
schema.required.push("ethPrices");
}
if (tovarish) {
schema.required.push("gasPrices", "latestBlock", "latestBalance", "syncStatus", "onSyncEvents");
}
return schema;
}
const jobsSchema = {
type: "object",
properties: {
error: { type: "string" },
id: { type: "string" },
type: { type: "string" },
status: { type: "string" },
contract: { type: "string" },
proof: { type: "string" },
args: {
type: "array",
items: { type: "string" }
},
txHash: { type: "string" },
confirmations: { type: "number" },
failedReason: { type: "string" }
},
required: ["id", "status"]
};
const jobRequestSchema = {
...jobsSchema,
required: ["id"]
};
const MIN_FEE = 0.1;
const MAX_FEE = 0.9;
const MIN_STAKE_BALANCE = ethers.parseEther("500");
function calculateScore({ stakeBalance, tornadoServiceFee }) {
if (tornadoServiceFee < MIN_FEE) {
tornadoServiceFee = MIN_FEE;
} else if (tornadoServiceFee >= MAX_FEE) {
return BigInt(0);
}
const serviceFeeCoefficient = (tornadoServiceFee - MIN_FEE) ** 2;
const feeDiffCoefficient = 1 / (MAX_FEE - MIN_FEE) ** 2;
const coefficientsMultiplier = 1 - feeDiffCoefficient * serviceFeeCoefficient;
return BigInt(Math.floor(Number(stakeBalance || "0") * coefficientsMultiplier));
}
function getWeightRandom(weightsScores, random) {
for (let i = 0; i < weightsScores.length; i++) {
if (random < weightsScores[i]) {
return i;
}
random = random - weightsScores[i];
}
return Math.floor(Math.random() * weightsScores.length);
}
function getSupportedInstances(instanceList) {
const rawList = Object.values(instanceList).map(({ instanceAddress }) => {
return Object.values(instanceAddress);
}).flat();
return rawList.map((l) => ethers.getAddress(l));
}
function pickWeightedRandomRelayer(relayers) {
const weightsScores = relayers.map((el) => calculateScore(el));
const totalWeight = weightsScores.reduce((acc, curr) => {
return acc = acc + curr;
}, BigInt("0"));
const random = BigInt(Math.floor(Number(totalWeight) * Math.random()));
const weightRandomIndex = getWeightRandom(weightsScores, random);
return relayers[weightRandomIndex];
}
class RelayerClient {
netId;
config;
selectedRelayer;
fetchDataOptions;
tovarish;
constructor({ netId, config, fetchDataOptions: fetchDataOptions2 }) {
this.netId = netId;
this.config = config;
this.fetchDataOptions = fetchDataOptions2;
this.tovarish = false;
}
async askRelayerStatus({
hostname,
url,
relayerAddress
}) {
if (!url && hostname) {
url = `https://${!hostname.endsWith("/") ? hostname + "/" : hostname}`;
} else if (url && !url.endsWith("/")) {
url += "/";
} else {
url = "";
}
const rawStatus = await fetchData(`${url}status`, {
...this.fetchDataOptions,
headers: {
"Content-Type": "application/json, application/x-www-form-urlencoded"
},
timeout: 3e4,
maxRetry: this.fetchDataOptions?.torPort ? 2 : 0
});
const statusValidator = ajv.compile(getStatusSchema(this.netId, this.config, this.tovarish));
if (!statusValidator(rawStatus)) {
throw new Error("Invalid status schema");
}
const status = {
...rawStatus,
url
};
if (status.currentQueue > 5) {
throw new Error("Withdrawal queue is overloaded");
}
if (status.netId !== this.netId) {
throw new Error("This relayer serves a different network");
}
if (relayerAddress && this.netId === NetId.MAINNET && status.rewardAccount !== relayerAddress) {
throw new Error("The Relayer reward address must match registered address");
}
return status;
}
async filterRelayer(relayer) {
const hostname = relayer.hostnames[this.netId];
const { ensName, relayerAddress } = relayer;
if (!hostname) {
return;
}
try {
const status = await this.askRelayerStatus({
hostname,
relayerAddress
});
return {
netId: status.netId,
url: status.url,
hostname,
ensName,
relayerAddress,
rewardAccount: ethers.getAddress(status.rewardAccount),
instances: getSupportedInstances(status.instances),
stakeBalance: relayer.stakeBalance,
gasPrice: status.gasPrices?.fast,
ethPrices: status.ethPrices,
currentQueue: status.currentQueue,
tornadoServiceFee: status.tornadoServiceFee
};
} catch (err) {
return {
hostname,
relayerAddress,
errorMessage: err.message,
hasError: true
};
}
}
async getValidRelayers(relayers) {
const invalidRelayers = [];
const validRelayers = (await Promise.all(relayers.map((relayer) => this.filterRelayer(relayer)))).filter(
(r) => {
if (!r) {
return false;
}
if (r.hasError) {
invalidRelayers.push(r);
return false;
}
return true;
}
);
return {
validRelayers,
invalidRelayers
};
}
pickWeightedRandomRelayer(relayers) {
return pickWeightedRandomRelayer(relayers);
}
async tornadoWithdraw({ contract, proof, args }, callback) {
const { url } = this.selectedRelayer;
const withdrawResponse = await fetchData(`${url}v1/tornadoWithdraw`, {
...this.fetchDataOptions,
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
contract,
proof,
args
})
});
const { id, error } = withdrawResponse;
if (error) {
throw new Error(error);
}
const jobValidator = ajv.compile(jobRequestSchema);
if (!jobValidator(withdrawResponse)) {
const errMsg = `${url}v1/tornadoWithdraw has an invalid job response`;
throw new Error(errMsg);
}
if (typeof callback === "function") {
callback(withdrawResponse);
}
let relayerStatus;
const jobUrl = `${url}v1/jobs/${id}`;
console.log(`Job submitted: ${jobUrl}
`);
while (!relayerStatus || !["FAILED", "CONFIRMED"].includes(relayerStatus)) {
const jobResponse = await fetchData(jobUrl, {
...this.fetchDataOptions,
method: "GET",
headers: {
"Content-Type": "application/json"
}
});
if (jobResponse.error) {
throw new Error(error);
}
const jobValidator2 = ajv.compile(jobsSchema);
if (!jobValidator2(jobResponse)) {
const errMsg = `${jobUrl} has an invalid job response`;
throw new Error(errMsg);
}
const { status, txHash, confirmations, failedReason } = jobResponse;
if (relayerStatus !== status) {
if (status === "FAILED") {
const errMsg = `Job ${status}: ${jobUrl} failed reason: ${failedReason}`;
throw new Error(errMsg);
} else if (status === "SENT") {
console.log(`Job ${status}: ${jobUrl}, txhash: ${txHash}
`);
} else if (status === "MINED") {
console.log(`Job ${status}: ${jobUrl}, txhash: ${txHash}, confirmations: ${confirmations}
`);
} else if (status === "CONFIRMED") {
console.log(`Job ${status}: ${jobUrl}, txhash: ${txHash}, confirmations: ${confirmations}
`);
} else {
console.log(`Job ${status}: ${jobUrl}
`);
}
relayerStatus = status;
if (typeof callback === "function") {
callback(jobResponse);
}
}
await sleep(3e3);
}
}
}
class BaseEventsService {
netId;
provider;
contract;
type;
deployedBlock;
batchEventsService;
fetchDataOptions;
tovarishClient;
constructor({
netId,
provider,
contract,
type = "",
deployedBlock = 0,
fetchDataOptions: fetchDataOptions2,
tovarishClient
}) {
this.netId = netId;
this.provider = provider;
this.fetchDataOptions = fetchDataOptions2;
this.contract = contract;
this.type = type;
this.deployedBlock = deployedBlock;
this.batchEventsService = new BatchEventsService({
provider,
contract,
onProgress: this.updateEventProgress
});
this.tovarishClient = tovarishClient;
}
getInstanceName() {
return "";
}
getType() {
return this.type || "";
}
getTovarishType() {
return String(this.getType() || "").toLowerCase();
}
/* eslint-disable @typescript-eslint/no-unused-vars */
updateEventProgress({ percentage, type, fromBlock, toBlock, count }) {
}
updateBlockProgress({ percentage, currentIndex, totalIndex }) {
}
updateTransactionProgress({ percentage, currentIndex, totalIndex }) {
}
/* eslint-enable @typescript-eslint/no-unused-vars */
async formatEvents(events) {
return await new Promise((resolve) => resolve(events));
}
/**
* Get saved or cached events
*/
async getEventsFromDB() {
return {
events: [],
lastBlock: 0
};
}
/**
* Events from remote cache (Either from local cache, CDN, or from IPFS)
*/
async getEventsFromCache() {
return {
events: [],
lastBlock: 0,
fromCache: true
};
}
async getSavedEvents() {
let dbEvents = await this.getEventsFromDB();
if (!dbEvents.lastBlock) {
dbEvents = await this.getEventsFromCache();
}
return dbEvents;
}
async getEventsFromRpc({
fromBlock,
toBlock
}) {
try {
if (!toBlock) {
toBlock = await this.provider.getBlockNumber();
}
if (fromBlock >= toBlock) {
return {
events: [],
lastBlock: toBlock
};
}
this.updateEventProgress({ percentage: 0, type: this.getType() });
const events = await this.formatEvents(
await this.batchEventsService.getBatchEvents({
fromBlock,
toBlock,
type: this.getType()
})
);
return {
events,
lastBlock: toBlock
};
} catch (err) {
console.log(err);
return {
events: [],
lastBlock: fromBlock
};
}
}
async getLatestEvents({ fromBlock }) {
if (this.tovarishClient?.selectedRelayer) {
const { events, lastSyncBlock: lastBlock } = await this.tovarishClient.getEvents({
type: this.getTovarishType(),
fromBlock
});
return {
events,
lastBlock
};
}
return await this.getEventsFromRpc({
fromBlock
});
}
/* eslint-disable @typescript-eslint/no-unused-vars */
async validateEvents({
events,
newEvents,
lastBlock
}) {
return void 0;
}
/* eslint-enable @typescript-eslint/no-unused-vars */
/**
* Handle saving events
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async saveEvents({ events, lastBlock }) {
}
/**
* Trigger saving and receiving latest events
*/
async updateEvents() {
const savedEvents = await this.getSavedEvents();
let fromBlock = this.deployedBlock;
if (savedEvents && savedEvents.lastBlock) {
fromBlock = savedEvents.lastBlock + 1;
}
const newEvents = await this.getLatestEvents({ fromBlock });
const eventSet = /* @__PURE__ */ new Set();
const allEvents = [...savedEvents.events, ...newEvents.events].sort((a, b) => {
if (a.blockNumber === b.blockNumber) {
return a.logIndex - b.logIndex;
}
return a.blockNumber - b.blockNumber;
}).filter(({ transactionHash, logIndex }) => {
const eventKey = `${transactionHash}_${logIndex}`;
const hasEvent = eventSet.has(eventKey);
eventSet.add(eventKey);
return !hasEvent;
});
const lastBlock = newEvents.lastBlock || allEvents[allEvents.length - 1]?.blockNumber;
const validateResult = await this.validateEvents({
events: allEvents,
newEvents: newEvents.events,
lastBlock
});
if (savedEvents.fromCache || newEvents.events.length) {
await this.saveEvents({ events: allEvents, lastBlock });
}
return {
events: allEvents,
lastBlock,
validateResult
};
}
}
class BaseTornadoService extends BaseEventsService {
amount;
currency;
optionalTree;
merkleTreeService;
batchTransactionService;
batchBlockService;
constructor(serviceConstructor) {
const { Tornado: contract, amount, currency, provider, optionalTree, merkleTreeService } = serviceConstructor;
super({
...serviceConstructor,
contract
});
this.amount = amount;
this.currency = currency;
this.optionalTree = optionalTree;
this.merkleTreeService = merkleTreeService;
this.batchTransactionService = new BatchTransactionService({
provider,
onProgress: this.updateTransactionProgress
});
this.batchBlockService = new BatchBlockService({
provider,
onProgress: this.updateBlockProgress
});
}
getInstanceName() {
return `${this.getType().toLowerCase()}s_${this.netId}_${this.currency}_${this.amount}`;
}
async formatEvents(events) {
const type = this.getType();
if (type === "Deposit") {
const txs = await this.batchTransactionService.getBatchTransactions([
...new Set(events.map(({ transactionHash }) => transactionHash))
]);
return events.map(({ blockNumber, index: logIndex, transactionHash, args }) => {
const { commitment, leafIndex, timestamp } = args;
return {
blockNumber,
logIndex,
transactionHash,
commitment,
leafIndex: Number(leafIndex),
timestamp: Number(timestamp),
from: txs.find(({ hash }) => hash === transactionHash)?.from || ""
};
});
} else {
const blocks = await this.batchBlockService.getBatchBlocks([
...new Set(events.map(({ blockNumber }) => blockNumber))
]);
return events.map(({ blockNumber, index: logIndex, transactionHash, args }) => {
const { nullifierHash, to, fee } = args;
return {
blockNumber,
logIndex,
transactionHash,
nullifierHash: String(nullifierHash),
to: ethers.getAddress(to),
fee: String(fee),
timestamp: blocks.find(({ number }) => number === blockNumber)?.timestamp || 0
};
});
}
}
async validateEvents({
events,
newEvents
}) {
if (events.length && this.getType() === "Deposit") {
const depositEvents = events;
const lastEvent = depositEvents[depositEvents.length - 1];
if (lastEvent.leafIndex !== depositEvents.length - 1) {
const errMsg = `Deposit events invalid wants ${depositEvents.length - 1} leafIndex have ${lastEvent.leafIndex}`;
throw new Error(errMsg);
}
if (this.merkleTreeService && (!this.optionalTree || newEvents.length)) {
return await this.merkleTreeService.verifyTree(depositEvents);
}
}
return void 0;
}
async getLatestEvents({
fromBlock
}) {
if (this.tovarishClient?.selectedRelayer) {
const { events, lastSyncBlock: lastBlock } = await this.tovarishClient.getEvents({
type: this.getTovarishType(),
currency: this.currency,
amount: this.amount,
fromBlock
});
return {
events,
lastBlock
};
}
return await this.getEventsFromRpc({
fromBlock
});
}
}
class BaseEchoService extends BaseEventsService {
constructor(serviceConstructor) {
super({
...serviceConstructor,
contract: serviceConstructor.Echoer,
type: "Echo"
});
}
getInstanceName() {
return `echo_${this.netId}`;
}
async formatEvents(events) {
return events.map(({ blockNumber, index: logIndex, transactionHash, args }) => {
const { who, data } = args;
if (who && data) {
const eventObjects = {
blockNumber,
logIndex,
transactionHash
};
return {
...eventObjects,
address: who,
encryptedAccount: data
};
}
}).filter((e) => e);
}
}
class BaseEncryptedNotesService extends BaseEventsService {
constructor(serviceConstructor) {
super({
...serviceConstructor,
contract: serviceConstructor.Router,
type: "EncryptedNote"
});
}
getInstanceName() {
return `encrypted_notes_${this.netId}`;
}
getTovarishType() {
return "encrypted_notes";
}
async formatEvents(events) {
return events.map(({ blockNumber, index: logIndex, transactionHash, args }) => {
const { encryptedNote } = args;
if (encryptedNote && encryptedNote !== "0x") {
const eventObjects = {
blockNumber,
logIndex,
transactionHash
};
return {
...eventObjects,
encryptedNote
};
}
}).filter((e) => e);
}
}
const abiCoder = ethers.AbiCoder.defaultAbiCoder();
const proposalState = {
0: "Pending",
1: "Active",
2: "Defeated",
3: "Timelocked",
4: "AwaitingExecution",
5: "Executed",
6: "Expired"
};
function parseDescription(id, text) {
switch (id) {
case 1:
return {
title: text,
description: "See: https://torn.community/t/proposal-1-enable-torn-transfers/38"
};
case 10:
text = text.replace("\n", "\\n\\n");
break;
case 11:
text = text.replace('"description"', ',"description"');
break;
case 13:
text = text.replace(/\\\\n\\\\n(\s)?(\\n)?/g, "\\n");
break;
case 15:
text = text.replaceAll("'", '"');
text = text.replace('"description"', ',"description"');
break;
case 16:
text = text.replace("#16: ", "");
break;
case 21:
return {
title: "Proposal #21: Restore Governance",
description: ""
};
}
let title, description, rest;
try {
({ title, description } = JSON.parse(text));
} catch {
[title, ...rest] = text.split("\n", 2);
description = rest.join("\n");
}
return {
title,
description
};
}
function parseComment(Governance2, calldata) {
try {
const methodLength = 4;
const result = abiCoder.decode(["address[]", "uint256", "bool"], ethers.dataSlice(calldata, methodLength));
const data = Governance2.interface.encodeFunctionData(
// @ts-expect-error encodeFunctionData is broken lol
"castDelegatedVote",
result
);
const length = ethers.dataLength(data);
const str = abiCoder.decode(["string"], ethers.dataSlice(calldata, length))[0];
const [contact, message] = JSON.parse(str);
return {
contact,
message
};
} catch {
return {
contact: "",
message: ""
};
}
}
class BaseGovernanceService extends BaseEventsService {
Governance;
Aggregator;
ReverseRecords;
batchTransactionService;
constructor(serviceConstructor) {
const { Governance: Governance2, Aggregator: Aggregator2, ReverseRecords, provider } = serviceConstructor;
super({
...serviceConstructor,
contract: Governance2,
type: "*"
});
this.Governance = Governance2;
this.Aggregator = Aggregator2;
this.ReverseRecords = ReverseRecords;
this.batchTransactionService = new BatchTransactionService({
provider,
onProgress: this.updateTransactionProgress
});
}
getInstanceName() {
return `governance_${this.netId}`;
}
getTovarishType() {
return "governance";
}
async formatEvents(events) {
const proposalEvents = [];
const votedEvents = [];
const delegatedEvents = [];
const undelegatedEvents = [];
events.forEach(({ blockNumber, index: logIndex, transactionHash, args, eventName: event }) => {
const eventObjects = {
blockNumber,
logIndex,
transactionHash,
event
};
if (event === "ProposalCreated") {
const { id, proposer, target, startTime, endTime, description } = args;
proposalEvents.push({
...eventObjects,
id: Number(id),
proposer,
target,
startTime: Number(startTime),
endTime: Number(endTime),
description
});
}
if (event === "Voted") {
const { proposalId, voter, support, votes } = args;
votedEvents.push({
...eventObjects,
proposalId: Number(proposalId),
voter,
support,
votes,
from: "",
input: ""
});
}
if (event === "Delegated") {
const { account, to: delegateTo } = args;
delegatedEvents.push({
...eventObjects,
account,
delegateTo
});
}
if (event === "Undelegated") {
const { account, from: delegateFrom } = args;
undelegatedEvents.push({
...eventObjects,
account,
delegateFrom
});
}
});
if (votedEvents.length) {
this.updateTransactionProgress({ percentage: 0 });
const txs = await this.batchTransactionService.getBatchTransactions([
...new Set(votedEvents.map(({ transactionHash }) => transactionHash))
]);
votedEvents.forEach((event, index) => {
let { data: input, from } = txs.find((t) => t.hash === event.transactionHash);
if (!input || input.length > 2048) {
input = "";
}
votedEvents[index].from = from;
votedEvents[index].input = input;
});
}
return [...proposalEvents, ...votedEvents, ...delegatedEvents, ...undelegatedEvents];
}
async getAllProposals() {
const { events } = await this.updateEvents();
const proposalEvents = events.filter((e) => e.event === "ProposalCreated");
const allProposers = [...new Set(proposalEvents.map((e) => [e.proposer]).flat())];
const [QUORUM_VOTES, proposalStatus, proposerNameRecords] = await Promise.all([
this.Governance.QUORUM_VOTES(),
this.Aggregator.getAllProposals(this.Governance.target),
this.ReverseRecords.getNames(allProposers)
]);
const proposerNames = allProposers.reduce(
(acc, address, index) => {
if (proposerNameRecords[index]) {
acc[address] = proposerNameRecords[index];
}
return acc;
},
{}
);
return proposalEvents.map((event, index) => {
const { id, proposer, description: text } = event;
const status = proposalStatus[index];
const { forVotes, againstVotes, executed, extended, state } = status;
const { title, description } = parseDescription(id, text);
const quorum = (Number(forVotes + againstVotes) / Number(QUORUM_VOTES) * 100).toFixed(0) + "%";
return {
...event,
title,
proposerName: proposerNames[proposer] || void 0,
description,
forVotes,
againstVotes,
executed,
extended,
quorum,
state: proposalState[String(state)]
};
});
}
async getVotes(proposalId) {
const { events } = await this.getSavedEvents();
const votedEvents = events.filter(
(e) => e.event === "Voted" && e.proposalId === proposalId
);
const allVoters = [...new Set(votedEvents.map((e) => [e.from, e.voter]).flat())];
const names = await this.ReverseRecords.getNames(allVoters);
const ensNames = allVoters.reduce(
(acc, address, index) => {
if (names[index]) {
acc[address] = names[index];
}
return acc;
},
{}
);
const votes = votedEvents.map((event) => {
const { from, voter } = event;
const { contact, message } = parseComment(this.Governance, event.input);
return {
...event,
contact,
message,
fromName: ensNames[from] || void 0,
voterName: ensNames[voter] || void 0
};
});
return votes;
}
async getDelegatedBalance(ethAccount) {
const { events } = await this.getSavedEvents();
const delegatedAccs = events.filter((e) => e.event === "Delegated" && e.delegateTo === ethAccount).map((e) => e.account);
const undelegatedAccs = events.filter((e) => e.event === "Undelegated" && e.delegateFrom === ethAccount).map((e) => e.account);
const undel = [...undelegatedAccs];
const uniq = delegatedAccs.filter((acc) => {
const indexUndelegated = undel.indexOf(acc);
if (indexUndelegated !== -1) {
undel.splice(indexUndelegated, 1);
return false;
}
return true;
});
const [balances, uniqNameRecords] = await Promise.all([
this.Aggregator.getGovernanceBalances(this.Governance.target, uniq),
this.ReverseRecords.getNames(uniq)
]);
const uniqNames = uniq.reduce(
(acc, address, index) => {
if (uniqNameRecords[index]) {
acc[address] = uniqNameRecords[index];
}
return acc;
},
{}
);
return {
delegatedAccs,
undelegatedAccs,
uniq,
uniqNames,
balances,
balance: balances.reduce((acc, curr) => acc + curr, BigInt(0))
};
}
}
async function getTovarishNetworks(registryService, relayers) {
await Promise.all(
relayers.filter((r) => r.tovarishHost).map(async (relayer) => {
try {
relayer.tovarishNetworks = await fetchData(relayer.tovarishHost, {
...registryService.fetchDataOptions,
headers: {
"Content-Type": "application/json"
},
timeout: 3e4,
maxRetry: registryService.fetchDataOptions?.torPort ? 2 : 0
});
} catch {
relayer.tovarishNetworks = [];
}
})
);
}
const staticRelayers = [
{
ensName: "tornadowithdraw.eth",
relayerAddress: "0x40c3d1656a26C9266f4A10fed0D87EFf79F54E64",
hostnames: {},
tovarishHost: "tornadowithdraw.com",
tovarishNetworks: enabledChains
}
];
class BaseRegistryService extends BaseEventsService {
Aggregator;
relayerEnsSubdomains;
updateInterval;
constructor(serviceConstructor) {
const { RelayerRegistry: contract, Aggregator: Aggregator2, relayerEnsSubdomains } = serviceConstructor;
super({
...serviceConstructor,
contract,
type: "*"
});
this.Aggregator = Aggregator2;
this.relayerEnsSubdomains = relayerEnsSubdomains;
this.updateInterval = 86400;
}
getInstanceName() {
return `registry_${this.netId}`;
}
getTovarishType() {
return "registry";
}
async formatEvents(events) {
const relayerRegisteredEvents = [];
const relayerUnregisteredEvents = [];
const workerRegisteredEvents = [];
const workerUnregisteredEvents = [];
events.forEach(({ blockNumber, index: logIndex, transactionHash, args, eventName: event }) => {
const eventObjects = {
blockNumber,
logIndex,
transactionHash,
event
};
if (event === "RelayerRegistered") {
const { relayer: ensHash, ensName, relayerAddress, stakedAmount } = args;
relayerRegisteredEvents.push({
...eventObjects,
ensName,
relayerAddress,
ensHash,
stakedAmount: ethers.formatEther(stakedAmount)
});
}
if (event === "RelayerUnregistered") {
const { relayer: relayerAddress } = args;
relayerUnregisteredEvents.push({
...eventObjects,
relayerAddress
});
}
if (event === "WorkerRegistered") {
const { relayer: relayerAddress, worker: workerAddress } = args;
workerRegisteredEvents.push({
...eventObjects,
relayerAddress,
workerAddress
});
}
if (event === "WorkerUnregistered") {
const { relayer: relayerAddress, worker: workerAddress } = args;
workerUnregisteredEvents.push({
...eventObjects,
relayerAddress,
workerAddress
});
}
});
return [
...relayerRegisteredEvents,
...relayerUnregisteredEvents,
...workerRegisteredEvents,
...workerUnregisteredEvents
];
}
/**
* Get saved or cached relayers
*/
async getRelayersFromDB() {
return {
lastBlock: 0,
timestamp: 0,
relayers: []
};
}
/**
* Relayers from remote cache (Either from local cache, CDN, or from IPFS)
*/
async getRelayersFromCache() {
return {
lastBlock: 0,
timestamp: 0,
relayers: [],
fromCache: true
};
}
async getSavedRelayers() {
let cachedRelayers = await this.getRelayersFromDB();
if (!cachedRelayers || !cachedRelayers.relayers.length) {
cachedRelayers = await this.getRelayersFromCache();
}
return cachedRelayers;
}
async getLatestRelayers() {
const { events: allEvents, lastBlock } = await this.updateEvents();
const events = allEvents.filter((e) => e.event === "RelayerRegistered");
const subdomains = Object.values(this.relayerEnsSubdomains);
const registerSet = /* @__PURE__ */ new Set();
const uniqueRegisters = events.filter(({ ensName }) => {
if (!registerSet.has(ensName)) {
registerSet.add(ensName);
return true;
}
return false;
});
const relayerNameHashes = uniqueRegisters.map((r) => ethers.namehash(r.ensName));
const [relayersData, timestamp] = await Promise.all([
this.Aggregator.relayersData.staticCall(relayerNameHashes, subdomains.concat("tovarish-relayer")),
this.provider.getBlock(lastBlock).then((b) => Number(b?.timestamp))
]);
const relayers = relayersData.map(({ owner, balance: stakeBalance, records, isRegistered }, index) => {
const { ensName, relayerAddress } = uniqueRegisters[index];
let tovarishHost = void 0;
const hostnames = records.reduce((acc, record, recordIndex) => {
if (record) {
if (recordIndex === records.length - 1) {
tovarishHost = record;
return acc;
}
acc[Number(Object.keys(this.relayerEnsSubdomains)[recordIndex])] = record;
}
return acc;
}, {});
const hasMinBalance = stakeBalance >= MIN_STAKE_BALANCE;
const preCondition = Object.keys(hostnames).length && isRegistered && hasMinBalance;
if (preCondition) {
return {
ensName,
relayerAddress: owner,
registeredAddress: owner !== relayerAddress ? relayerAddress : void 0,
isRegistered,
stakeBalance: ethers.formatEther(stakeBalance),
hostnames,
tovarishHost
};
}
}).filter((r) => r);
await getTovarishNetworks(this, relayers);
const allRelayers = [...staticRelayers, ...relayers];
const tovarishRelayers = allRelayers.filter((r) => r.tovarishHost);
const classicRelayers = allRelayers.filter((r) => !r.tovarishHost);
return {
lastBlock,
timestamp,
relayers: [...tovarishRelayers, ...classicRelayers]
};
}
/**
* Handle saving relayers
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async saveRelayers({ lastBlock, timestamp, relayers }) {
}
/**
* Get cached or latest relayer and save to local
*/
async updateRelayers() {
let { lastBlock, timestamp, relayers, fromCache } = await this.getSavedRelayers();
let shouldSave = fromCache ?? false;
if (!relayers.length || timestamp + this.updateInterval < Math.floor(Date.now() / 1e3)) {
console.log("\nUpdating relayers from registry\n");
({ lastBlock, timestamp, relayers } = await this.getLatestRelayers());
shouldSave = true;
}
if (shouldSave) {
await this.saveRelayers({ lastBlock, timestamp, relayers });
}
return { lastBlock, timestamp, relayers };
}
}
class BaseRevenueService extends BaseEventsService {
batchTransactionService;
batchBlockService;
constructor(serviceConstructor) {
const { RelayerRegistry: contract, provider } = serviceConstructor;
super({
...serviceConstructor,
contract,
type: "StakeBurned"
});
this.batchTransactionService = new BatchTransactionService({
provider,
onProgress: this.updateTransactionProgress
});
this.batchBlockService = new BatchBlockService({
provider,
onProgress: this.updateBlockProgress
});
}
getInstanceName() {
return `revenue_${this.netId}`;
}
getTovarishType() {
return "revenue";
}
async formatEvents(events) {
const blocks = await this.batchBlockService.getBatchBlocks([
...new Set(events.map(({ blockNumber }) => blockNumber))
]);
const receipts = await this.batchTransactionService.getBatchReceipt([
...new Set(events.map(({ transactionHash }) => transactionHash))
]);
const registeredRelayers = new Set(events.map(({ args }) => args.relayer));
const tornadoInterface = contracts.Tornado__factory.createInterface();
const withdrawHash = tornadoInterface.getEvent("Withdrawal").topicHash;
const withdrawalLogs = receipts.map(
(receipt) => receipt.logs.map((log) => {
if (log.topics[0] === withdrawHash) {
const block = blocks.find((b) => b.number === log.blockNumber);
const parsedLog = tornadoInterface.parseLog(log);
if (parsedLog && registeredRelayers.has(parsedLog.args.relayer)) {
return {
instanceAddress: log.address,
gasFee: (receipt.cumulativeGasUsed * receipt.gasPrice).toString(),
relayerFee: parsedLog.args.fee.toString(),
timestamp: block?.timestamp || 0
};
}
}
}).filter((l) => l)
).flat();
if (withdrawalLogs.length !== events.length) {
console.log(
`
RevenueService: Mismatch on withdrawal logs (${withdrawalLogs.length} ) and events logs (${events.length})
`
);
}
return events.map(({ blockNumber, index: logIndex, transactionHash, args }, index) => {
const eventObjects = {
blockNumber,
logIndex,
transactionHash
};
const { relayer: relayerAddress, amountBurned } = args;
const { instanceAddress, gasFee, relayerFee, timestamp } = withdrawalLogs[index];
return {
...eventObjects,
relayerAddress,
amountBurned: amountBurned.toString(),
instanceAddress,
gasFee,
relayerFee,
timestamp
};
});
}
}
function zipAsync(file, options) {
return new Promise((res, rej) => {
fflate.zip(file, { ...options || {}, mtime: /* @__PURE__ */ new Date("1/1/1980") }, (err, data) => {
if (err) {
rej(err);
return;
}
res(data);
});
});
}
function unzipAsync(data) {
return new Promise((res, rej) => {
fflate.unzip(data, {}, (err, data2) => {
if (err) {
rej(err);
return;
}
res(data2);
});
});
}
async function downloadZip({
staticUrl = "",
zipName,
zipDigest,
parseJson = true
}) {
const url = `${staticUrl}/${zipName}.zip`;
const resp = await fetchData(url, {
method: "GET",
returnResponse: true
});
const data = new Uint8Array(await resp.arrayBuffer());
if (zipDigest) {
const hash = "sha384-" + bytesToBase64(await digest(data));
if (zipDigest !== hash) {
const errMsg = `Invalid digest hash for file ${url}, wants ${zipDigest} has ${hash}`;
throw new Error(errMsg);
}
}
const { [zipName]: content } = await unzipAsync(data);
console.log(`Downloaded ${url}${zipDigest ? ` ( Digest: ${zipDigest} )` : ""}`);
if (parseJson) {
return JSON.parse(new TextDecoder().decode(content));
}
return content;
}
async function saveDBEvents({
idb,
instanceName,
events,
lastBlock
}) {
try {
const formattedEvents = events.map((e) => {
return {
eid: `${e.transactionHash}_${e.logIndex}`,
...e
};
});
await idb.createMultipleTransactions({
data: formattedEvents,
storeName: instanceName
});
await idb.putItem({
data: {
blockNumber: lastBlock,
name: instanceName
},
storeName: "lastEvents"
});
} catch (err) {
console.log("Method saveDBEvents has error");
console.log(err);
}
}
async function loadDBEvents({
idb,
instanceName
}) {
try {
const lastBlockStore = await idb.getItem({
storeName: "lastEvents",
key: instanceName
});
if (!lastBlockStore?.blockNumber) {
return {
events: [],
lastBlock: 0
};
}
const events = (await idb.getAll({
storeName: instanceName
})).map((e) => {
delete e.eid;
return e;
});
return {
events,
lastBlock: lastBlockStore.blockNumber
};
} catch (err) {
console.log("Method loadDBEvents has error");
console.log(err);
return {
events: [],
lastBlock: 0
};
}
}
async function loadRemoteEvents({
staticUrl,
instanceName,
deployedBlock,
zipDigest
}) {
try {
const zipName = `${instanceName}.json`.toLowerCase();
const events = await downloadZip({
staticUrl,
zipName,
zipDigest
});
if (!Array.isArray(events)) {
const errStr = `Invalid events from ${staticUrl}/${zipName}`;
throw new Error(errStr);
}
return {
events,
lastBlock: events[events.length - 1]?.blockNumber || deployedBlock,
fromCache: true
};
} catch (err) {
console.log("Method loadRemoteEvents has error");
console.log(err);
return {
events: [],
lastBlock: deployedBlock,
fromCache: true
};
}
}
class DBTornadoService extends BaseTornadoService {
staticUrl;
idb;
zipDigest;
constructor(params) {
super(params);
this.staticUrl = params.staticUrl;
this.idb = params.idb;
}
async getEventsFromDB() {
return await loadDBEvents({
idb: this.idb,
instanceName: this.getInstanceName()
});
}
async getEventsFromCache() {
return await loadRemoteEvents({
staticUrl: this.staticUrl,
instanceName: this.getInstanceName(),
deployedBlock: this.deployedBlock,
zipDigest: this.zipDigest
});
}
async saveEvents({ events, lastBlock }) {
await saveDBEvents({
idb: this.idb,
instanceName: this.getInstanceName(),
events,
lastBlock
});
}
}
class DBEchoService extends BaseEchoService {
staticUrl;
idb;
zipDigest;
constructor(params) {
super(params);
this.staticUrl = params.staticUrl;
this.idb = params.idb;
}
async getEventsFromDB() {
return await loadDBEvents({
idb: this.idb,
instanceName: this.getInstanceName()
});
}
async getEventsFromCache() {
return await loadRemoteEvents({
staticUrl: this.staticUrl,
instanceName: this.getInstanceName(),
deployedBlock: this.deployedBlock,
zipDigest: this.zipDigest
});
}
async saveEvents({ events, lastBlock }) {
await saveDBEvents({
idb: this.idb,
instanceName: this.getInstanceName(),
events,
lastBlock
});
}
}
class DBEncryptedNotesService extends BaseEncryptedNotesService {
staticUrl;
idb;
zipDigest;
constructor(params) {
super(params);
this.staticUrl = params.staticUrl;
this.idb = params.idb;
}
async getEventsFromDB() {
return await loadDBEvents({
idb: this.idb,
instanceName: this.getInstanceName()
});
}
async getEventsFromCache() {
return await loadRemoteEvents({
staticUrl: this.staticUrl,
instanceName: this.getInstanceName(),
deployedBlock: this.deployedBlock,
zipDigest: this.zipDigest
});
}
async saveEvents({ events, lastBlock }) {
await saveDBEvents({
idb: this.idb,
instanceName: this.getInstanceName(),
events,
lastBlock
});
}
}
class DBGovernanceService extends BaseGovernanceService {
staticUrl;
idb;
zipDigest;
constructor(params) {
super(params);
this.staticUrl = params.staticUrl;
this.idb = params.idb;
}
async getEventsFromDB() {
return await loadDBEvents({
idb: this.idb,
instanceName: this.getInstanceName()
});
}
async getEventsFromCache() {
return await loadRemoteEvents({
staticUrl: this.staticUrl,
instanceName: this.getInstanceName(),
deployedBlock: this.deployedBlock,
zipDigest: this.zipDigest
});
}
async saveEvents({ events, lastBlock }) {
await saveDBEvents({
idb: this.idb,
instanceName: this.getInstanceName(),
events,
lastBlock
});
}
}
class DBRegistryService extends BaseRegistryService {
staticUrl;
idb;
zipDigest;
relayerJsonDigest;
constructor(params) {
super(params);
this.staticUrl = params.staticUrl;
this.idb = params.idb;
}
async getEventsFromDB() {
return await loadDBEvents({
idb: this.idb,
instanceName: this.getInstanceName()
});
}
async getEventsFromCache() {
return await loadRemoteEvents({
staticUrl: this.staticUrl,
instanceName: this.getInstanceName(),
deployedBlock: this.deployedBlock,
zipDigest: this.zipDigest
});
}
async saveEvents({ events, lastBlock }) {
await saveDBEvents({
idb: this.idb,
instanceName: this.getInstanceName(),
events,
lastBlock
});
}
async getRelayersFromDB() {
try {
const allCachedRelayers = await this.idb.getAll({
storeName: `relayers_${this.netId}`
});
if (!allCachedRelayers?.length) {
return {
lastBlock: 0,
timestamp: 0,
relayers: []
};
}
return allCachedRelayers.slice(-1)[0];
} catch (err) {
console.log("Method getRelayersFromDB has error");
console.log(err);
return {
lastBlock: 0,
timestamp: 0,
relayers: []
};
}
}
async getRelayersFromCache() {
const url = `${this.staticUrl}/relayers.json`;
try {
const resp = await fetchData(url, {
method: "GET",
returnResponse: true
});
const data = new Uint8Array(await resp.arrayBuffer());
if (this.relayerJsonDigest) {
const hash = "sha384-" + bytesToBase64(await digest(data));
if (hash !== this.relayerJsonDigest) {
const errMsg = `Invalid digest hash for ${url}, wants ${this.relayerJsonDigest} has ${hash}`;
throw new Error(errMsg);
}
}
return JSON.parse(new TextDecoder().decode(data));
} catch (err) {
console.log("Method getRelayersFromCache has error");
console.log(err);
return {
lastBlock: 0,
timestamp: 0,
relayers: []
};
}
}
async saveRelayers(cachedRelayers) {
try {
await this.idb.putItem({
data: cachedRelayers,
storeName: `relayers_${this.netId}`
});
} catch (err) {
console.log("Method saveRelayers has error");
console.log(err);
}
}
}
class DBRevenueService extends BaseRevenueService {
staticUrl;
idb;
zipDigest;
relayerJsonDigest;
constructor(params) {
super(params);
this.staticUrl = params.staticUrl;
this.idb = params.idb;
}
async getEventsFromDB() {
return await loadDBEvents({
idb: this.idb,
instanceName: this.getInstanceName()
});
}
async getEventsFromCache() {
return await loadRemoteEvents({
staticUrl: this.staticUrl,
instanceName: this.getInstanceName(),
deployedBlock: this.deployedBlock,
zipDigest: this.zipDigest
});
}
async saveEvents({ events, lastBlock }) {
await saveDBEvents({
idb: this.idb,
instanceName: this.getInstanceName(),
events,
lastBlock
});
}
}
const _abi$8 = [
{
constant: true,
inputs: [
{
internalType: "bytes4",
name: "interfaceID",
type: "bytes4"
}
],
name: "supportsInterface",
outputs: [
{
internalType: "bool",
name: "",
type: "bool"
}
],
payable: false,
stateMutability: "pure",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "string",
name: "key",
type: "string"
},
{
internalType: "string",
name: "value",
type: "string"
}
],
name: "setText",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: true,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "bytes4",
name: "interfaceID",
type: "bytes4"
}
],
name: "interfaceImplementer",
outputs: [
{
internalType: "address",
name: "",
type: "address"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: true,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "uint256",
name: "contentTypes",
type: "uint256"
}
],
name: "ABI",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
},
{
internalType: "bytes",
name: "",
type: "bytes"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "bytes32",
name: "x",
type: "bytes32"
},
{
internalType: "bytes32",
name: "y",
type: "bytes32"
}
],
name: "setPubkey",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "bytes",
name: "hash",
type: "bytes"
}
],
name: "setContenthash",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: true,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
}
],
name: "addr",
outputs: [
{
internalType: "address",
name: "",
type: "address"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "address",
name: "target",
type: "address"
},
{
internalType: "bool",
name: "isAuthorised",
type: "bool"
}
],
name: "setAuthorisation",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: true,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "string",
name: "key",
type: "string"
}
],
name: "text",
outputs: [
{
internalType: "string",
name: "",
type: "string"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "uint256",
name: "contentType",
type: "uint256"
},
{
internalType: "bytes",
name: "data",
type: "bytes"
}
],
name: "setABI",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: true,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
}
],
name: "name",
outputs: [
{
internalType: "string",
name: "",
type: "string"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "string",
name: "name",
type: "string"
}
],
name: "setName",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "uint256",
name: "coinType",
type: "uint256"
},
{
internalType: "bytes",
name: "a",
type: "bytes"
}
],
name: "setAddr",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: true,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
}
],
name: "contenthash",
outputs: [
{
internalType: "bytes",
name: "",
type: "bytes"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: true,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
}
],
name: "pubkey",
outputs: [
{
internalType: "bytes32",
name: "x",
type: "bytes32"
},
{
internalType: "bytes32",
name: "y",
type: "bytes32"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "address",
name: "a",
type: "address"
}
],
name: "setAddr",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "bytes4",
name: "interfaceID",
type: "bytes4"
},
{
internalType: "address",
name: "implementer",
type: "address"
}
],
name: "setInterface",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: true,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "uint256",
name: "coinType",
type: "uint256"
}
],
name: "addr",
outputs: [
{
internalType: "bytes",
name: "",
type: "bytes"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: true,
inputs: [
{
internalType: "bytes32",
name: "",
type: "bytes32"
},
{
internalType: "address",
name: "",
type: "address"
},
{
internalType: "address",
name: "",
type: "address"
}
],
name: "authorisations",
outputs: [
{
internalType: "bool",
name: "",
type: "bool"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "contract ENS",
name: "_ens",
type: "address"
}
],
payable: false,
stateMutability: "nonpayable",
type: "constructor"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: true,
internalType: "address",
name: "owner",
type: "address"
},
{
indexed: true,
internalType: "address",
name: "target",
type: "address"
},
{
indexed: false,
internalType: "bool",
name: "isAuthorised",
type: "bool"
}
],
name: "AuthorisationChanged",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: false,
internalType: "string",
name: "indexedKey",
type: "string"
},
{
indexed: false,
internalType: "string",
name: "key",
type: "string"
}
],
name: "TextChanged",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: false,
internalType: "bytes32",
name: "x",
type: "bytes32"
},
{
indexed: false,
internalType: "bytes32",
name: "y",
type: "bytes32"
}
],
name: "PubkeyChanged",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: false,
internalType: "string",
name: "name",
type: "string"
}
],
name: "NameChanged",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: true,
internalType: "bytes4",
name: "interfaceID",
type: "bytes4"
},
{
indexed: false,
internalType: "address",
name: "implementer",
type: "address"
}
],
name: "InterfaceChanged",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: false,
internalType: "bytes",
name: "hash",
type: "bytes"
}
],
name: "ContenthashChanged",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: false,
internalType: "address",
name: "a",
type: "address"
}
],
name: "AddrChanged",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: false,
internalType: "uint256",
name: "coinType",
type: "uint256"
},
{
indexed: false,
internalType: "bytes",
name: "newAddress",
type: "bytes"
}
],
name: "AddressChanged",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: true,
internalType: "uint256",
name: "contentType",
type: "uint256"
}
],
name: "ABIChanged",
type: "event"
}
];
class ENS__factory {
static abi = _abi$8;
static createInterface() {
return new ethers.Interface(_abi$8);
}
static connect(address, runner) {
return new ethers.Contract(address, _abi$8, runner);
}
}
const _abi$7 = [
{
inputs: [
{
internalType: "contract ENS",
name: "_ens",
type: "address"
},
{
internalType: "contract IBaseRegistrar",
name: "_registrar",
type: "address"
},
{
internalType: "contract IMetadataService",
name: "_metadataService",
type: "address"
}
],
stateMutability: "nonpayable",
type: "constructor"
},
{
inputs: [],
name: "CannotUpgrade",
type: "error"
},
{
inputs: [],
name: "IncompatibleParent",
type: "error"
},
{
inputs: [
{
internalType: "address",
name: "owner",
type: "address"
}
],
name: "IncorrectTargetOwner",
type: "error"
},
{
inputs: [],
name: "IncorrectTokenType",
type: "error"
},
{
inputs: [
{
internalType: "bytes32",
name: "labelHash",
type: "bytes32"
},
{
internalType: "bytes32",
name: "expectedLabelhash",
type: "bytes32"
}
],
name: "LabelMismatch",
type: "error"
},
{
inputs: [
{
internalType: "string",
name: "label",
type: "string"
}
],
name: "LabelTooLong",
type: "error"
},
{
inputs: [],
name: "LabelTooShort",
type: "error"
},
{
inputs: [],
name: "NameIsNotWrapped",
type: "error"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
}
],
name: "OperationProhibited",
type: "error"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "address",
name: "addr",
type: "address"
}
],
name: "Unauthorised",
type: "error"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "owner",
type: "address"
},
{
indexed: true,
internalType: "address",
name: "approved",
type: "address"
},
{
indexed: true,
internalType: "uint256",
name: "tokenId",
type: "uint256"
}
],
name: "Approval",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "account",
type: "address"
},
{
indexed: true,
internalType: "address",
name: "operator",
type: "address"
},
{
indexed: false,
internalType: "bool",
name: "approved",
type: "bool"
}
],
name: "ApprovalForAll",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "controller",
type: "address"
},
{
indexed: false,
internalType: "bool",
name: "active",
type: "bool"
}
],
name: "ControllerChanged",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: false,
internalType: "uint64",
name: "expiry",
type: "uint64"
}
],
name: "ExpiryExtended",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: false,
internalType: "uint32",
name: "fuses",
type: "uint32"
}
],
name: "FusesSet",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: false,
internalType: "address",
name: "owner",
type: "address"
}
],
name: "NameUnwrapped",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: false,
internalType: "bytes",
name: "name",
type: "bytes"
},
{
indexed: false,
internalType: "address",
name: "owner",
type: "address"
},
{
indexed: false,
internalType: "uint32",
name: "fuses",
type: "uint32"
},
{
indexed: false,
internalType: "uint64",
name: "expiry",
type: "uint64"
}
],
name: "NameWrapped",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "previousOwner",
type: "address"
},
{
indexed: true,
internalType: "address",
name: "newOwner",
type: "address"
}
],
name: "OwnershipTransferred",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "operator",
type: "address"
},
{
indexed: true,
internalType: "address",
name: "from",
type: "address"
},
{
indexed: true,
internalType: "address",
name: "to",
type: "address"
},
{
indexed: false,
internalType: "uint256[]",
name: "ids",
type: "uint256[]"
},
{
indexed: false,
internalType: "uint256[]",
name: "values",
type: "uint256[]"
}
],
name: "TransferBatch",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "operator",
type: "address"
},
{
indexed: true,
internalType: "address",
name: "from",
type: "address"
},
{
indexed: true,
internalType: "address",
name: "to",
type: "address"
},
{
indexed: false,
internalType: "uint256",
name: "id",
type: "uint256"
},
{
indexed: false,
internalType: "uint256",
name: "value",
type: "uint256"
}
],
name: "TransferSingle",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "string",
name: "value",
type: "string"
},
{
indexed: true,
internalType: "uint256",
name: "id",
type: "uint256"
}
],
name: "URI",
type: "event"
},
{
inputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
name: "_tokens",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "uint32",
name: "fuseMask",
type: "uint32"
}
],
name: "allFusesBurned",
outputs: [
{
internalType: "bool",
name: "",
type: "bool"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "to",
type: "address"
},
{
internalType: "uint256",
name: "tokenId",
type: "uint256"
}
],
name: "approve",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "account",
type: "address"
},
{
internalType: "uint256",
name: "id",
type: "uint256"
}
],
name: "balanceOf",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "address[]",
name: "accounts",
type: "address[]"
},
{
internalType: "uint256[]",
name: "ids",
type: "uint256[]"
}
],
name: "balanceOfBatch",
outputs: [
{
internalType: "uint256[]",
name: "",
type: "uint256[]"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "address",
name: "addr",
type: "address"
}
],
name: "canExtendSubnames",
outputs: [
{
internalType: "bool",
name: "",
type: "bool"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "address",
name: "addr",
type: "address"
}
],
name: "canModifyName",
outputs: [
{
internalType: "bool",
name: "",
type: "bool"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "",
type: "address"
}
],
name: "controllers",
outputs: [
{
internalType: "bool",
name: "",
type: "bool"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "ens",
outputs: [
{
internalType: "contract ENS",
name: "",
type: "address"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "parentNode",
type: "bytes32"
},
{
internalType: "bytes32",
name: "labelhash",
type: "bytes32"
},
{
internalType: "uint64",
name: "expiry",
type: "uint64"
}
],
name: "extendExpiry",
outputs: [
{
internalType: "uint64",
name: "",
type: "uint64"
}
],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "uint256",
name: "id",
type: "uint256"
}
],
name: "getApproved",
outputs: [
{
internalType: "address",
name: "operator",
type: "address"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "uint256",
name: "id",
type: "uint256"
}
],
name: "getData",
outputs: [
{
internalType: "address",
name: "owner",
type: "address"
},
{
internalType: "uint32",
name: "fuses",
type: "uint32"
},
{
internalType: "uint64",
name: "expiry",
type: "uint64"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "account",
type: "address"
},
{
internalType: "address",
name: "operator",
type: "address"
}
],
name: "isApprovedForAll",
outputs: [
{
internalType: "bool",
name: "",
type: "bool"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "parentNode",
type: "bytes32"
},
{
internalType: "bytes32",
name: "labelhash",
type: "bytes32"
}
],
name: "isWrapped",
outputs: [
{
internalType: "bool",
name: "",
type: "bool"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
}
],
name: "isWrapped",
outputs: [
{
internalType: "bool",
name: "",
type: "bool"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "metadataService",
outputs: [
{
internalType: "contract IMetadataService",
name: "",
type: "address"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "name",
outputs: [
{
internalType: "string",
name: "",
type: "string"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "",
type: "bytes32"
}
],
name: "names",
outputs: [
{
internalType: "bytes",
name: "",
type: "bytes"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "to",
type: "address"
},
{
internalType: "address",
name: "",
type: "address"
},
{
internalType: "uint256",
name: "tokenId",
type: "uint256"
},
{
internalType: "bytes",
name: "data",
type: "bytes"
}
],
name: "onERC721Received",
outputs: [
{
internalType: "bytes4",
name: "",
type: "bytes4"
}
],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [],
name: "owner",
outputs: [
{
internalType: "address",
name: "",
type: "address"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "uint256",
name: "id",
type: "uint256"
}
],
name: "ownerOf",
outputs: [
{
internalType: "address",
name: "owner",
type: "address"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "_token",
type: "address"
},
{
internalType: "address",
name: "_to",
type: "address"
},
{
internalType: "uint256",
name: "_amount",
type: "uint256"
}
],
name: "recoverFunds",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "string",
name: "label",
type: "string"
},
{
internalType: "address",
name: "wrappedOwner",
type: "address"
},
{
internalType: "uint256",
name: "duration",
type: "uint256"
},
{
internalType: "address",
name: "resolver",
type: "address"
},
{
internalType: "uint16",
name: "ownerControlledFuses",
type: "uint16"
}
],
name: "registerAndWrapETH2LD",
outputs: [
{
internalType: "uint256",
name: "registrarExpiry",
type: "uint256"
}
],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [],
name: "registrar",
outputs: [
{
internalType: "contract IBaseRegistrar",
name: "",
type: "address"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "uint256",
name: "tokenId",
type: "uint256"
},
{
internalType: "uint256",
name: "duration",
type: "uint256"
}
],
name: "renew",
outputs: [
{
internalType: "uint256",
name: "expires",
type: "uint256"
}
],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [],
name: "renounceOwnership",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "from",
type: "address"
},
{
internalType: "address",
name: "to",
type: "address"
},
{
internalType: "uint256[]",
name: "ids",
type: "uint256[]"
},
{
internalType: "uint256[]",
name: "amounts",
type: "uint256[]"
},
{
internalType: "bytes",
name: "data",
type: "bytes"
}
],
name: "safeBatchTransferFrom",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "from",
type: "address"
},
{
internalType: "address",
name: "to",
type: "address"
},
{
internalType: "uint256",
name: "id",
type: "uint256"
},
{
internalType: "uint256",
name: "amount",
type: "uint256"
},
{
internalType: "bytes",
name: "data",
type: "bytes"
}
],
name: "safeTransferFrom",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "operator",
type: "address"
},
{
internalType: "bool",
name: "approved",
type: "bool"
}
],
name: "setApprovalForAll",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "parentNode",
type: "bytes32"
},
{
internalType: "bytes32",
name: "labelhash",
type: "bytes32"
},
{
internalType: "uint32",
name: "fuses",
type: "uint32"
},
{
internalType: "uint64",
name: "expiry",
type: "uint64"
}
],
name: "setChildFuses",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "controller",
type: "address"
},
{
internalType: "bool",
name: "active",
type: "bool"
}
],
name: "setController",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "uint16",
name: "ownerControlledFuses",
type: "uint16"
}
],
name: "setFuses",
outputs: [
{
internalType: "uint32",
name: "",
type: "uint32"
}
],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "contract IMetadataService",
name: "_metadataService",
type: "address"
}
],
name: "setMetadataService",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "address",
name: "owner",
type: "address"
},
{
internalType: "address",
name: "resolver",
type: "address"
},
{
internalType: "uint64",
name: "ttl",
type: "uint64"
}
],
name: "setRecord",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "address",
name: "resolver",
type: "address"
}
],
name: "setResolver",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "parentNode",
type: "bytes32"
},
{
internalType: "string",
name: "label",
type: "string"
},
{
internalType: "address",
name: "owner",
type: "address"
},
{
internalType: "uint32",
name: "fuses",
type: "uint32"
},
{
internalType: "uint64",
name: "expiry",
type: "uint64"
}
],
name: "setSubnodeOwner",
outputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
}
],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "parentNode",
type: "bytes32"
},
{
internalType: "string",
name: "label",
type: "string"
},
{
internalType: "address",
name: "owner",
type: "address"
},
{
internalType: "address",
name: "resolver",
type: "address"
},
{
internalType: "uint64",
name: "ttl",
type: "uint64"
},
{
internalType: "uint32",
name: "fuses",
type: "uint32"
},
{
internalType: "uint64",
name: "expiry",
type: "uint64"
}
],
name: "setSubnodeRecord",
outputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
}
],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "uint64",
name: "ttl",
type: "uint64"
}
],
name: "setTTL",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "contract INameWrapperUpgrade",
name: "_upgradeAddress",
type: "address"
}
],
name: "setUpgradeContract",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes4",
name: "interfaceId",
type: "bytes4"
}
],
name: "supportsInterface",
outputs: [
{
internalType: "bool",
name: "",
type: "bool"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "newOwner",
type: "address"
}
],
name: "transferOwnership",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "parentNode",
type: "bytes32"
},
{
internalType: "bytes32",
name: "labelhash",
type: "bytes32"
},
{
internalType: "address",
name: "controller",
type: "address"
}
],
name: "unwrap",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "labelhash",
type: "bytes32"
},
{
internalType: "address",
name: "registrant",
type: "address"
},
{
internalType: "address",
name: "controller",
type: "address"
}
],
name: "unwrapETH2LD",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes",
name: "name",
type: "bytes"
},
{
internalType: "bytes",
name: "extraData",
type: "bytes"
}
],
name: "upgrade",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [],
name: "upgradeContract",
outputs: [
{
internalType: "contract INameWrapperUpgrade",
name: "",
type: "address"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "uint256",
name: "tokenId",
type: "uint256"
}
],
name: "uri",
outputs: [
{
internalType: "string",
name: "",
type: "string"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "bytes",
name: "name",
type: "bytes"
},
{
internalType: "address",
name: "wrappedOwner",
type: "address"
},
{
internalType: "address",
name: "resolver",
type: "address"
}
],
name: "wrap",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "string",
name: "label",
type: "string"
},
{
internalType: "address",
name: "wrappedOwner",
type: "address"
},
{
internalType: "uint16",
name: "ownerControlledFuses",
type: "uint16"
},
{
internalType: "address",
name: "resolver",
type: "address"
}
],
name: "wrapETH2LD",
outputs: [
{
internalType: "uint64",
name: "expiry",
type: "uint64"
}
],
stateMutability: "nonpayable",
type: "function"
}
];
class ENSNameWrapper__factory {
static abi = _abi$7;
static createInterface() {
return new ethers.Interface(_abi$7);
}
static connect(address, runner) {
return new ethers.Contract(address, _abi$7, runner);
}
}
const _abi$6 = [
{
inputs: [
{
internalType: "contract ENS",
name: "_old",
type: "address"
}
],
payable: false,
stateMutability: "nonpayable",
type: "constructor"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "owner",
type: "address"
},
{
indexed: true,
internalType: "address",
name: "operator",
type: "address"
},
{
indexed: false,
internalType: "bool",
name: "approved",
type: "bool"
}
],
name: "ApprovalForAll",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: true,
internalType: "bytes32",
name: "label",
type: "bytes32"
},
{
indexed: false,
internalType: "address",
name: "owner",
type: "address"
}
],
name: "NewOwner",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: false,
internalType: "address",
name: "resolver",
type: "address"
}
],
name: "NewResolver",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: false,
internalType: "uint64",
name: "ttl",
type: "uint64"
}
],
name: "NewTTL",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: false,
internalType: "address",
name: "owner",
type: "address"
}
],
name: "Transfer",
type: "event"
},
{
constant: true,
inputs: [
{
internalType: "address",
name: "owner",
type: "address"
},
{
internalType: "address",
name: "operator",
type: "address"
}
],
name: "isApprovedForAll",
outputs: [
{
internalType: "bool",
name: "",
type: "bool"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: true,
inputs: [],
name: "old",
outputs: [
{
internalType: "contract ENS",
name: "",
type: "address"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: true,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
}
],
name: "owner",
outputs: [
{
internalType: "address",
name: "",
type: "address"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: true,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
}
],
name: "recordExists",
outputs: [
{
internalType: "bool",
name: "",
type: "bool"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: true,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
}
],
name: "resolver",
outputs: [
{
internalType: "address",
name: "",
type: "address"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "address",
name: "operator",
type: "address"
},
{
internalType: "bool",
name: "approved",
type: "bool"
}
],
name: "setApprovalForAll",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "address",
name: "owner",
type: "address"
}
],
name: "setOwner",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "address",
name: "owner",
type: "address"
},
{
internalType: "address",
name: "resolver",
type: "address"
},
{
internalType: "uint64",
name: "ttl",
type: "uint64"
}
],
name: "setRecord",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "address",
name: "resolver",
type: "address"
}
],
name: "setResolver",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "bytes32",
name: "label",
type: "bytes32"
},
{
internalType: "address",
name: "owner",
type: "address"
}
],
name: "setSubnodeOwner",
outputs: [
{
internalType: "bytes32",
name: "",
type: "bytes32"
}
],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "bytes32",
name: "label",
type: "bytes32"
},
{
internalType: "address",
name: "owner",
type: "address"
},
{
internalType: "address",
name: "resolver",
type: "address"
},
{
internalType: "uint64",
name: "ttl",
type: "uint64"
}
],
name: "setSubnodeRecord",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "uint64",
name: "ttl",
type: "uint64"
}
],
name: "setTTL",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: true,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
}
],
name: "ttl",
outputs: [
{
internalType: "uint64",
name: "",
type: "uint64"
}
],
payable: false,
stateMutability: "view",
type: "function"
}
];
class ENSRegistry__factory {
static abi = _abi$6;
static createInterface() {
return new ethers.Interface(_abi$6);
}
static connect(address, runner) {
return new ethers.Contract(address, _abi$6, runner);
}
}
const _abi$5 = [
{
inputs: [
{
internalType: "contract ENS",
name: "_ens",
type: "address"
},
{
internalType: "contract INameWrapper",
name: "wrapperAddress",
type: "address"
},
{
internalType: "address",
name: "_trustedETHController",
type: "address"
},
{
internalType: "address",
name: "_trustedReverseRegistrar",
type: "address"
}
],
stateMutability: "nonpayable",
type: "constructor"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: true,
internalType: "uint256",
name: "contentType",
type: "uint256"
}
],
name: "ABIChanged",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: false,
internalType: "address",
name: "a",
type: "address"
}
],
name: "AddrChanged",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: false,
internalType: "uint256",
name: "coinType",
type: "uint256"
},
{
indexed: false,
internalType: "bytes",
name: "newAddress",
type: "bytes"
}
],
name: "AddressChanged",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "owner",
type: "address"
},
{
indexed: true,
internalType: "address",
name: "operator",
type: "address"
},
{
indexed: false,
internalType: "bool",
name: "approved",
type: "bool"
}
],
name: "ApprovalForAll",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "address",
name: "owner",
type: "address"
},
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: true,
internalType: "address",
name: "delegate",
type: "address"
},
{
indexed: true,
internalType: "bool",
name: "approved",
type: "bool"
}
],
name: "Approved",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: false,
internalType: "bytes",
name: "hash",
type: "bytes"
}
],
name: "ContenthashChanged",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: false,
internalType: "bytes",
name: "name",
type: "bytes"
},
{
indexed: false,
internalType: "uint16",
name: "resource",
type: "uint16"
},
{
indexed: false,
internalType: "bytes",
name: "record",
type: "bytes"
}
],
name: "DNSRecordChanged",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: false,
internalType: "bytes",
name: "name",
type: "bytes"
},
{
indexed: false,
internalType: "uint16",
name: "resource",
type: "uint16"
}
],
name: "DNSRecordDeleted",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: false,
internalType: "bytes",
name: "lastzonehash",
type: "bytes"
},
{
indexed: false,
internalType: "bytes",
name: "zonehash",
type: "bytes"
}
],
name: "DNSZonehashChanged",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: true,
internalType: "bytes4",
name: "interfaceID",
type: "bytes4"
},
{
indexed: false,
internalType: "address",
name: "implementer",
type: "address"
}
],
name: "InterfaceChanged",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: false,
internalType: "string",
name: "name",
type: "string"
}
],
name: "NameChanged",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: false,
internalType: "bytes32",
name: "x",
type: "bytes32"
},
{
indexed: false,
internalType: "bytes32",
name: "y",
type: "bytes32"
}
],
name: "PubkeyChanged",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: true,
internalType: "string",
name: "indexedKey",
type: "string"
},
{
indexed: false,
internalType: "string",
name: "key",
type: "string"
},
{
indexed: false,
internalType: "string",
name: "value",
type: "string"
}
],
name: "TextChanged",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
indexed: false,
internalType: "uint64",
name: "newVersion",
type: "uint64"
}
],
name: "VersionChanged",
type: "event"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "uint256",
name: "contentTypes",
type: "uint256"
}
],
name: "ABI",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
},
{
internalType: "bytes",
name: "",
type: "bytes"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
}
],
name: "addr",
outputs: [
{
internalType: "address payable",
name: "",
type: "address"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "uint256",
name: "coinType",
type: "uint256"
}
],
name: "addr",
outputs: [
{
internalType: "bytes",
name: "",
type: "bytes"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "address",
name: "delegate",
type: "address"
},
{
internalType: "bool",
name: "approved",
type: "bool"
}
],
name: "approve",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
}
],
name: "clearRecords",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
}
],
name: "contenthash",
outputs: [
{
internalType: "bytes",
name: "",
type: "bytes"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "bytes32",
name: "name",
type: "bytes32"
},
{
internalType: "uint16",
name: "resource",
type: "uint16"
}
],
name: "dnsRecord",
outputs: [
{
internalType: "bytes",
name: "",
type: "bytes"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "bytes32",
name: "name",
type: "bytes32"
}
],
name: "hasDNSRecords",
outputs: [
{
internalType: "bool",
name: "",
type: "bool"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "bytes4",
name: "interfaceID",
type: "bytes4"
}
],
name: "interfaceImplementer",
outputs: [
{
internalType: "address",
name: "",
type: "address"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "owner",
type: "address"
},
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "address",
name: "delegate",
type: "address"
}
],
name: "isApprovedFor",
outputs: [
{
internalType: "bool",
name: "",
type: "bool"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "account",
type: "address"
},
{
internalType: "address",
name: "operator",
type: "address"
}
],
name: "isApprovedForAll",
outputs: [
{
internalType: "bool",
name: "",
type: "bool"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "bytes[]",
name: "data",
type: "bytes[]"
}
],
name: "multicall",
outputs: [
{
internalType: "bytes[]",
name: "results",
type: "bytes[]"
}
],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "nodehash",
type: "bytes32"
},
{
internalType: "bytes[]",
name: "data",
type: "bytes[]"
}
],
name: "multicallWithNodeCheck",
outputs: [
{
internalType: "bytes[]",
name: "results",
type: "bytes[]"
}
],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
}
],
name: "name",
outputs: [
{
internalType: "string",
name: "",
type: "string"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
}
],
name: "pubkey",
outputs: [
{
internalType: "bytes32",
name: "x",
type: "bytes32"
},
{
internalType: "bytes32",
name: "y",
type: "bytes32"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "",
type: "bytes32"
}
],
name: "recordVersions",
outputs: [
{
internalType: "uint64",
name: "",
type: "uint64"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "uint256",
name: "contentType",
type: "uint256"
},
{
internalType: "bytes",
name: "data",
type: "bytes"
}
],
name: "setABI",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "uint256",
name: "coinType",
type: "uint256"
},
{
internalType: "bytes",
name: "a",
type: "bytes"
}
],
name: "setAddr",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "address",
name: "a",
type: "address"
}
],
name: "setAddr",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "operator",
type: "address"
},
{
internalType: "bool",
name: "approved",
type: "bool"
}
],
name: "setApprovalForAll",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "bytes",
name: "hash",
type: "bytes"
}
],
name: "setContenthash",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "bytes",
name: "data",
type: "bytes"
}
],
name: "setDNSRecords",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "bytes4",
name: "interfaceID",
type: "bytes4"
},
{
internalType: "address",
name: "implementer",
type: "address"
}
],
name: "setInterface",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "string",
name: "newName",
type: "string"
}
],
name: "setName",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "bytes32",
name: "x",
type: "bytes32"
},
{
internalType: "bytes32",
name: "y",
type: "bytes32"
}
],
name: "setPubkey",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "string",
name: "key",
type: "string"
},
{
internalType: "string",
name: "value",
type: "string"
}
],
name: "setText",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "bytes",
name: "hash",
type: "bytes"
}
],
name: "setZonehash",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "bytes4",
name: "interfaceID",
type: "bytes4"
}
],
name: "supportsInterface",
outputs: [
{
internalType: "bool",
name: "",
type: "bool"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
},
{
internalType: "string",
name: "key",
type: "string"
}
],
name: "text",
outputs: [
{
internalType: "string",
name: "",
type: "string"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32"
}
],
name: "zonehash",
outputs: [
{
internalType: "bytes",
name: "",
type: "bytes"
}
],
stateMutability: "view",
type: "function"
}
];
class ENSResolver__factory {
static abi = _abi$5;
static createInterface() {
return new ethers.Interface(_abi$5);
}
static connect(address, runner) {
return new ethers.Contract(address, _abi$5, runner);
}
}
const _abi$4 = [
{
constant: true,
inputs: [],
name: "totalSupply",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: true,
inputs: [],
name: "_totalSupply",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: true,
inputs: [
{
internalType: "address",
name: "who",
type: "address"
}
],
name: "balanceOf",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: true,
inputs: [],
name: "name",
outputs: [
{
internalType: "string",
name: "",
type: "string"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: true,
inputs: [],
name: "symbol",
outputs: [
{
internalType: "string",
name: "",
type: "string"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: true,
inputs: [],
name: "decimals",
outputs: [
{
internalType: "uint8",
name: "",
type: "uint8"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "address",
name: "to",
type: "address"
},
{
internalType: "uint256",
name: "value",
type: "uint256"
}
],
name: "transfer",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "owner",
type: "address"
},
{
indexed: true,
internalType: "address",
name: "spender",
type: "address"
},
{
indexed: false,
internalType: "uint256",
name: "value",
type: "uint256"
}
],
name: "Approval",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "from",
type: "address"
},
{
indexed: true,
internalType: "address",
name: "to",
type: "address"
},
{
indexed: false,
internalType: "uint256",
name: "value",
type: "uint256"
}
],
name: "Transfer",
type: "event"
},
{
constant: true,
inputs: [
{
internalType: "address",
name: "owner",
type: "address"
},
{
internalType: "address",
name: "spender",
type: "address"
}
],
name: "allowance",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "address",
name: "from",
type: "address"
},
{
internalType: "address",
name: "to",
type: "address"
},
{
internalType: "uint256",
name: "value",
type: "uint256"
}
],
name: "transferFrom",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
constant: false,
inputs: [
{
internalType: "address",
name: "spender",
type: "address"
},
{
internalType: "uint256",
name: "value",
type: "uint256"
}
],
name: "approve",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "owner",
type: "address"
}
],
name: "nonces",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "owner",
type: "address"
},
{
internalType: "address",
name: "spender",
type: "address"
},
{
internalType: "uint256",
name: "amount",
type: "uint256"
},
{
internalType: "uint256",
name: "deadline",
type: "uint256"
},
{
internalType: "uint8",
name: "v",
type: "uint8"
},
{
internalType: "bytes32",
name: "r",
type: "bytes32"
},
{
internalType: "bytes32",
name: "s",
type: "bytes32"
}
],
name: "permit",
outputs: [],
stateMutability: "nonpayable",
type: "function"
}
];
class ERC20__factory {
static abi = _abi$4;
static createInterface() {
return new ethers.Interface(_abi$4);
}
static connect(address, runner) {
return new ethers.Contract(address, _abi$4, runner);
}
}
const _abi$3 = [
{
inputs: [
{
components: [
{
internalType: "address",
name: "target",
type: "address"
},
{
internalType: "bytes",
name: "callData",
type: "bytes"
}
],
internalType: "struct Multicall3.Call[]",
name: "calls",
type: "tuple[]"
}
],
name: "aggregate",
outputs: [
{
internalType: "uint256",
name: "blockNumber",
type: "uint256"
},
{
internalType: "bytes[]",
name: "returnData",
type: "bytes[]"
}
],
stateMutability: "payable",
type: "function"
},
{
inputs: [
{
components: [
{
internalType: "address",
name: "target",
type: "address"
},
{
internalType: "bool",
name: "allowFailure",
type: "bool"
},
{
internalType: "bytes",
name: "callData",
type: "bytes"
}
],
internalType: "struct Multicall3.Call3[]",
name: "calls",
type: "tuple[]"
}
],
name: "aggregate3",
outputs: [
{
components: [
{
internalType: "bool",
name: "success",
type: "bool"
},
{
internalType: "bytes",
name: "returnData",
type: "bytes"
}
],
internalType: "struct Multicall3.Result[]",
name: "returnData",
type: "tuple[]"
}
],
stateMutability: "payable",
type: "function"
},
{
inputs: [
{
components: [
{
internalType: "address",
name: "target",
type: "address"
},
{
internalType: "bool",
name: "allowFailure",
type: "bool"
},
{
internalType: "uint256",
name: "value",
type: "uint256"
},
{
internalType: "bytes",
name: "callData",
type: "bytes"
}
],
internalType: "struct Multicall3.Call3Value[]",
name: "calls",
type: "tuple[]"
}
],
name: "aggregate3Value",
outputs: [
{
components: [
{
internalType: "bool",
name: "success",
type: "bool"
},
{
internalType: "bytes",
name: "returnData",
type: "bytes"
}
],
internalType: "struct Multicall3.Result[]",
name: "returnData",
type: "tuple[]"
}
],
stateMutability: "payable",
type: "function"
},
{
inputs: [
{
components: [
{
internalType: "address",
name: "target",
type: "address"
},
{
internalType: "bytes",
name: "callData",
type: "bytes"
}
],
internalType: "struct Multicall3.Call[]",
name: "calls",
type: "tuple[]"
}
],
name: "blockAndAggregate",
outputs: [
{
internalType: "uint256",
name: "blockNumber",
type: "uint256"
},
{
internalType: "bytes32",
name: "blockHash",
type: "bytes32"
},
{
components: [
{
internalType: "bool",
name: "success",
type: "bool"
},
{
internalType: "bytes",
name: "returnData",
type: "bytes"
}
],
internalType: "struct Multicall3.Result[]",
name: "returnData",
type: "tuple[]"
}
],
stateMutability: "payable",
type: "function"
},
{
inputs: [],
name: "getBasefee",
outputs: [
{
internalType: "uint256",
name: "basefee",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "uint256",
name: "blockNumber",
type: "uint256"
}
],
name: "getBlockHash",
outputs: [
{
internalType: "bytes32",
name: "blockHash",
type: "bytes32"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "getBlockNumber",
outputs: [
{
internalType: "uint256",
name: "blockNumber",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "getChainId",
outputs: [
{
internalType: "uint256",
name: "chainid",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "getCurrentBlockCoinbase",
outputs: [
{
internalType: "address",
name: "coinbase",
type: "address"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "getCurrentBlockDifficulty",
outputs: [
{
internalType: "uint256",
name: "difficulty",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "getCurrentBlockGasLimit",
outputs: [
{
internalType: "uint256",
name: "gaslimit",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "getCurrentBlockTimestamp",
outputs: [
{
internalType: "uint256",
name: "timestamp",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "addr",
type: "address"
}
],
name: "getEthBalance",
outputs: [
{
internalType: "uint256",
name: "balance",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "getLastBlockHash",
outputs: [
{
internalType: "bytes32",
name: "blockHash",
type: "bytes32"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "bool",
name: "requireSuccess",
type: "bool"
},
{
components: [
{
internalType: "address",
name: "target",
type: "address"
},
{
internalType: "bytes",
name: "callData",
type: "bytes"
}
],
internalType: "struct Multicall3.Call[]",
name: "calls",
type: "tuple[]"
}
],
name: "tryAggregate",
outputs: [
{
components: [
{
internalType: "bool",
name: "success",
type: "bool"
},
{
internalType: "bytes",
name: "returnData",
type: "bytes"
}
],
internalType: "struct Multicall3.Result[]",
name: "returnData",
type: "tuple[]"
}
],
stateMutability: "payable",
type: "function"
},
{
inputs: [
{
internalType: "bool",
name: "requireSuccess",
type: "bool"
},
{
components: [
{
internalType: "address",
name: "target",
type: "address"
},
{
internalType: "bytes",
name: "callData",
type: "bytes"
}
],
internalType: "struct Multicall3.Call[]",
name: "calls",
type: "tuple[]"
}
],
name: "tryBlockAndAggregate",
outputs: [
{
internalType: "uint256",
name: "blockNumber",
type: "uint256"
},
{
internalType: "bytes32",
name: "blockHash",
type: "bytes32"
},
{
components: [
{
internalType: "bool",
name: "success",
type: "bool"
},
{
internalType: "bytes",
name: "returnData",
type: "bytes"
}
],
internalType: "struct Multicall3.Result[]",
name: "returnData",
type: "tuple[]"
}
],
stateMutability: "payable",
type: "function"
}
];
class Multicall__factory {
static abi = _abi$3;
static createInterface() {
return new ethers.Interface(_abi$3);
}
static connect(address, runner) {
return new ethers.Contract(address, _abi$3, runner);
}
}
const _abi$2 = [
{
inputs: [
{
internalType: "contract MultiWrapper",
name: "_multiWrapper",
type: "address"
},
{
internalType: "contract IOracle[]",
name: "existingOracles",
type: "address[]"
},
{
internalType: "enum OffchainOracle.OracleType[]",
name: "oracleTypes",
type: "uint8[]"
},
{
internalType: "contract IERC20[]",
name: "existingConnectors",
type: "address[]"
},
{
internalType: "contract IERC20",
name: "wBase",
type: "address"
},
{
internalType: "address",
name: "owner",
type: "address"
}
],
stateMutability: "nonpayable",
type: "constructor"
},
{
inputs: [],
name: "ArraysLengthMismatch",
type: "error"
},
{
inputs: [],
name: "ConnectorAlreadyAdded",
type: "error"
},
{
inputs: [],
name: "InvalidOracleTokenKind",
type: "error"
},
{
inputs: [],
name: "OracleAlreadyAdded",
type: "error"
},
{
inputs: [],
name: "SameTokens",
type: "error"
},
{
inputs: [],
name: "TooBigThreshold",
type: "error"
},
{
inputs: [],
name: "UnknownConnector",
type: "error"
},
{
inputs: [],
name: "UnknownOracle",
type: "error"
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "contract IERC20",
name: "connector",
type: "address"
}
],
name: "ConnectorAdded",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "contract IERC20",
name: "connector",
type: "address"
}
],
name: "ConnectorRemoved",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "contract MultiWrapper",
name: "multiWrapper",
type: "address"
}
],
name: "MultiWrapperUpdated",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "contract IOracle",
name: "oracle",
type: "address"
},
{
indexed: false,
internalType: "enum OffchainOracle.OracleType",
name: "oracleType",
type: "uint8"
}
],
name: "OracleAdded",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "contract IOracle",
name: "oracle",
type: "address"
},
{
indexed: false,
internalType: "enum OffchainOracle.OracleType",
name: "oracleType",
type: "uint8"
}
],
name: "OracleRemoved",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "previousOwner",
type: "address"
},
{
indexed: true,
internalType: "address",
name: "newOwner",
type: "address"
}
],
name: "OwnershipTransferred",
type: "event"
},
{
inputs: [
{
internalType: "contract IERC20",
name: "connector",
type: "address"
}
],
name: "addConnector",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "contract IOracle",
name: "oracle",
type: "address"
},
{
internalType: "enum OffchainOracle.OracleType",
name: "oracleKind",
type: "uint8"
}
],
name: "addOracle",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [],
name: "connectors",
outputs: [
{
internalType: "contract IERC20[]",
name: "allConnectors",
type: "address[]"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "contract IERC20",
name: "srcToken",
type: "address"
},
{
internalType: "contract IERC20",
name: "dstToken",
type: "address"
},
{
internalType: "bool",
name: "useWrappers",
type: "bool"
}
],
name: "getRate",
outputs: [
{
internalType: "uint256",
name: "weightedRate",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "contract IERC20",
name: "srcToken",
type: "address"
},
{
internalType: "bool",
name: "useSrcWrappers",
type: "bool"
}
],
name: "getRateToEth",
outputs: [
{
internalType: "uint256",
name: "weightedRate",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "contract IERC20",
name: "srcToken",
type: "address"
},
{
internalType: "bool",
name: "useSrcWrappers",
type: "bool"
},
{
internalType: "contract IERC20[]",
name: "customConnectors",
type: "address[]"
},
{
internalType: "uint256",
name: "thresholdFilter",
type: "uint256"
}
],
name: "getRateToEthWithCustomConnectors",
outputs: [
{
internalType: "uint256",
name: "weightedRate",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "contract IERC20",
name: "srcToken",
type: "address"
},
{
internalType: "bool",
name: "useSrcWrappers",
type: "bool"
},
{
internalType: "uint256",
name: "thresholdFilter",
type: "uint256"
}
],
name: "getRateToEthWithThreshold",
outputs: [
{
internalType: "uint256",
name: "weightedRate",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "contract IERC20",
name: "srcToken",
type: "address"
},
{
internalType: "contract IERC20",
name: "dstToken",
type: "address"
},
{
internalType: "bool",
name: "useWrappers",
type: "bool"
},
{
internalType: "contract IERC20[]",
name: "customConnectors",
type: "address[]"
},
{
internalType: "uint256",
name: "thresholdFilter",
type: "uint256"
}
],
name: "getRateWithCustomConnectors",
outputs: [
{
internalType: "uint256",
name: "weightedRate",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "contract IERC20",
name: "srcToken",
type: "address"
},
{
internalType: "contract IERC20",
name: "dstToken",
type: "address"
},
{
internalType: "bool",
name: "useWrappers",
type: "bool"
},
{
internalType: "uint256",
name: "thresholdFilter",
type: "uint256"
}
],
name: "getRateWithThreshold",
outputs: [
{
internalType: "uint256",
name: "weightedRate",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "multiWrapper",
outputs: [
{
internalType: "contract MultiWrapper",
name: "",
type: "address"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "oracles",
outputs: [
{
internalType: "contract IOracle[]",
name: "allOracles",
type: "address[]"
},
{
internalType: "enum OffchainOracle.OracleType[]",
name: "oracleTypes",
type: "uint8[]"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "owner",
outputs: [
{
internalType: "address",
name: "",
type: "address"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "contract IERC20",
name: "connector",
type: "address"
}
],
name: "removeConnector",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "contract IOracle",
name: "oracle",
type: "address"
},
{
internalType: "enum OffchainOracle.OracleType",
name: "oracleKind",
type: "uint8"
}
],
name: "removeOracle",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [],
name: "renounceOwnership",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "contract MultiWrapper",
name: "_multiWrapper",
type: "address"
}
],
name: "setMultiWrapper",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "newOwner",
type: "address"
}
],
name: "transferOwnership",
outputs: [],
stateMutability: "nonpayable",
type: "function"
}
];
class OffchainOracle__factory {
static abi = _abi$2;
static createInterface() {
return new ethers.Interface(_abi$2);
}
static connect(address, runner) {
return new ethers.Contract(address, _abi$2, runner);
}
}
const _abi$1 = [
{
inputs: [
{
internalType: "address",
name: "_owner",
type: "address"
}
],
stateMutability: "nonpayable",
type: "constructor"
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "uint256",
name: "",
type: "uint256"
}
],
name: "DecimalsUpdated",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "uint256",
name: "",
type: "uint256"
}
],
name: "GasPriceUpdated",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "uint256",
name: "",
type: "uint256"
}
],
name: "L1BaseFeeUpdated",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "uint256",
name: "",
type: "uint256"
}
],
name: "OverheadUpdated",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "previousOwner",
type: "address"
},
{
indexed: true,
internalType: "address",
name: "newOwner",
type: "address"
}
],
name: "OwnershipTransferred",
type: "event"
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "uint256",
name: "",
type: "uint256"
}
],
name: "ScalarUpdated",
type: "event"
},
{
inputs: [],
name: "decimals",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "gasPrice",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "bytes",
name: "_data",
type: "bytes"
}
],
name: "getL1Fee",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "bytes",
name: "_data",
type: "bytes"
}
],
name: "getL1GasUsed",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "l1BaseFee",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "overhead",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "owner",
outputs: [
{
internalType: "address",
name: "",
type: "address"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [],
name: "renounceOwnership",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [],
name: "scalar",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256"
}
],
stateMutability: "view",
type: "function"
},
{
inputs: [
{
internalType: "uint256",
name: "_decimals",
type: "uint256"
}
],
name: "setDecimals",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "uint256",
name: "_gasPrice",
type: "uint256"
}
],
name: "setGasPrice",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "uint256",
name: "_baseFee",
type: "uint256"
}
],
name: "setL1BaseFee",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "uint256",
name: "_overhead",
type: "uint256"
}
],
name: "setOverhead",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "uint256",
name: "_scalar",
type: "uint256"
}
],
name: "setScalar",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{
internalType: "address",
name: "newOwner",
type: "address"
}
],
name: "transferOwnership",
outputs: [],
stateMutability: "nonpayable",
type: "function"
}
];
class OvmGasPriceOracle__factory {
static abi = _abi$1;
static createInterface() {
return new ethers.Interface(_abi$1);
}
static connect(address, runner) {
return new ethers.Contract(address, _abi$1, runner);
}
}
const _abi = [
{
inputs: [
{
internalType: "contract ENS",
name: "_ens",
type: "address"
}
],
stateMutability: "nonpayable",
type: "constructor"
},
{
inputs: [
{
internalType: "address[]",
name: "addresses",
type: "address[]"
}
],
name: "getNames",
outputs: [
{
internalType: "string[]",
name: "r",
type: "string[]"
}
],
stateMutability: "view",
type: "function"
}
];
class ReverseRecords__factory {
static abi = _abi;
static createInterface() {
return new ethers.Interface(_abi);
}
static connect(address, runner) {
return new ethers.Contract(address, _abi, runner);
}
}
var index = /*#__PURE__*/Object.freeze({
__proto__: null,
ENSNameWrapper__factory: ENSNameWrapper__factory,
ENSRegistry__factory: ENSRegistry__factory,
ENSResolver__factory: ENSResolver__factory,
ENS__factory: ENS__factory,
ERC20__factory: ERC20__factory,
Multicall__factory: Multicall__factory,
OffchainOracle__factory: OffchainOracle__factory,
OvmGasPriceOracle__factory: OvmGasPriceOracle__factory,
ReverseRecords__factory: ReverseRecords__factory
});
class Pedersen {
pedersenHash;
babyJub;
pedersenPromise;
constructor() {
this.pedersenPromise = this.initPedersen();
}
async initPedersen() {
this.pedersenHash = await circomlibjs.buildPedersenHash();
this.babyJub = this.pedersenHash.babyJub;
}
async unpackPoint(buffer) {
await this.pedersenPromise;
return this.babyJub?.unpackPoint(this.pedersenHash?.hash(buffer));
}
toStringBuffer(buffer) {
return this.babyJub?.F.toString(buffer);
}
}
const pedersen = new Pedersen();
async function buffPedersenHash(buffer) {
const [hash] = await pedersen.unpackPoint(buffer);
return pedersen.toStringBuffer(hash);
}
function parseNote(noteString) {
const noteRegex = /tornado-(?<currency>\w+)-(?<amount>[\d.]+)-(?<netId>\d+)-0x(?<noteHex>[0-9a-fA-F]{124})/g;
const match = noteRegex.exec(noteString);
if (!match) {
return;
}
const { currency, amount, netId, noteHex } = match.groups;
return {
currency: currency.toLowerCase(),
amount,
netId: Number(netId),
noteHex: "0x" + noteHex,
note: noteString
};
}
function parseInvoice(invoiceString) {
const invoiceRegex = /tornadoInvoice-(?<currency>\w+)-(?<amount>[\d.]+)-(?<netId>\d+)-0x(?<commitmentHex>[0-9a-fA-F]{64})/g;
const match = invoiceRegex.exec(invoiceString);
if (!match) {
return;
}
const { currency, amount, netId, commitmentHex } = match.groups;
return {
currency: currency.toLowerCase(),
amount,
netId: Number(netId),
commitmentHex: "0x" + commitmentHex,
invoice: invoiceString
};
}
async function createDeposit({ nullifier, secret }) {
const preimage = new Uint8Array([...leInt2Buff(nullifier), ...leInt2Buff(secret)]);
const noteHex = toFixedHex(bytesToBN(preimage), 62);
const commitment = BigInt(await buffPedersenHash(preimage));
const commitmentHex = toFixedHex(commitment);
const nullifierHash = BigInt(await buffPedersenHash(leInt2Buff(nullifier)));
const nullifierHex = toFixedHex(nullifierHash);
return {
preimage,
noteHex,
commitment,
commitmentHex,
nullifierHash,
nullifierHex
};
}
class Deposit {
currency;
amount;
netId;
nullifier;
secret;
note;
noteHex;
invoice;
commitmentHex;
nullifierHex;
constructor({
currency,
amount,
netId,
nullifier,
secret,
note,
noteHex,
invoice,
commitmentHex,
nullifierHex
}) {
this.currency = currency;
this.amount = amount;
this.netId = netId;
this.nullifier = nullifier;
this.secret = secret;
this.note = note;
this.noteHex = noteHex;
this.invoice = invoice;
this.commitmentHex = commitmentHex;
this.nullifierHex = nullifierHex;
}
toString() {
return JSON.stringify(
{
currency: this.currency,
amount: this.amount,
netId: this.netId,
nullifier: this.nullifier,
secret: this.secret,
note: this.note,
noteHex: this.noteHex,
invoice: this.invoice,
commitmentHex: this.commitmentHex,
nullifierHex: this.nullifierHex
},
null,
2
);
}
static async createNote({ currency, amount, netId, nullifier, secret }) {
if (!nullifier) {
nullifier = rBigInt(31);
}
if (!secret) {
secret = rBigInt(31);
}
const depositObject = await createDeposit({
nullifier,
secret
});
const newDeposit = new Deposit({
currency: currency.toLowerCase(),
amount,
netId,
note: `tornado-${currency.toLowerCase()}-${amount}-${netId}-${depositObject.noteHex}`,
noteHex: depositObject.noteHex,
invoice: `tornadoInvoice-${currency.toLowerCase()}-${amount}-${netId}-${depositObject.commitmentHex}`,
nullifier,
secret,
commitmentHex: depositObject.commitmentHex,
nullifierHex: depositObject.nullifierHex
});
return newDeposit;
}
static async parseNote(noteString) {
const parsedNote = parseNote(noteString);
if (!parsedNote) {
throw new Error("The note has invalid format");
}
const { currency, amount, netId, note, noteHex: parsedNoteHex } = parsedNote;
const bytes = bnToBytes(parsedNoteHex);
const nullifier = BigInt(leBuff2Int(bytes.slice(0, 31)).toString());
const secret = BigInt(leBuff2Int(bytes.slice(31, 62)).toString());
const { noteHex, commitmentHex, nullifierHex } = await createDeposit({
nullifier,
secret
});
const invoice = `tornadoInvoice-${currency}-${amount}-${netId}-${commitmentHex}`;
const newDeposit = new Deposit({
currency,
amount,
netId,
note,
noteHex,
invoice,
nullifier,
secret,
commitmentHex,
nullifierHex
});
return newDeposit;
}
}
class Invoice {
currency;
amount;
netId;
commitmentHex;
invoice;
constructor(invoiceString) {
const parsedInvoice = parseInvoice(invoiceString);
if (!parsedInvoice) {
throw new Error("The invoice has invalid format");
}
const { currency, amount, netId, invoice, commitmentHex } = parsedInvoice;
this.currency = currency;
this.amount = amount;
this.netId = netId;
this.commitmentHex = commitmentHex;
this.invoice = invoice;
}
toString() {
return JSON.stringify(
{
currency: this.currency,
amount: this.amount,
netId: this.netId,
commitmentHex: this.commitmentHex,
invoice: this.invoice
},
null,
2
);
}
}
function packEncryptedMessage({ nonce, ephemPublicKey, ciphertext }) {
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);
}
function unpackEncryptedMessage(encryptedMessage) {
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
};
}
class NoteAccount {
blockNumber;
// 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;
// Address derived from recoveryKey, only used for frontend UI
recoveryAddress;
// Note encryption public key derived from recoveryKey
recoveryPublicKey;
constructor({ blockNumber, recoveryKey }) {
if (!recoveryKey) {
recoveryKey = rHex(32).slice(2);
}
this.blockNumber = blockNumber;
this.recoveryKey = recoveryKey;
this.recoveryAddress = ethers.computeAddress("0x" + recoveryKey);
this.recoveryPublicKey = ethSigUtil.getEncryptionPublicKey(recoveryKey);
}
/**
* Intends to mock eth_getEncryptionPublicKey behavior from MetaMask
* In order to make the recoveryKey retrival from Echoer possible from the bare private key
*/
static async getSignerPublicKey(signer) {
if (signer.privateKey) {
const wallet = signer;
const privateKey = wallet.privateKey.slice(0, 2) === "0x" ? wallet.privateKey.slice(2) : wallet.privateKey;
return ethSigUtil.getEncryptionPublicKey(privateKey);
}
const provider = signer.provider;
return await provider.send("eth_getEncryptionPublicKey", [
signer.address
]);
}
// 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) {
const encryptedData = ethSigUtil.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
*/
static async decryptSignerNoteAccounts(signer, events) {
const signerAddress = signer.address;
const decryptedEvents = [];
for (const event of events) {
if (event.address !== signerAddress) {
continue;
}
try {
const unpackedMessage = unpackEncryptedMessage(event.encryptedAccount);
let recoveryKey;
if (signer.privateKey) {
const wallet = signer;
const privateKey = wallet.privateKey.slice(0, 2) === "0x" ? wallet.privateKey.slice(2) : wallet.privateKey;
recoveryKey = ethSigUtil.decrypt({
encryptedData: unpackedMessage,
privateKey
});
} else {
const { version, nonce, ephemPublicKey, ciphertext } = unpackedMessage;
const unpackedBuffer = bytesToHex(
new TextEncoder().encode(
JSON.stringify({
version,
nonce,
ephemPublicKey,
ciphertext
})
)
);
const provider = signer.provider;
recoveryKey = await provider.send("eth_decrypt", [unpackedBuffer, signerAddress]);
}
decryptedEvents.push(
new NoteAccount({
blockNumber: event.blockNumber,
recoveryKey
})
);
} catch {
continue;
}
}
return decryptedEvents;
}
decryptNotes(events) {
const decryptedEvents = [];
for (const event of events) {
try {
const unpackedMessage = unpackEncryptedMessage(event.encryptedNote);
const [address, noteHex] = ethSigUtil.decrypt({
encryptedData: unpackedMessage,
privateKey: this.recoveryKey
}).split("-");
decryptedEvents.push({
blockNumber: event.blockNumber,
address: ethers.getAddress(address),
noteHex
});
} catch {
continue;
}
}
return decryptedEvents;
}
encryptNote({ address, noteHex }) {
const encryptedData = ethSigUtil.encrypt({
publicKey: this.recoveryPublicKey,
data: `${address}-${noteHex}`,
version: "x25519-xsalsa20-poly1305"
});
return packEncryptedMessage(encryptedData);
}
}
function encodedLabelToLabelhash(label) {
if (label.length !== 66) return null;
if (label.indexOf("[") !== 0) return null;
if (label.indexOf("]") !== 65) return null;
const hash = `0x${label.slice(1, 65)}`;
if (!isHex(hash)) return null;
return hash;
}
function labelhash(label) {
if (!label) {
return bytesToHex(new Uint8Array(32).fill(0));
}
return encodedLabelToLabelhash(label) || ethers.keccak256(new TextEncoder().encode(label));
}
function makeLabelNodeAndParent(name) {
const labels = name.split(".");
const label = labels.shift();
const parentNode = ethers.namehash(labels.join("."));
return {
label,
labelhash: labelhash(label),
parentNode
};
}
const EnsContracts = {
[NetId.MAINNET]: {
ensRegistry: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e",
ensPublicResolver: "0x231b0Ee14048e9dCcD1d247744d114a4EB5E8E63",
ensNameWrapper: "0xD4416b13d2b3a9aBae7AcD5D6C2BbDBE25686401"
},
[NetId.SEPOLIA]: {
ensRegistry: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e",
ensPublicResolver: "0x8FADE66B79cC9f707aB26799354482EB93a5B7dD",
ensNameWrapper: "0x0635513f179D50A207757E05759CbD106d7dFcE8"
}
};
class ENSUtils {
ENSRegistry;
ENSResolver;
ENSNameWrapper;
provider;
constructor(provider) {
this.provider = provider;
}
async getContracts() {
const { chainId } = await this.provider.getNetwork();
const { ensRegistry, ensPublicResolver, ensNameWrapper } = EnsContracts[Number(chainId)];
this.ENSRegistry = ENSRegistry__factory.connect(ensRegistry, this.provider);
this.ENSResolver = ENSResolver__factory.connect(ensPublicResolver, this.provider);
this.ENSNameWrapper = ENSNameWrapper__factory.connect(ensNameWrapper, this.provider);
}
async getOwner(name) {
if (!this.ENSRegistry) {
await this.getContracts();
}
return this.ENSRegistry.owner(ethers.namehash(name));
}
// nameWrapper connected with wallet signer
async unwrap(signer, name) {
if (!this.ENSNameWrapper) {
await this.getContracts();
}
const owner = signer.address;
const nameWrapper = this.ENSNameWrapper.connect(signer);
const { labelhash: labelhash2 } = makeLabelNodeAndParent(name);
return nameWrapper.unwrapETH2LD(labelhash2, owner, owner);
}
// https://github.com/ensdomains/ensjs/blob/main/packages/ensjs/src/functions/wallet/createSubname.ts
async setSubnodeRecord(signer, name) {
if (!this.ENSResolver) {
await this.getContracts();
}
const resolver = this.ENSResolver;
const registry = this.ENSRegistry.connect(signer);
const owner = signer.address;
const { labelhash: labelhash2, parentNode } = makeLabelNodeAndParent(name);
return registry.setSubnodeRecord(parentNode, labelhash2, owner, resolver.target, BigInt(0));
}
getResolver(name) {
return ethers.EnsResolver.fromName(this.provider, name);
}
async getText(name, key) {
const resolver = await this.getResolver(name);
if (!resolver) {
return resolver;
}
return await resolver.getText(key) || "";
}
// https://github.com/ensdomains/ensjs/blob/main/packages/ensjs/src/functions/wallet/setTextRecord.ts
async setText(signer, name, key, value) {
const resolver = ENSResolver__factory.connect((await this.getResolver(name))?.address, signer);
return resolver.setText(ethers.namehash(name), key, value);
}
}
const DUMMY_ADDRESS = "0x1111111111111111111111111111111111111111";
const DUMMY_NONCE = 1024;
const DUMMY_WITHDRAW_DATA = "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111";
function convertETHToTokenAmount(amountInWei, tokenPriceInWei, tokenDecimals = 18) {
const tokenDecimalsMultiplier = BigInt(10 ** Number(tokenDecimals));
return BigInt(amountInWei) * tokenDecimalsMultiplier / BigInt(tokenPriceInWei);
}
class TornadoFeeOracle {
provider;
ovmGasPriceOracle;
constructor(provider, ovmGasPriceOracle) {
this.provider = provider;
if (ovmGasPriceOracle) {
this.ovmGasPriceOracle = ovmGasPriceOracle;
}
}
/**
* Calculates Gas Price
* We apply 50% premium of EIP-1559 network fees instead of 100% from ethers.js
* (This should cover up to 4 full blocks which is equivalent of minute)
* (A single block can bump 12.5% of fees, see the methodology https://hackmd.io/@tvanepps/1559-wallets)
* (Still it is recommended to use 100% premium for sending transactions to prevent stucking it)
*/
async gasPrice() {
const [block, getGasPrice, getPriorityFee] = await Promise.all([
this.provider.getBlock("latest"),
(async () => {
try {
return BigInt(await this.provider.send("eth_gasPrice", []));
} catch {
return ethers.parseUnits("1", "gwei");
}
})(),
(async () => {
try {
return BigInt(await this.provider.send("eth_maxPriorityFeePerGas", []));
} catch {
return BigInt(0);
}
})()
]);
return block?.baseFeePerGas ? block.baseFeePerGas * BigInt(15) / BigInt(10) + getPriorityFee : getGasPrice;
}
/**
* Calculate L1 fee for op-stack chains
*
* This is required since relayers would pay the full transaction fees for users
*/
fetchL1OptimismFee(tx) {
if (!this.ovmGasPriceOracle) {
return new Promise((resolve) => resolve(BigInt(0)));
}
if (!tx) {
tx = {
type: 0,
gasLimit: 1e6,
nonce: DUMMY_NONCE,
data: DUMMY_WITHDRAW_DATA,
gasPrice: ethers.parseUnits("1", "gwei"),
to: DUMMY_ADDRESS
};
}
return this.ovmGasPriceOracle.getL1Fee.staticCall(ethers.Transaction.from(tx).unsignedSerialized);
}
/**
* We don't need to distinguish default refunds by tokens since most users interact with other defi protocols after withdrawal
* So we default with 1M gas which is enough for two or three swaps
* Using 30 gwei for default but it is recommended to supply cached gasPrice value from the UI
*/
defaultEthRefund(gasPrice, gasLimit) {
return (gasPrice ? BigInt(gasPrice) : ethers.parseUnits("30", "gwei")) * BigInt(gasLimit || 1e6);
}
/**
* Calculates token amount for required ethRefund purchases required to calculate fees
*/
calculateTokenAmount(ethRefund, tokenPriceInEth, tokenDecimals) {
return convertETHToTokenAmount(ethRefund, tokenPriceInEth, tokenDecimals);
}
/**
* Warning: For tokens you need to check if the fees are above denomination
* (Usually happens for small denomination pool or if the gas price is high)
*/
calculateRelayerFee({
gasPrice,
gasLimit = 6e5,
l1Fee = 0,
denomination,
ethRefund = BigInt(0),
tokenPriceInWei,
tokenDecimals = 18,
relayerFeePercent = 0.33,
isEth = true,
premiumPercent = 20
}) {
const gasCosts = BigInt(gasPrice) * BigInt(gasLimit) + BigInt(l1Fee);
const relayerFee = BigInt(denomination) * BigInt(Math.floor(1e4 * relayerFeePercent)) / BigInt(1e4 * 100);
if (isEth) {
return (gasCosts + relayerFee) * BigInt(premiumPercent ? 100 + premiumPercent : 100) / BigInt(100);
}
const feeInEth = gasCosts + BigInt(ethRefund);
return (convertETHToTokenAmount(feeInEth, tokenPriceInWei, tokenDecimals) + relayerFee) * BigInt(premiumPercent ? 100 + premiumPercent : 100) / BigInt(100);
}
}
const gasZipInbounds = {
[NetId.MAINNET]: "0x391E7C679d29bD940d63be94AD22A25d25b5A604",
[NetId.BSC]: "0x391E7C679d29bD940d63be94AD22A25d25b5A604",
[NetId.POLYGON]: "0x391E7C679d29bD940d63be94AD22A25d25b5A604",
[NetId.OPTIMISM]: "0x391E7C679d29bD940d63be94AD22A25d25b5A604",
[NetId.ARBITRUM]: "0x391E7C679d29bD940d63be94AD22A25d25b5A604",
[NetId.GNOSIS]: "0x391E7C679d29bD940d63be94AD22A25d25b5A604",
[NetId.AVALANCHE]: "0x391E7C679d29bD940d63be94AD22A25d25b5A604"
};
const gasZipID = {
[NetId.MAINNET]: 255,
[NetId.BSC]: 14,
[NetId.POLYGON]: 17,
[NetId.OPTIMISM]: 55,
[NetId.ARBITRUM]: 57,
[NetId.GNOSIS]: 16,
[NetId.AVALANCHE]: 15,
[NetId.SEPOLIA]: 102
};
function gasZipInput(to, shorts) {
let data = "0x";
if (ethers.isAddress(to)) {
if (to.length === 42) {
data += "02";
data += to.slice(2);
} else {
return null;
}
} else {
data += "01";
}
for (const i in shorts) {
data += Number(shorts[i]).toString(16).padStart(4, "0");
}
return data;
}
function gasZipMinMax(ethUsd) {
return {
min: 1 / ethUsd,
max: 50 / ethUsd,
ethUsd
};
}
const hasherBytecode = "0x38600c600039612b1b6000f3606460006000377c01000000000000000000000000000000000000000000000000000000006000510463f47d33b5146200003557fe5b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016004518160245181838180828009800909089082827f0fbe43c36a80e36d7c7c584d4f8f3759fb51f0d66065d8a227b688d12488c5d408839081808280098009098391089082827f9c48cd3e00a6195a253fc009e60f249456f802ff9baf6549210d201321efc1cc08839081808280098009098391089082827f27c0849dba2643077c13eb42ffb97663cdcecd669bf10f756be30bab71b86cf808839081808280098009098391089082827f2bf76744736132e5c68f7dfdd5b792681d415098554fd8280f00d11b172b80d208839081808280098009098391089082827f33133eb4a1a1ab45037c8bdf9adbb2999baf06f20a9c95180dc4ccdcbec5856808839081808280098009098391089082827f588bb66012356dbc9b059ef1d792b563d6c18624dddecc3fe4583fd3551e9b3008839081808280098009098391089082827f71bc3e244e1b92911fe7f53cf523e491fd6ff487d59337a1d92f92668c4f4c3608839081808280098009098391089082827fd1808e2b039fd010c489768f78d7499938ccc0858f3295151787cfe8b7e40be108839081808280098009098391089082827f76978af3ded437cf41b3faa40cd6bcfce94f27f4abcc3ed34be19abd2c4537d008839081808280098009098391089082827f0a9baee798a320b0ca5b1cf888386d1dc12c13b38e10225aa4e9f03069a099f508839081808280098009098391089082827fb79dbf6050a03b16c3ade8d77e11c767d2251af9cdbd6cdf9a8a0ee921b32c7908839081808280098009098391089082827fa74bbcf5067f067faec2cce4b98d130d7927456f5c5f6c00e0f5406a24eb8b1908839081808280098009098391089082827fab7ab080d4c4018bda6ecc8bd67468bc4619ba12f25b0da879a639c758c8855d08839081808280098009098391089082827fe6a5b797c2bba7e9a873b37f5c41adc47765e9be4a1f0e0650e6a24ad226876308839081808280098009098391089082827f6270ae87cf3d82cf9c0b5f428466c429d7b7cbe234cecff39969171af006016c08839081808280098009098391089082827f9951c9f6e76d636b52f7600d979ca9f3b643dfbe9551c83b31542830321b2a6608839081808280098009098391089082827f4119469e44229cc40c4ff555a2b6f6b39961088e741e3c20a3c9b47f130c555008839081808280098009098391089082827f5d795e02bbaf90ff1f384741e5f18f8b644a0080441315d0e5b3c8123452a0b008839081808280098009098391089082827f281e90a515e6409e9177b4f297f8049ce3d4c3659423c48b3fd64e83596ff10108839081808280098009098391089082827f424185c60a21e84970f7d32cacaa2725aa8a844caea7ed760d2b965af1bf3e7d08839081808280098009098391089082827fd96fcbc3960614ea887da609187a5dada2e1b829f23309a6375212cea1f25c0908839081808280098009098391089082827ffde84026d7c294300af18f7712fc3662f43387ae8cf7fdda1f9a810f4b24bcf208839081808280098009098391089082827f3a9d568575846aa6b8a890b3c237fd0447426db878e6e25333b8eb9b386195c108839081808280098009098391089082827f55a2aa32c84a4cae196dd4094b685dd11757470a3be094d98eea73f02452aa3608839081808280098009098391089082827fcbc9481380978d29ebc5b0a8d4481cd2ef654ee800907adb3d38dc2fd9265fab08839081808280098009098391089082827f24e53af71ef06bacb76d3294c11223911e9d177ff09b7009febc484add0beb7408839081808280098009098391089082827fdbd44e16108225766dac3e5fe7acbe9df519bbba97380e5e9437a90658f2139308839081808280098009098391089082827fc6f434863c79013bb2c202331e04bccea2251c1ff6f191dc2afa23e6f6d28e4e08839081808280098009098391089082827f3490eeb39a733c0e8062d87f981ae65a8fccf25c448f4455d27db3915351b06608839081808280098009098391089082827f30b89830ff7ade3558a5361a24869130ce1fcce97211602962e34859525dac4f08839081808280098009098391089082827f29bae21b579d080a75c1694da628d0ecfd83efc9c8468704f410300062f64ca908839081808280098009098391089082827fe326499de0476e719915dd1c661ef4550723d4aee9ee9af224edd208790fce4408839081808280098009098391089082827f8c45208b8baa6f473821415957088c0b7e72a465f460b09ece2d270aee2f184108839081808280098009098391089082827ffe2ad454f451348f26ce2cc7e7914aef3eb96e8f89a4619a1dc7d11f8401c35208839081808280098009098391089082827f0929db368ef2af2d29bca38845325b0b7a820a4889e44b5829bbe1ed47fd4d5208839081808280098009098391089082827f16531d424b0cbaf9abbf2d2acde698462ea4555bf32ccf1bbd26697e905066f608839081808280098009098391089082827ff5c30d247f045ff6d05cf0dd0a49c9823e7a24b0d751d3c721353b96f29d76f608839081808280098009098391089082827f6eb7a3614056c230c6f171370fdd9d1048bb00b2cdd1b2721d11bdda5023f48608839081808280098009098391089082827f0ee9c4621642a272f710908707557498d25a6fdd51866da5d9f0d205355a618908839081808280098009098391089082827f78ca1cb1c7f6c6894d1cf94f327b8763be173151b6b06f99dfc6a944bb5a72f008839081808280098009098391089082827f5d24d0b1b304d05311ce0f274b0d93746a4860ed5cdd8d4348de557ea7a5ee7a08839081808280098009098391089082827f77423dabd1a3cddc8691438fc5891e3fd49ac0f3e21aaf249791bfde1303d2f308839081808280098009098391089082827f0642e8800a48cc04c0168232c6f542396597a67cf395ad622d947e98bb68697a08839081808280098009098391089082827fc1e7d3cbbc4c35b7490647d8402e56d334336943bda91fe2d34ca9727c0e3df508839081808280098009098391089082827f8d6fb1730335204f38f85e408ac861e76f24349ab6ee0469c22e19350bb24fe108839081808280098009098391089082827f67d0faf5f0db32a1b60e13dc4914246b9edac7990fb4990b19aa86815586441a08839081808280098009098391089082827f2605b9b909ded1b04971eae979027c4e0de57f3b6a60d5ed58aba619c34749ce08839081808280098009098391089082827fd276890b2c205db85f000d1f5111ed8f177e279cae3e52862780f04e846228d008839081808280098009098391089082827f2ac5905f9450a21ef6905ed5951a91b3730e3a2e2d62b50bdeb810015d50376b08839081808280098009098391089082827f7a366839f0291ca54da674ac3f0e1e9aa8b687ba533926cb40268039e57b967a08839081808280098009098391089082827f67ab0f3466989c3dbbe209c37ec272ba83984ba6e445be6d472b63e3ca7270e308839081808280098009098391089082827f0e786007d0ce7e28a90e31d3263887d40c556dec88fcb8b56bc9e9c05ecc0c2908839081808280098009098391089082827f0b814ed99bd00eca389b0022663dbfddfbfa15e321c19abcf1eaf9556075fb6808839081808280098009098391089082827f65c0321ba26fcee4fdc35b4999b78ceb54dcaf9fec2e3bdea98e9f82925c093208839081808280098009098391089082827fab2d2a929601f9c3520e0b14aaa6ba9f1e79821a5b768919670a4ea970722bf408839081808280098009098391089082827fcdd2e0744d4af1a81918de69ec12128a5871367303ff83ed764771cbbdf6502308839081808280098009098391089082827f74527d0c0868f2ec628086b874fa66a7347d3d3b918d2e07a5f33e1067e8ac5808839081808280098009098391089082827f1c6bf6ac0314caead23e357bfcbbaa17d670672ae3a475f80934c716f10aca2508839081808280098009098391089082827f3c4007e286f8dc7efd5d0eeb0e95d7aa6589361d128a0cccb17b554c851a643208839081808280098009098391089082827fae468a86a5a7db7c763a053eb09ac1a02809ce095258c88101ee319e12b0697e08839081808280098009098391089082827f9333e3d052b7c77fcac1eb366f610f6f97852242b1317a87b80f3bbc5c8c2d1d08839081808280098009098391089082827f52ec1d675cf5353153f6b628414783ca6b7fc0fe01948ca206daad712296e39508839081808280098009098391089082827f13ceeeb301572b4991076750e11ea7e7fcbfee454d90dc1763989004a1894f9308839081808280098009098391089082827f8505737e7e94939a08d8cda10b6fbbbf879b2141ae7eabc30fcd22405135fe6408839081808280098009098391089082827f6127db7ac5200a212092b66ec2bfc63653f4dc8ac66c76008fef885258a258b508839081808280098009098391089082827f12692a7d808f44e31d628dbcfea377eb073fb918d7beb8136ea47f8cf094c88c08839081808280098009098391089082827f260e384b1268e3a347c91d6987fd280fa0a275541a7c5be34bf126af35c962e008839081808280098009098391089082827fd88c3b01966d90e713aee8d482ceaa6925311d2342e1a5aca4fcd2f44b6daddc08839081808280098009098391089082827fb87e868affd91b078a87fa75ac9332a6cf23587d94e20c3262db5e91f30bf04b08839081808280098009098391089082827fb5ba5f8acad1a950a3bbf2201055cd3ea27056c0c53f0c4c97f33cda8dbfe90908839081808280098009098391089082827f59ca814b49e00d7b3118c53a2986ded128584acd7428735e08ade6661c457f7508839081808280098009098391089082827f0fc4c0bea813a223fd510c07f7bbe337badd4bcf28649a0d378970c2a15b3aa508839081808280098009098391089082827f0053f1ea6dd60e7a6db09a00be77549ff3d4ee3737be7fb42052ae1321f667c308839081808280098009098391089082827feb937077bb10c8fe38716d4e38edc1f9e7b18c6414fef85fe7e9c5567baa4a0408839081808280098009098391089082827fbacb14c0f1508d828f7fd048d716b8044aec7f0fb48e85e717bf532db972520708839081808280098009098391089082827f4ca0abb8beb7cff572a0c1e6f58e080e1bb243d497a3e74538442a4555ad40be08839081808280098009098391089082827fda9eefd411e590d7e44592cce298af87b2c62aa3cc8bb137aa99ca8d4aa551b508839081808280098009098391089082827f153dae43cef763e7a2fc9846f09a2973b0ad9c35894c220699bcc2954501c6bd08839081808280098009098391089082827fd4ed2a09375813b4fb504c7a9ba13110bdd8549a47349db82c15a434c090e87b08839081808280098009098391089082827f0063a5c4c9c12dcf4bae72c69f3a225664469503d61d9eae5d9553bfb006095b08839081808280098009098391089082827fdc8a4d35ad28e59dd3713b45985cd3b70e37ccc2be42086f1ea078fe2dc9d82d08839081808280098009098391089082827f486ba219308f0c847b22fcb4449f8855192536c01b8057904e81c1c7814f483b08839081808280098009098391089082827f34d9604140a1ac9fdb204285b9fe1b303c281af2fc5fb362f6577282b423bcf308839081808280098009098391089082827fc1681959ec4bc3656911db2b2f56aa4db709c26f1a0a25c879286e37f437465d08839081808280098009098391089082827ffcd849f3b5f9e4368af75619fb27f2e335adbb9b44988f17c4d389fa751ad47a08839081808280098009098391089082827ff5f7fc22ad64c8e7c1e005110e13f4f1c6b1f8f8cc59000db0e3bb38f99554a508839081808280098009098391089082827fa9133b8a20fbae4633ec5f82cb47a38ae1877d12d1febb23982c7c808aa5317508839081808280098009098391089082827ff4827c5c7b61141cc31b75984bb3ed16ed579e5b72e32a1289b63ab55eaf8c1208839081808280098009098391089082827fcca361819ffefe3e50fe34c91a322c9405f4e5a168c1fc0a0a1883993e32c9f408839081808280098009098391089082827f6656088842bfc9e325a532784d3362cecfa86f9c7b208a6b499836ebe48ff15708839081808280098009098391089082827f00129c7cd00e42ed05a37dbceb80d47b65e1d750ef2148278a54723fdf42c4cc08839081808280098009098391089082827fa85b235631b786f85cd46f7768f6c71ae004ad267ae59bdf929ada149b19588808839081808280098009098391089082827f34df65a82686be09c5b237911abf237a9887c1a418f279ac79b446d7d311f5ea08839081808280098009098391089082827f815a850c3989df9ca6231e0bdd9916fc0e076f2c6c7f0f260a846d0179f9c32d08839081808280098009098391089082827f50fb0940848a67aee83d348421fadd79aefc7a2adabeec6e64904ebe1bf63e7d08839081808280098009098391089082827fbab63a16273599f8b66895461e62a19ff0d103693be771d93e3691bba89cdd8d08839081808280098009098391089082827f6931a091756e0bc709ebecfffba5038634c5b3d5d0c5876dd72aac67452db8a208839081808280098009098391089082827f55559b8bb79db8809c46ee627f1b5ce1d8e6d89bf94a9987a1407759d1ba896308839081808280098009098391089082827fa9a1a11b2979018cb155914d09f1df19b7ffec241e8b2487b6f6272a56a44a0a08839081808280098009098391089082827ff83293400e7bccea4bb86dcb0d5ca57fa2466e13a572d7d3531c6fa491cb0f1b08839081808280098009098391089082827fb7cb5742b6bc5339624d3568a33c21f31b877f8396972582028da999abf249f208839081808280098009098391089082827ff56efb400f8500b5c5bf811c65c86c7ed2e965f14f1a69bca436c0c60b79f46508839081808280098009098391089082827fd7c4427998d9c440f849dcd75b7157996eaad1b9a1d58cc2441931300e26eb2208839081808280098009098391089082827fca5ed18ad53e33fdc3ae8cf353ff3f6dd315f60060442b74f6b614b24ebd4cc308839081808280098009098391089082827f9ad3e9376c97b194a0fbf43e22a3616981d777365c765ead09a1d033fdf536b708839081808280098009098391089082827fc6daeff5769a06b26fe3b8fef30df07b1387373a7814cef364fe1d6059eaf54a08839081808280098009098391089082827fc20a78398345c6b8cf439643dab96223bf879c302648293eaf496fee5c978c6608839081808280098009098391089082827f589ca65b6cf0e90653c06dddc057dc61ba2839974569051c98b43e8618716efb08839081808280098009098391089082827f83064161f127d8c59fc73625957e21630dc6dc99e5443f6ce37ecd6bf28e69b708839081808280098009098391089082827f46d0ba662b50100b9a3af52052f68932feec1d12290b2033c4f49148893d8ba308839081808280098009098391089082827f18dd55b4a83a53f2ee578eb3e6d26f594824d44670fc3f4de80642344d15c09a08839081808280098009098391089082827f9fb5b594f48bc58b345ab90ded705920a7274b8e070eee8ce8cf90c72c3604b608839081808280098009098391089082827f1901d8f4f2c8449128e00663978f2050f2eb1cd6acb60d9d09c57c5d46ee54fe08839081808280098009098391089082827f5ec56789beab24ef7ee32f594d5fc561ec59dfeb93606dc7dcc6fe65133a7db408839081808280098009098391089082827f01c0b2cbe4fa9877a3d08eb67c510e8630da0a8beda94a6d9283e6f70d268bc508839081808280098009098391089082827f0b1d85acd9031a9107350eed946a25734e974799c5ba7cff13b15a5a623a25f008839081808280098009098391089082827f204497d1d359552905a2fe655f3d6f94926ea92d12cdaa6556ec26362f239f6408839081808280098009098391089082827fe075f7edc6631a8d7ffe33019f44fc91f286236d5a5f90f16de4791b72a2a5f008839081808280098009098391089082827f243f46e353354256ab8fe0ca4e9230dfc330bc163e602dfeaf307c1d1a7264b908839081808280098009098391089082827fd448ae5e09625fa1fcfd732fc9cd8f06e4c33b81f0a9240c83da56f41e9ecceb08839081808280098009098391089082827f2f312eef69a33d9fa753c08840275692a03432b3e6da67f9c59b9f9f4971cd5608839081808280098009098391089082827f5f333996af231bd5a293137da91801e191a6f24eb532ad1a7e6e9a2ad0efbc0008839081808280098009098391089082827fa8f771e0383a832dc8e2eaa8efabda300947acaf0684fabddf8b4abb0abd8a6208839081808280098009098391089082827f9ff0b3d7a4643596f651b70c1963cc4fa6c46018d78f05cb2c5f187e25df83a908839081808280098009098391089082827f9c373b704838325648273734dcdf962d7c156f431f70380ba4855832c4a238b808839081808280098009098391089082827fea2afa02604b8afeeb570f48a0e97a5e6bfe9613394b9a6b0026ecd6cec8c33a08839081808280098009098391089082827f68892258cd8eb43b71caa6d6837ec9959bfdfd72f25c9005ebaffc4011f8a7bf08839081808280098009098391089082827ff2824f561f6f82e3c1232836b0d268fa3b1b5489edd39a5fe1503bfc7ca91f4908839081808280098009098391089082827f164eda75fda2861f9d812f24e37ac938844fbe383c243b32b9f66ae2e76be71908839081808280098009098391089082827ff0a6fc431f5bf0dd1cca93b8b65b3f72c91f0693e2c74be9243b15abb31afcc008839081808280098009098391089082827fe68db66ba891ef0cd527f09ec6fff3ec0a269cf3d891a35ec13c902f70334b4f08839081808280098009098391089082827f3a44a5b102f7883a2b8630a3cae6e6db2e6e483bb7cfeb3492cbd91793ef598e08839081808280098009098391089082827f43939fe8ef789acb33cbf129ba8a3aa1bd61510a178022a05177c9c5a1c59bf108839081808280098009098391089082827f936fe3b66dfda1bc5a7aae241b4db442858bd720c1d579c0c869f273cd55d77408839081808280098009098391089082827f3490fcaa8ffa37f35dc67ae006e81352c7103945417b8e4b142afcaefa344b8508839081808280098009098391089082827fcae66096cff344caca53ffe0e58aafeb468bd174f00d8abc425b2099c088187408839081808280098009098391089082827fc7d05783a41bc14f3c9a45384b6d5e2547c5b6a224c8316910b208f2718a70ab08839081808280098009098391089082827f5ac6b9ba94040d5692b865b6677b60ef3201b5c2121699f70beb9f9b2528a02608839081808280098009098391089082827fa902a3d4d9ecbfb9b2c76fddf780554bf93cad97b244e805d3adb94e1816290008839081808280098009098391089082827fe9df91ffeeb086a4d26041c29dac6fca1d56a4d022fe34b38831267395b98d2708839081808280098009098391089082827f862646f851d91a8840ad9ee711f12ec13b3e8f980ff5ef5ee43ca4520d57def708839081808280098009098391089082827f30b7381c9725b9db07816baf8524943a79cea135807c84cce0833485c11e0c2e08839081808280098009098391089082827f96afc10c5cedaddbda99df79387397c9be74a5b50f3a0c04ccb68d4e0f3a989f08839081808280098009098391089082827f3543da80d10da251c548776fe907c4ef89993d62e0062ae5c0496fcb851c366108839081808280098009098391089082827fe5140fe26d8b008430fccd50a68e3e11c1163d63b6d8b7cc40bc6f3c1d0b1b0608839081808280098009098391089082827ffefdf1872e4475e8bbb0ef6fab7f561bff121314695c433bd4c29ec118060c9608839081808280098009098391089082827f6bb8c9f3d57b18e002df059db1e6a5d42ad566f153f18460774f68ac2650940008839081808280098009098391089082827f5415122d50b26f4fab5784004c56cf03f128f825ad2236f4b3d51f74737bd97308839081808280098009098391089082827f00e115c4a98efae6a3a5ecc873b0cef63ccd5b515710a3ab03ec52218f784dc908839081808280098009098391089082827fda7d525427bad87b88238657c21331245578bc76aa6240b7f972382537a202ab08839081808280098009098391089082827f83332e8b34505b83010270dc795290a2f515b8f89c163acecdf4799df04c62f808839081808280098009098391089082827fb09ecb6033d1a065f17a61066cd737d0c3c5873b51c3ab0a285e26939e62aa1808839081808280098009098391089082827f24e65c718938c2b937378e7435332174329730bde85a4185e37875824eb4985908839081808280098009098391089082827f68e41430ccd41cc5e92a9f9acd2e955c1385b9f5ed8d3f133d767429484a8eba08839081808280098009098391089082827fc038fe9d0125ab8be54545276f841274e414c596ed4c9eaa6919604603d1ffa908839081808280098009098391089082827f23248698612cd8e83234fcf5db9b6b225f4b0ba78d72ef13ea1edff5f0fb029808839081808280098009098391089082827fd2a9fa3d39c1ba91eefa666a1db71c6e0e4e3b707626b0197a4e59e7110cf0d408839081808280098009098391089082827fc28931ee7dfa02b62872e0d937ba3dc5c637118273a1f1f0c4fc880905c82efc08839081808280098009098391089082827f01cd399556445e3d7b201d6c5e56a5794e60be2cfd9a4643e7ead79bb4f60f7908839081808280098009098391089082827fac855cc58d5fbb0dff91a79683eb0e914c1b7d8d0a540d416838a89f83a8312f08839081808280098009098391089082827ff7798af7ccf36b836705849f7dd40328bf9346657255b431446ec75a6817181608839081808280098009098391089082827fe52a24c92d3f067bf551eeaf98c62ba525e84882d7adad835fad8de72986b2b108839081808280098009098391089082827fffc8682759a2bf1dd67c87a77c285467801f1c44fd78fa4eb5957a4832c9d72d08839081808280098009098391089082827f1482ac3e7e4f321627850d95a13942aea6d2923402b913046856ff7e8aaf9aff08839081808280098009098391089082827f17332b4c7aac2a07ccfe954de7ad22ccf6fcb4c5fa15c130ed22a40ae9398f4708839081808280098009098391089082827fd4be0546013f84a0d1e118b37589723b58e323983263616d1b036f8b3fdd858308839081808280098009098391089082827fa64ec737d31dddf939b184438ccdd3e1d3e667572857cd6c9c31a0d1d9b7b08508839081808280098009098391089082827f8ad12fbc74117cff4743d674539c86548c6758710a07a6abe3715e4b53526d3408839081808280098009098391089082827f15a16435a2300b27a337561401f06682ba85019aa0af61b264a1177d38b5c13c08839081808280098009098391089082827f22616f306e76352293a22ab6ee15509d9b108d4136b32fa7f9ed259793f392a108839081808280098009098391089082827f519727b25560caf00ce0d3f911bd4356f907160ab5186da10a629c7ccae1851e08839081808280098009098391089082827fcff39e77928ce9310118d50e29bc87e7f78b53ad51366359aa17f07902ae639208839081808280098009098391089082827f17dead3bfa1968c744118023dead77cdbee22c5b7c2414f5a6bdf82fd94cf3ad08839081808280098009098391089082827f2bef0f8b22a1cfb90100f4a552a9d02b772130123de8144a00c4d57497e1d7f408839081808280098009098391089082827fbf5188713fef90b31c35243f92cfa4331ab076e30e24b355c79b01f41d152a1108839081808280098009098391089082827f3baadd2fd92e3e12fb371be0578941dc0a108fbca0a7d81b88316fb94d6b4dfe08839081808280098009098391089082827fd4f955742e20a28d38611bf9fc4a478c97b673a7cd40d0113a58a1efe338d9aa08839081808280098009098391089082827f3c1c3fe9a5f7ccd54ad5a51a224b3f94775266d19c3733017e4920d7391ad64508839081808280098009098391089082827f6372df6148abeed66fda5461779a9651130c6c525df733852bcd929016768a7a08839081808280098009098391089082827f6d098e848fb853f95adb5a6364b5ab33c79fb08877f2cf3e0e160d9fcb3ebcc508839081808280098009098391089082827f48c5fc90f27431fabfe496dfba14bb0dba71141eb5472a365fd13023f4fe629608839081808280098009098391089082827fbb988dfc0c4dfe53999bd34840adcb63fdbf501ccd622ca2ddf5064ad8cdebf408839081808280098009098391089082827f25b068c942724c424ed5851c9575c22752c9bd25f91ebfa589de3d88ee7627f908839081808280098009098391089082827fed98a1931e361add218de11ff7879bd7114cda19c24ddbe15b3b0190ce01e1aa08839081808280098009098391089082827fc80b5a7d63f6c43542ad612023d3ffd6c684ce2eab837180addcb4decf51854408839081808280098009098391089082827fe2ef24bf47c5203118c6ff96657dd3c6fdff7212d5c798d826455de77b4b70cd08839081808280098009098391089082827f907da812fd5a8375587e4860f87691d0a8d61d454c507d09e5562e1a5d0fcc7608839081808280098009098391089082827fc459abbc62bc6070cacdff597e97990de56edc51cc6643afb0f6789fef1bad6308839081808280098009098391089082827f38d61f5e566855d70d36ef0f0f1fefcd7c829bdd60d95e0ef1fb5b98856280a408839081808280098009098391089082827f13218626665c420d3aa2b0fa49224a3dce8e08b8b56f8851bd9cb5e25cb3042d08839081808280098009098391089082827f6f685fb152dba21b4d02422e237e246df73d7d711ae6d7d33983bae0f873e31008839081808280098009098391089082827f5ade34719e2498dde70e4571c40474475a4af706a3cb82ac18a7fa44c22d1c4708839081808280098009098391089082827f8a0c3dc7a496adca059cb95d9b173812a00f3c4d435e0b9e8116e0c4b5f56acb08839081808280098009098391089082827f196bc98252f63169ed79073ee091a0e8ed0b5af51017da143940c00bdb86370908839081808280098009098391089082827fd979bf70695d93f8efb552a413701918afec9e12dfe213f4d0c27cfa68fad6c208839081808280098009098391089082827fb803072d02f54d237a3c6c4cc18eda6dce87a03c6819df54e4ed8aed6dc56d4608839081808280098009098391089082827f1efcda9d986cddcf431af4d59c6a7709d650885b7886cba70f0e7cd92b331cdc08839081808280098009098391089082827fd3ca5f7859b82ac50b63da06d43aa68a6b685f0a60397638bbea173b3f60419208839081808280098009098391089082827fa59d392c0667316ad37a06be2d51aabe9e79bdef0013bc109985648a14c7e41f08839081808280098009098391089082827fac2f5f0d2146791b396e2bed6cf15a20bc22cc4c8cf7dd4b3514ac00148dd0a708839081808280098009098391089082827f17a993a6af068d72bc36f0e814d29fef3f97d7a72aa963889b16a8457409861a08839081808280098009098391089082827f6f1bf99686550e0396f7f4e2df6fdaa090fbc272c8c76eb32a3c6791de5a07b508839081808280098009098391089082827f8234d705e1ecdc59cc6ed40749069d4b45e63deb49b5b7d7f527abd31c072b1b08839081808280098009098391089082827f6fe929a1fd6aacba5c4012c45dd727d2c816119567450003913d882cb97bc47e08839081808280098009098391089082827fad5371215f2aba49026b2e48739c11b4d8ffbb24dd4a6e41b9763862af96787a08839081808280098009098391089082827fd0e704566c49e1a11edc2c128b2e07f36dc0c755468268f8fe4c4859b9fa595b08839081808280098009098391089082827f263e1195090d00be1d8fb37de17ccf3b66d180645efa0d831865cfaa8797769e08839081808280098009098391089082827fe65c090eebde2cfa7f9c92cf75641c7683fb8e81f4a48f5b7a9c7eb26a85029f08839081808280098009098391089082827fa18971781c6855f6a9752912780bb9b719c14a677a4c6393d62d6e046b97a2ac08839081808280098009098391089082827ff6fc1ef1bca8bec055cc66edecc5dc99030fe78311a3f21d8cd624df4f89e62508839081808280098009098391089082827f824e4e2838501516d3296542cb47a59a1ca4326e947c9c874d88dccc8e37b99a08839081808280098009098391089082827f3cd5a9e7353a50e454c9c1381b556b543897cc89153c3e3749f2021d8237226308839081808280098009098391089082827fb4bcedbd54d0c917a315cc7ca785e3c5995abbeeb3deb3ebaf02c7a9bf6cc83f08839081808280098009098391089082827f1f7476211105b3039cef009c51155ae93526c53a74973ecfce40754b3df1052108839081808280098009098391089082827f58aefbd978440c94b4b9fbd36e00e6e36caeacf82b0da0a6161d34c541a5a6e308839081808280098009098391089082827fc22cd6d61be780a33c77677bc6ba40307b597ed981db57cb485313eec2a5a49708839081808280098009098391089082827fd9ffc4fe0dc5f835c8dcdc1e60b8f0b1637f32a809175371b94a057272b0748d08839081808280098009098391089082827ff6a5268541bc4c64ad0ade8f55dda3492604857a71c923662a214dd7e9c20c1008839081808280098009098391089082826000088390818082800980090983910860205260005260406000f3";
function deployHasher(signer) {
return signer.sendTransaction({ data: hasherBytecode });
}
const INDEX_DB_ERROR = "A mutation operation was attempted on a database that did not allow mutations.";
class IndexedDB {
dbExists;
isBlocked;
// todo: TestDBSchema on any
options;
dbName;
dbVersion;
db;
constructor({ dbName, stores }) {
this.dbExists = false;
this.isBlocked = false;
this.options = {
upgrade(db) {
Object.values(db.objectStoreNames).forEach((value) => {
db.deleteObjectStore(value);
});
[{ name: "keyval" }, ...stores || []].forEach(({ name, keyPath, indexes }) => {
const store = db.createObjectStore(name, {
keyPath,
autoIncrement: true
});
if (Array.isArray(indexes)) {
indexes.forEach(({ name: name2, unique = false }) => {
store.createIndex(name2, name2, { unique });
});
}
});
}
};
this.dbName = dbName;
this.dbVersion = 35;
}
async initDB() {
try {
if (this.dbExists || this.isBlocked) {
return;
}
this.db = await idb.openDB(this.dbName, this.dbVersion, this.options);
this.db.addEventListener("onupgradeneeded", async () => {
await this._removeExist();
});
this.dbExists = true;
} catch (err) {
if (err.message.includes(INDEX_DB_ERROR)) {
console.log("This browser does not support IndexedDB!");
this.isBlocked = true;
return;
}
if (err.message.includes("less than the existing version")) {
console.log(`Upgrading DB ${this.dbName} to ${this.dbVersion}`);
await this._removeExist();
return;
}
console.error(`Method initDB has error: ${err.message}`);
}
}
async _removeExist() {
await idb.deleteDB(this.dbName);
this.dbExists = false;
await this.initDB();
}
async getFromIndex({
storeName,
indexName,
key
}) {
await this.initDB();
if (!this.db) {
return;
}
try {
return await this.db.getFromIndex(storeName, indexName, key);
} catch (err) {
throw new Error(`Method getFromIndex has error: ${err.message}`);
}
}
async getAllFromIndex({
storeName,
indexName,
key,
count
}) {
await this.initDB();
if (!this.db) {
return [];
}
try {
return await this.db.getAllFromIndex(storeName, indexName, key, count);
} catch (err) {
throw new Error(`Method getAllFromIndex has error: ${err.message}`);
}
}
async getItem({ storeName, key }) {
await this.initDB();
if (!this.db) {
return;
}
try {
const store = this.db.transaction(storeName).objectStore(storeName);
return await store.get(key);
} catch (err) {
throw new Error(`Method getItem has error: ${err.message}`);
}
}
async addItem({ storeName, data, key = "" }) {
await this.initDB();
if (!this.db) {
return;
}
try {
const tx = this.db.transaction(storeName, "readwrite");
const isExist = await tx.objectStore(storeName).get(key);
if (!isExist) {
await tx.objectStore(storeName).add(data);
}
} catch (err) {
throw new Error(`Method addItem has error: ${err.message}`);
}
}
async putItem({ storeName, data, key }) {
await this.initDB();
if (!this.db) {
return;
}
try {
const tx = this.db.transaction(storeName, "readwrite");
await tx.objectStore(storeName).put(data, key);
} catch (err) {
throw new Error(`Method putItem has error: ${err.message}`);
}
}
async deleteItem({ storeName, key }) {
await this.initDB();
if (!this.db) {
return;
}
try {
const tx = this.db.transaction(storeName, "readwrite");
await tx.objectStore(storeName).delete(key);
} catch (err) {
throw new Error(`Method deleteItem has error: ${err.message}`);
}
}
async getAll({ storeName }) {
await this.initDB();
if (!this.db) {
return [];
}
try {
const tx = this.db.transaction(storeName, "readonly");
return await tx.objectStore(storeName).getAll();
} catch (err) {
throw new Error(`Method getAll has error: ${err.message}`);
}
}
/**
* Simple key-value store inspired by idb-keyval package
*/
getValue(key) {
return this.getItem({ storeName: "keyval", key });
}
setValue(key, data) {
return this.putItem({ storeName: "keyval", key, data });
}
delValue(key) {
return this.deleteItem({ storeName: "keyval", key });
}
async clearStore({ storeName, mode = "readwrite" }) {
await this.initDB();
if (!this.db) {
return;
}
try {
const tx = this.db.transaction(storeName, mode);
await tx.objectStore(storeName).clear();
} catch (err) {
throw new Error(`Method clearStore has error: ${err.message}`);
}
}
async createTransactions({
storeName,
data,
mode = "readwrite"
}) {
await this.initDB();
if (!this.db) {
return;
}
try {
const tx = this.db.transaction(storeName, mode);
await tx.objectStore(storeName).add(data);
await tx.done;
} catch (err) {
throw new Error(`Method createTransactions has error: ${err.message}`);
}
}
async createMultipleTransactions({
storeName,
data,
index,
mode = "readwrite"
}) {
await this.initDB();
if (!this.db) {
return;
}
try {
const tx = this.db.transaction(storeName, mode);
for (const item of data) {
if (item) {
await tx.store.put({ ...item, ...index });
}
}
} catch (err) {
throw new Error(`Method createMultipleTransactions has error: ${err.message}`);
}
}
}
async function getIndexedDB(netId) {
if (!netId) {
const idb2 = new IndexedDB({ dbName: "tornado-core" });
await idb2.initDB();
return idb2;
}
const minimalIndexes = [
{
name: "blockNumber",
unique: false
},
{
name: "transactionHash",
unique: false
}
];
const defaultState = [
{
name: `echo_${netId}`,
keyPath: "eid",
indexes: [
...minimalIndexes,
{
name: "address",
unique: false
}
]
},
{
name: `encrypted_notes_${netId}`,
keyPath: "eid",
indexes: minimalIndexes
},
{
name: "lastEvents",
keyPath: "name",
indexes: [
{
name: "name",
unique: false
}
]
}
];
const config = getConfig(netId);
const { tokens, nativeCurrency, registryContract, governanceContract } = config;
const stores = [...defaultState];
if (registryContract) {
stores.push({
name: `registry_${netId}`,
keyPath: "ensName",
indexes: [
...minimalIndexes,
{
name: "event",
unique: false
}
]
});
stores.push({
name: `relayers_${netId}`,
keyPath: "timestamp",
indexes: [
{
name: "timestamp",
unique: true
}
]
});
stores.push({
name: `revenue_${netId}`,
keyPath: "timestamp",
indexes: [
{
name: "timestamp",
unique: true
}
]
});
}
if (governanceContract) {
stores.push({
name: `governance_${netId}`,
keyPath: "eid",
indexes: [
...minimalIndexes,
{
name: "event",
unique: false
}
]
});
}
Object.entries(tokens).forEach(([token, { instanceAddress }]) => {
Object.keys(instanceAddress).forEach((amount) => {
if (nativeCurrency === token) {
stores.push(
{
name: `stringify_bloom_${netId}_${token}_${amount}`,
keyPath: "hashBloom",
indexes: []
},
{
name: `stringify_tree_${netId}_${token}_${amount}`,
keyPath: "hashTree",
indexes: []
}
);
}
stores.push(
{
name: `deposits_${netId}_${token}_${amount}`,
keyPath: "leafIndex",
// the key by which it refers to the object must be in all instances of the storage
indexes: [
...minimalIndexes,
{
name: "commitment",
unique: true
}
]
},
{
name: `withdrawals_${netId}_${token}_${amount}`,
keyPath: "eid",
indexes: [
...minimalIndexes,
{
name: "nullifierHash",
unique: true
}
// keys on which the index is created
]
}
);
});
});
const idb = new IndexedDB({
dbName: `tornado_core_${netId}`,
stores
});
await idb.initDB();
return idb;
}
async function fetchIp(ipEcho) {
return await fetchData(ipEcho, {
method: "GET",
timeout: 3e4
});
}
class Mimc {
sponge;
hash;
mimcPromise;
constructor() {
this.mimcPromise = this.initMimc();
}
async initMimc() {
this.sponge = await circomlibjs.buildMimcSponge();
this.hash = (left, right) => this.sponge?.F.toString(this.sponge?.multiHash([BigInt(left), BigInt(right)]));
}
async getHash() {
await this.mimcPromise;
return {
sponge: this.sponge,
hash: this.hash
};
}
}
const mimc = new Mimc();
class MerkleTreeService {
currency;
amount;
netId;
Tornado;
commitmentHex;
instanceName;
merkleTreeHeight;
emptyElement;
merkleWorkerPath;
constructor({
netId,
amount,
currency,
Tornado,
commitmentHex,
merkleTreeHeight = 20,
emptyElement = "21663839004416932945382355908790599225266501822907911457504978515578255421292",
merkleWorkerPath
}) {
const instanceName = `${netId}_${currency}_${amount}`;
this.currency = currency;
this.amount = amount;
this.netId = Number(netId);
this.Tornado = Tornado;
this.instanceName = instanceName;
this.commitmentHex = commitmentHex;
this.merkleTreeHeight = merkleTreeHeight;
this.emptyElement = emptyElement;
this.merkleWorkerPath = merkleWorkerPath;
}
async createTree(events) {
const { hash: hashFunction } = await mimc.getHash();
if (this.merkleWorkerPath) {
console.log("Using merkleWorker\n");
try {
if (isNode) {
const merkleWorkerPromise = new Promise((resolve, reject) => {
const worker = new worker_threads.Worker(this.merkleWorkerPath, {
workerData: {
merkleTreeHeight: this.merkleTreeHeight,
elements: events,
zeroElement: this.emptyElement
}
});
worker.on("message", resolve);
worker.on("error", reject);
worker.on("exit", (code) => {
if (code !== 0) {
reject(new Error(`Worker stopped with exit code ${code}`));
}
});
});
return fixedMerkleTree.MerkleTree.deserialize(JSON.parse(await merkleWorkerPromise), hashFunction);
} else {
const merkleWorkerPromise = new Promise((resolve, reject) => {
const worker = new Worker(this.merkleWorkerPath);
worker.onmessage = (e) => {
resolve(e.data);
};
worker.onerror = (e) => {
reject(e);
};
worker.postMessage({
merkleTreeHeight: this.merkleTreeHeight,
elements: events,
zeroElement: this.emptyElement
});
});
return fixedMerkleTree.MerkleTree.deserialize(JSON.parse(await merkleWorkerPromise), hashFunction);
}
} catch (err) {
console.log("merkleWorker failed, falling back to synchronous merkle tree");
console.log(err);
}
}
return new fixedMerkleTree.MerkleTree(this.merkleTreeHeight, events, {
zeroElement: this.emptyElement,
hashFunction
});
}
async createPartialTree({ edge, elements }) {
const { hash: hashFunction } = await mimc.getHash();
if (this.merkleWorkerPath) {
console.log("Using merkleWorker\n");
try {
if (isNode) {
const merkleWorkerPromise = new Promise((resolve, reject) => {
const worker = new worker_threads.Worker(this.merkleWorkerPath, {
workerData: {
merkleTreeHeight: this.merkleTreeHeight,
edge,
elements,
zeroElement: this.emptyElement
}
});
worker.on("message", resolve);
worker.on("error", reject);
worker.on("exit", (code) => {
if (code !== 0) {
reject(new Error(`Worker stopped with exit code ${code}`));
}
});
});
return fixedMerkleTree.PartialMerkleTree.deserialize(JSON.parse(await merkleWorkerPromise), hashFunction);
} else {
const merkleWorkerPromise = new Promise((resolve, reject) => {
const worker = new Worker(this.merkleWorkerPath);
worker.onmessage = (e) => {
resolve(e.data);
};
worker.onerror = (e) => {
reject(e);
};
worker.postMessage({
merkleTreeHeight: this.merkleTreeHeight,
edge,
elements,
zeroElement: this.emptyElement
});
});
return fixedMerkleTree.PartialMerkleTree.deserialize(JSON.parse(await merkleWorkerPromise), hashFunction);
}
} catch (err) {
console.log("merkleWorker failed, falling back to synchronous merkle tree");
console.log(err);
}
}
return new fixedMerkleTree.PartialMerkleTree(this.merkleTreeHeight, edge, elements, {
zeroElement: this.emptyElement,
hashFunction
});
}
async verifyTree(events) {
console.log(
`
Creating deposit tree for ${this.netId} ${this.amount} ${this.currency.toUpperCase()} would take a while
`
);
const timeStart = Date.now();
const tree = await this.createTree(events.map(({ commitment }) => commitment));
const isKnownRoot = await this.Tornado.isKnownRoot(toFixedHex(BigInt(tree.root)));
if (!isKnownRoot) {
const errMsg = `Deposit Event ${this.netId} ${this.amount} ${this.currency} is invalid`;
throw new Error(errMsg);
}
console.log(
`
Created ${this.netId} ${this.amount} ${this.currency.toUpperCase()} tree in ${Date.now() - timeStart}ms
`
);
return tree;
}
}
async function multicall(Multicall2, calls) {
const calldata = calls.map((call) => {
const target = call.contract?.target || call.address;
const callInterface = call.contract?.interface || call.interface;
return {
target,
callData: callInterface.encodeFunctionData(call.name, call.params),
allowFailure: call.allowFailure ?? false
};
});
const returnData = await Multicall2.aggregate3.staticCall(calldata);
const res = returnData.map((call, i) => {
const callInterface = calls[i].contract?.interface || calls[i].interface;
const [result, data] = call;
const decodeResult = result && data && data !== "0x" ? callInterface.decodeFunctionResult(calls[i].name, data) : null;
return !decodeResult ? null : decodeResult.length === 1 ? decodeResult[0] : decodeResult;
});
return res;
}
const permit2Address = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
async function getPermitSignature({
Token,
signer,
spender,
value,
nonce,
deadline
}) {
const sigSigner = signer || Token.runner;
const provider = sigSigner.provider;
const [name, lastNonce, { chainId }] = await Promise.all([
Token.name(),
Token.nonces(sigSigner.address),
provider.getNetwork()
]);
const DOMAIN_SEPARATOR = {
name,
version: "1",
chainId,
verifyingContract: Token.target
};
const PERMIT_TYPE = {
Permit: [
{ name: "owner", type: "address" },
{ name: "spender", type: "address" },
{ name: "value", type: "uint256" },
{ name: "nonce", type: "uint256" },
{ name: "deadline", type: "uint256" }
]
};
return ethers.Signature.from(
await sigSigner.signTypedData(DOMAIN_SEPARATOR, PERMIT_TYPE, {
owner: sigSigner.address,
spender,
value,
nonce: nonce || lastNonce,
deadline: deadline || ethers.MaxUint256
})
);
}
async function getPermitCommitmentsSignature({
PermitTornado: PermitTornado2,
Token,
signer,
denomination,
commitments,
nonce
}) {
const value = BigInt(commitments.length) * denomination;
const commitmentsHash = ethers.solidityPackedKeccak256(["bytes32[]"], [commitments]);
return await getPermitSignature({
Token,
signer,
spender: PermitTornado2.target,
value,
nonce,
deadline: BigInt(commitmentsHash)
});
}
async function getPermit2Signature({
Token,
signer,
spender,
value: amount,
nonce,
deadline,
witness
}) {
const sigSigner = signer || Token.runner;
const provider = sigSigner.provider;
const domain = {
name: "Permit2",
chainId: (await provider.getNetwork()).chainId,
verifyingContract: permit2Address
};
const types = !witness ? {
PermitTransferFrom: [
{ name: "permitted", type: "TokenPermissions" },
{ name: "spender", type: "address" },
{ name: "nonce", type: "uint256" },
{ name: "deadline", type: "uint256" }
],
TokenPermissions: [
{ name: "token", type: "address" },
{ name: "amount", type: "uint256" }
]
} : {
PermitWitnessTransferFrom: [
{ name: "permitted", type: "TokenPermissions" },
{ name: "spender", type: "address" },
{ name: "nonce", type: "uint256" },
{ name: "deadline", type: "uint256" },
{ name: "witness", type: witness.witnessTypeName }
],
TokenPermissions: [
{ name: "token", type: "address" },
{ name: "amount", type: "uint256" }
],
...witness.witnessType
};
const values = {
permitted: {
token: Token.target,
amount
},
spender,
// Sorted nonce are not required for Permit2
nonce: nonce || rBigInt(16),
deadline: deadline || ethers.MaxUint256
};
if (witness) {
values.witness = witness.witness;
}
const hash = new ethers.TypedDataEncoder(types).hash(values);
const signature = ethers.Signature.from(await sigSigner.signTypedData(domain, types, values));
return {
domain,
types,
values,
hash,
signature
};
}
async function getPermit2CommitmentsSignature({
PermitTornado: PermitTornado2,
Token,
signer,
denomination,
commitments,
nonce,
deadline
}) {
const value = BigInt(commitments.length) * denomination;
const commitmentsHash = ethers.solidityPackedKeccak256(["bytes32[]"], [commitments]);
return await getPermit2Signature({
Token,
signer,
spender: PermitTornado2.target,
value,
nonce,
deadline,
witness: {
witnessTypeName: "PermitCommitments",
witnessType: {
PermitCommitments: [
{ name: "instance", type: "address" },
{ name: "commitmentsHash", type: "bytes32" }
]
},
witness: {
instance: PermitTornado2.target,
commitmentsHash
}
}
});
}
class TokenPriceOracle {
oracle;
multicall;
provider;
fallbackPrice;
constructor(provider, multicall2, oracle) {
this.provider = provider;
this.multicall = multicall2;
this.oracle = oracle;
this.fallbackPrice = ethers.parseEther("0.0001");
}
buildCalls(tokens) {
return tokens.map(({ tokenAddress }) => ({
contract: this.oracle,
name: "getRateToEth",
params: [tokenAddress, true],
allowFailure: true
}));
}
buildStable(stablecoinAddress) {
const stablecoin = ERC20__factory.connect(stablecoinAddress, this.provider);
return [
{
contract: stablecoin,
name: "decimals"
},
{
contract: this.oracle,
name: "getRateToEth",
params: [stablecoin.target, true],
allowFailure: true
}
];
}
async fetchPrice(tokenAddress, decimals) {
if (!this.oracle) {
return new Promise((resolve) => resolve(this.fallbackPrice));
}
try {
const price = await this.oracle.getRateToEth(tokenAddress, true);
return price * BigInt(10 ** decimals) / BigInt(10 ** 18);
} catch (err) {
console.log(
`Failed to fetch oracle price for ${tokenAddress}, will use fallback price ${this.fallbackPrice}`
);
console.log(err);
return this.fallbackPrice;
}
}
async fetchPrices(tokens) {
if (!this.oracle) {
return new Promise((resolve) => resolve(tokens.map(() => this.fallbackPrice)));
}
const prices = await multicall(this.multicall, this.buildCalls(tokens));
return prices.map((price, index) => {
if (!price) {
price = this.fallbackPrice;
}
return price * BigInt(10 ** tokens[index].decimals) / BigInt(10 ** 18);
});
}
async fetchEthUSD(stablecoinAddress) {
if (!this.oracle) {
return new Promise((resolve) => resolve(10 ** 18 / Number(this.fallbackPrice)));
}
const [decimals, price] = await multicall(this.multicall, this.buildStable(stablecoinAddress));
const ethPrice = (price || this.fallbackPrice) * BigInt(10n ** decimals) / BigInt(10 ** 18);
return 1 / Number(ethers.formatEther(ethPrice));
}
}
async function getTokenBalances({
provider,
Multicall: Multicall2,
currencyName,
userAddress,
tokenAddresses = []
}) {
const tokenCalls = tokenAddresses.map((tokenAddress) => {
const Token = ERC20__factory.connect(tokenAddress, provider);
return [
{
contract: Token,
name: "balanceOf",
params: [userAddress]
},
{
contract: Token,
name: "name"
},
{
contract: Token,
name: "symbol"
},
{
contract: Token,
name: "decimals"
}
];
}).flat();
const multicallResults = await multicall(Multicall2, [
{
contract: Multicall2,
name: "getEthBalance",
params: [userAddress]
},
...tokenCalls.length ? tokenCalls : []
]);
const ethResults = multicallResults[0];
const tokenResults = multicallResults.slice(1).length ? chunk(multicallResults.slice(1), tokenCalls.length / tokenAddresses.length) : [];
const tokenBalances = tokenResults.map((tokenResult, index) => {
const [tokenBalance, tokenName, tokenSymbol, tokenDecimals] = tokenResult;
const tokenAddress = tokenAddresses[index];
return {
address: tokenAddress,
name: tokenName,
symbol: tokenSymbol,
decimals: Number(tokenDecimals),
balance: tokenBalance
};
});
return [
{
address: ethers.ZeroAddress,
name: currencyName,
symbol: currencyName,
decimals: 18,
balance: ethResults
},
...tokenBalances
];
}
const MAX_TOVARISH_EVENTS = 5e3;
class TovarishClient extends RelayerClient {
constructor(clientConstructor) {
super(clientConstructor);
this.tovarish = true;
}
async askRelayerStatus({
hostname,
url,
relayerAddress
}) {
const status = await super.askRelayerStatus({
hostname,
url,
relayerAddress
});
if (!status.version.includes("tovarish")) {
throw new Error("Not a tovarish relayer!");
}
return status;
}
/**
* Ask status for all enabled chains for tovarish relayer
*/
async askAllStatus({
hostname,
url,
relayerAddress
}) {
if (!url && hostname) {
url = `https://${!hostname.endsWith("/") ? hostname + "/" : hostname}`;
} else if (url && !url.endsWith("/")) {
url += "/";
} else {
url = "";
}
const statusArray = await fetchData(`${url}status`, {
...this.fetchDataOptions,
headers: {
"Content-Type": "application/json, application/x-www-form-urlencoded"
},
timeout: 3e4,
maxRetry: this.fetchDataOptions?.torPort ? 2 : 0
});
if (!Array.isArray(statusArray)) {
return [];
}
const tovarishStatus = [];
for (const rawStatus of statusArray) {
const netId = rawStatus.netId;
const config = getConfig(netId);
const statusValidator = ajv.compile(
getStatusSchema(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
rawStatus.netId,
config,
this.tovarish
)
);
if (!statusValidator) {
continue;
}
const status = {
...rawStatus,
url: `${url}${netId}/`
};
if (status.currentQueue > 5) {
throw new Error("Withdrawal queue is overloaded");
}
if (!enabledChains.includes(status.netId)) {
throw new Error("This relayer serves a different network");
}
if (relayerAddress && status.netId === NetId.MAINNET && status.rewardAccount !== relayerAddress) {
throw new Error("The Relayer reward address must match registered address");
}
if (!status.version.includes("tovarish")) {
throw new Error("Not a tovarish relayer!");
}
tovarishStatus.push(status);
}
return tovarishStatus;
}
async filterRelayer(relayer) {
const { ensName, relayerAddress, tovarishHost, tovarishNetworks } = relayer;
if (!tovarishHost || !tovarishNetworks?.includes(this.netId)) {
return;
}
const hostname = `${tovarishHost}/${this.netId}`;
try {
const status = await this.askRelayerStatus({
hostname,
relayerAddress
});
return {
netId: status.netId,
url: status.url,
hostname,
ensName,
relayerAddress,
rewardAccount: ethers.getAddress(status.rewardAccount),
instances: getSupportedInstances(status.instances),
stakeBalance: relayer.stakeBalance,
gasPrice: status.gasPrices?.fast,
ethPrices: status.ethPrices,
currentQueue: status.currentQueue,
tornadoServiceFee: status.tornadoServiceFee,
// Additional fields for tovarish relayer
latestBlock: Number(status.latestBlock),
latestBalance: status.latestBalance,
version: status.version,
events: status.events,
syncStatus: status.syncStatus
};
} catch (err) {
return {
hostname,
relayerAddress,
errorMessage: err.message,
hasError: true
};
}
}
async getValidRelayers(relayers) {
const invalidRelayers = [];
const validRelayers = (await Promise.all(relayers.map((relayer) => this.filterRelayer(relayer)))).filter(
(r) => {
if (!r) {
return false;
}
if (r.hasError) {
invalidRelayers.push(r);
return false;
}
return true;
}
);
return {
validRelayers,
invalidRelayers
};
}
async getTovarishRelayers(relayers) {
const validRelayers = [];
const invalidRelayers = [];
await Promise.all(
relayers.filter((r) => r.tovarishHost && r.tovarishNetworks?.length).map(async (relayer) => {
const { ensName, relayerAddress, tovarishHost } = relayer;
try {
const statusArray = await this.askAllStatus({
hostname: tovarishHost,
relayerAddress
});
for (const status of statusArray) {
validRelayers.push({
netId: status.netId,
url: status.url,
hostname: tovarishHost,
ensName,
relayerAddress,
rewardAccount: ethers.getAddress(status.rewardAccount),
instances: getSupportedInstances(status.instances),
stakeBalance: relayer.stakeBalance,
gasPrice: status.gasPrices?.fast,
ethPrices: status.ethPrices,
currentQueue: status.currentQueue,
tornadoServiceFee: status.tornadoServiceFee,
// Additional fields for tovarish relayer
latestBlock: Number(status.latestBlock),
latestBalance: status.latestBalance,
version: status.version,
events: status.events,
syncStatus: status.syncStatus
});
}
} catch (err) {
invalidRelayers.push({
hostname: tovarishHost,
relayerAddress,
errorMessage: err.message,
hasError: true
});
}
})
);
return {
validRelayers,
invalidRelayers
};
}
async getEvents({
type,
currency,
amount,
fromBlock,
recent
}) {
const url = `${this.selectedRelayer?.url}events`;
const schemaValidator = getEventsSchemaValidator(type);
try {
const events = [];
let lastSyncBlock = fromBlock;
while (true) {
let { events: fetchedEvents, lastSyncBlock: currentBlock } = await fetchData(url, {
...this.fetchDataOptions,
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
type,
currency,
amount,
fromBlock,
recent
})
});
if (!schemaValidator(fetchedEvents)) {
const errMsg = `Schema validation failed for ${type} events`;
throw new Error(errMsg);
}
if (recent) {
return {
events: fetchedEvents,
lastSyncBlock: currentBlock
};
}
lastSyncBlock = currentBlock;
if (!Array.isArray(fetchedEvents) || !fetchedEvents.length) {
break;
}
fetchedEvents = fetchedEvents.sort((a, b) => {
if (a.blockNumber === b.blockNumber) {
return a.logIndex - b.logIndex;
}
return a.blockNumber - b.blockNumber;
});
const [lastEvent] = fetchedEvents.slice(-1);
if (fetchedEvents.length < MAX_TOVARISH_EVENTS - 100) {
events.push(...fetchedEvents);
break;
}
fetchedEvents = fetchedEvents.filter((e) => e.blockNumber !== lastEvent.blockNumber);
fromBlock = Number(lastEvent.blockNumber);
events.push(...fetchedEvents);
}
return {
events,
lastSyncBlock
};
} catch (err) {
console.log("Error from TovarishClient events endpoint");
console.log(err);
return {
events: [],
lastSyncBlock: fromBlock
};
}
}
}
let groth16;
async function initGroth16() {
if (!groth16) {
groth16 = await websnarkGroth({ wasmInitialMemory: 2e3 });
}
}
async function calculateSnarkProof(input, circuit, provingKey) {
if (!groth16) {
await initGroth16();
}
const snarkInput = {
root: input.root,
nullifierHash: BigInt(input.nullifierHex).toString(),
recipient: BigInt(input.recipient),
relayer: BigInt(input.relayer),
fee: input.fee,
refund: input.refund,
nullifier: input.nullifier,
secret: input.secret,
pathElements: input.pathElements,
pathIndices: input.pathIndices
};
console.log("Start generating SNARK proof", snarkInput);
console.time("SNARK proof time");
const proofData = await websnarkUtils__namespace.genWitnessAndProve(await groth16, snarkInput, circuit, provingKey);
const proof = websnarkUtils__namespace.toSolidityInput(proofData).proof;
console.timeEnd("SNARK proof time");
const args = [
toFixedHex(input.root, 32),
toFixedHex(input.nullifierHex, 32),
input.recipient,
input.relayer,
toFixedHex(input.fee, 32),
toFixedHex(input.refund, 32)
];
return { proof, args };
}
exports.BaseEchoService = BaseEchoService;
exports.BaseEncryptedNotesService = BaseEncryptedNotesService;
exports.BaseEventsService = BaseEventsService;
exports.BaseGovernanceService = BaseGovernanceService;
exports.BaseRegistryService = BaseRegistryService;
exports.BaseRevenueService = BaseRevenueService;
exports.BaseTornadoService = BaseTornadoService;
exports.BatchBlockService = BatchBlockService;
exports.BatchEventsService = BatchEventsService;
exports.BatchTransactionService = BatchTransactionService;
exports.DBEchoService = DBEchoService;
exports.DBEncryptedNotesService = DBEncryptedNotesService;
exports.DBGovernanceService = DBGovernanceService;
exports.DBRegistryService = DBRegistryService;
exports.DBRevenueService = DBRevenueService;
exports.DBTornadoService = DBTornadoService;
exports.Deposit = Deposit;
exports.ENSNameWrapper__factory = ENSNameWrapper__factory;
exports.ENSRegistry__factory = ENSRegistry__factory;
exports.ENSResolver__factory = ENSResolver__factory;
exports.ENSUtils = ENSUtils;
exports.ENS__factory = ENS__factory;
exports.ERC20__factory = ERC20__factory;
exports.EnsContracts = EnsContracts;
exports.INDEX_DB_ERROR = INDEX_DB_ERROR;
exports.IndexedDB = IndexedDB;
exports.Invoice = Invoice;
exports.MAX_FEE = MAX_FEE;
exports.MAX_TOVARISH_EVENTS = MAX_TOVARISH_EVENTS;
exports.MIN_FEE = MIN_FEE;
exports.MIN_STAKE_BALANCE = MIN_STAKE_BALANCE;
exports.MerkleTreeService = MerkleTreeService;
exports.Mimc = Mimc;
exports.Multicall__factory = Multicall__factory;
exports.NetId = NetId;
exports.NoteAccount = NoteAccount;
exports.OffchainOracle__factory = OffchainOracle__factory;
exports.OvmGasPriceOracle__factory = OvmGasPriceOracle__factory;
exports.Pedersen = Pedersen;
exports.RelayerClient = RelayerClient;
exports.ReverseRecords__factory = ReverseRecords__factory;
exports.TokenPriceOracle = TokenPriceOracle;
exports.TornadoBrowserProvider = TornadoBrowserProvider;
exports.TornadoFeeOracle = TornadoFeeOracle;
exports.TornadoRpcSigner = TornadoRpcSigner;
exports.TornadoVoidSigner = TornadoVoidSigner;
exports.TornadoWallet = TornadoWallet;
exports.TovarishClient = TovarishClient;
exports.addNetwork = addNetwork;
exports.addressSchemaType = addressSchemaType;
exports.ajv = ajv;
exports.base64ToBytes = base64ToBytes;
exports.bigIntReplacer = bigIntReplacer;
exports.bnSchemaType = bnSchemaType;
exports.bnToBytes = bnToBytes;
exports.buffPedersenHash = buffPedersenHash;
exports.bufferToBytes = bufferToBytes;
exports.bytes32BNSchemaType = bytes32BNSchemaType;
exports.bytes32SchemaType = bytes32SchemaType;
exports.bytesToBN = bytesToBN;
exports.bytesToBase64 = bytesToBase64;
exports.bytesToHex = bytesToHex;
exports.calculateScore = calculateScore;
exports.calculateSnarkProof = calculateSnarkProof;
exports.chunk = chunk;
exports.concatBytes = concatBytes;
exports.convertETHToTokenAmount = convertETHToTokenAmount;
exports.createDeposit = createDeposit;
exports.crypto = crypto;
exports.defaultConfig = defaultConfig;
exports.defaultUserAgent = defaultUserAgent;
exports.deployHasher = deployHasher;
exports.depositsEventsSchema = depositsEventsSchema;
exports.digest = digest;
exports.downloadZip = downloadZip;
exports.echoEventsSchema = echoEventsSchema;
exports.enabledChains = enabledChains;
exports.encodedLabelToLabelhash = encodedLabelToLabelhash;
exports.encryptedNotesSchema = encryptedNotesSchema;
exports.factories = index;
exports.fetchData = fetchData;
exports.fetchGetUrlFunc = fetchGetUrlFunc;
exports.fetchIp = fetchIp;
exports.fromContentHash = fromContentHash;
exports.gasZipID = gasZipID;
exports.gasZipInbounds = gasZipInbounds;
exports.gasZipInput = gasZipInput;
exports.gasZipMinMax = gasZipMinMax;
exports.getActiveTokenInstances = getActiveTokenInstances;
exports.getActiveTokens = getActiveTokens;
exports.getConfig = getConfig;
exports.getEventsSchemaValidator = getEventsSchemaValidator;
exports.getHttpAgent = getHttpAgent;
exports.getIndexedDB = getIndexedDB;
exports.getInstanceByAddress = getInstanceByAddress;
exports.getNetworkConfig = getNetworkConfig;
exports.getPermit2CommitmentsSignature = getPermit2CommitmentsSignature;
exports.getPermit2Signature = getPermit2Signature;
exports.getPermitCommitmentsSignature = getPermitCommitmentsSignature;
exports.getPermitSignature = getPermitSignature;
exports.getProvider = getProvider;
exports.getProviderWithNetId = getProviderWithNetId;
exports.getRelayerEnsSubdomains = getRelayerEnsSubdomains;
exports.getStatusSchema = getStatusSchema;
exports.getSubInfo = getSubInfo;
exports.getSupportedInstances = getSupportedInstances;
exports.getTokenBalances = getTokenBalances;
exports.getTovarishNetworks = getTovarishNetworks;
exports.getWeightRandom = getWeightRandom;
exports.governanceEventsSchema = governanceEventsSchema;
exports.hasherBytecode = hasherBytecode;
exports.hexToBytes = hexToBytes;
exports.initGroth16 = initGroth16;
exports.isHex = isHex;
exports.isNode = isNode;
exports.jobRequestSchema = jobRequestSchema;
exports.jobsSchema = jobsSchema;
exports.labelhash = labelhash;
exports.leBuff2Int = leBuff2Int;
exports.leInt2Buff = leInt2Buff;
exports.loadDBEvents = loadDBEvents;
exports.loadRemoteEvents = loadRemoteEvents;
exports.makeLabelNodeAndParent = makeLabelNodeAndParent;
exports.mimc = mimc;
exports.multiQueryFilter = multiQueryFilter;
exports.multicall = multicall;
exports.numberFormatter = numberFormatter;
exports.packEncryptedMessage = packEncryptedMessage;
exports.parseInvoice = parseInvoice;
exports.parseNote = parseNote;
exports.pedersen = pedersen;
exports.permit2Address = permit2Address;
exports.pickWeightedRandomRelayer = pickWeightedRandomRelayer;
exports.populateTransaction = populateTransaction;
exports.proofSchemaType = proofSchemaType;
exports.proposalState = proposalState;
exports.rBigInt = rBigInt;
exports.rHex = rHex;
exports.relayerRegistryEventsSchema = relayerRegistryEventsSchema;
exports.saveDBEvents = saveDBEvents;
exports.sleep = sleep;
exports.stakeBurnedEventsSchema = stakeBurnedEventsSchema;
exports.substring = substring;
exports.toContentHash = toContentHash;
exports.toFixedHex = toFixedHex;
exports.toFixedLength = toFixedLength;
exports.unpackEncryptedMessage = unpackEncryptedMessage;
exports.unzipAsync = unzipAsync;
exports.validateUrl = validateUrl;
exports.withdrawalsEventsSchema = withdrawalsEventsSchema;
exports.zipAsync = zipAsync;