UNPKG

@baking-bad/tezos-etherlink-bridge-sdk

Version:

SDK designed for building token bridge applications between Tezos (L1) and Etherlink (L2)

1,249 lines (1,220 loc) 134 kB
var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __publicField = (obj, key, value) => { __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; // src/common/errors.ts var TokenBridgeError = class extends Error { constructor(message, options) { super(message, options); __publicField(this, "name"); this.name = this.constructor.name; } }; var DisposedError = class extends TokenBridgeError { }; var RemoteServiceResponseError = class _RemoteServiceResponseError extends TokenBridgeError { constructor(status, content) { super(_RemoteServiceResponseError.getMessage(status, content)); } static getMessage(status, content) { return `Response Error [Code: ${status}]. Content = ${content}`; } }; // src/utils/memoize.ts var defaultEqualityCheck = (a, b) => a === b; var areArgumentsShallowlyEqual = (equalityCheck, prev, next) => { if (prev === null || next === null || prev.length !== next.length) { return false; } const length = prev.length; for (let i = 0; i < length; i++) { if (!equalityCheck(prev[i], next[i])) { return false; } } return true; }; var memoize = (func, equalityCheck = defaultEqualityCheck) => { let lastArgs = null; let lastResult = null; return function() { if (!areArgumentsShallowlyEqual(equalityCheck, lastArgs, arguments)) { lastResult = func.apply(null, arguments); } lastArgs = arguments; return lastResult; }; }; // src/utils/guards.ts var guards_exports = {}; __export(guards_exports, { isArray: () => isArray, isDisposable: () => isDisposable, isReadonlyArray: () => isReadonlyArray }); var isArray = (arg) => { return Array.isArray(arg); }; var isReadonlyArray = (arg) => { return Array.isArray(arg); }; var isDisposable = (arg) => { return typeof arg?.[Symbol.dispose] === "function"; }; // src/utils/textUtils.ts var textUtils_exports = {}; __export(textUtils_exports, { trimSlashes: () => trimSlashes }); var trimSlashes = (value) => { const hasFirst = value.startsWith("/"); const hasLast = value.endsWith("/"); return hasFirst && hasLast ? value.slice(1, -1) : hasFirst ? value.slice(1) : hasLast ? value.slice(0, -1) : value; }; // src/utils/tezosUtils.ts var tezosUtils_exports = {}; __export(tezosUtils_exports, { convertAddressToBytes: () => convertAddressToBytes, convertBytesToAddress: () => convertBytesToAddress }); import { b58decode, encodeAddress } from "@taquito/utils"; var convertAddressToBytes = (address, addPrefix = false) => { const bytes = b58decode(address); return addPrefix ? "0x" + bytes : bytes; }; var convertBytesToAddress = (bytes) => { if (bytes.startsWith("0x")) bytes = bytes.substring(2); return encodeAddress(bytes); }; // src/utils/tokenUtils.ts var tokenUtils_exports = {}; __export(tokenUtils_exports, { isTezosToken: () => isTezosToken, toDisplayString: () => toDisplayString }); var isTezosToken = (token) => { return token.type === "native" || token.type === "fa1.2" || token.type === "fa2"; }; var convertTokenToDisplayString = (token) => { if (!token) return `[token is ${token === null ? "null" : "undefined"}]`; switch (token.type) { case "native": return "[native token]"; case "erc20": return `[${token.address} | ERC-20]`; case "fa1.2": return `[${token.address} | FA1.2]`; case "fa2": return `[${token.address} | FA2 | Id: ${token.tokenId}]`; default: return "[unknown token type]"; } }; var convertTokensToDisplayString = (tokens) => { return tokens.reduce( (result, token, index, tokens2) => result + convertTokenToDisplayString(token) + (index < tokens2.length - 1 ? ", " : "]"), "[" ); }; var toDisplayString = (tokenOrTokens) => { return isReadonlyArray(tokenOrTokens) ? convertTokensToDisplayString(tokenOrTokens) : convertTokenToDisplayString(tokenOrTokens); }; // src/utils/etherlinkUtils.ts var etherlinkUtils_exports = {}; __export(etherlinkUtils_exports, { isAddress: () => isAddress2, isTransaction: () => isTransaction, prepareHexPrefix: () => prepareHexPrefix, toChecksumAddress: () => toChecksumAddress2 }); import * as web3Utils from "web3-utils"; import * as web3Validator from "web3-validator"; var isAddress2 = (address) => web3Validator.isAddress(address, true); var isTransactionRegex = /^0x[0-9a-f]{64}$/; var isTransaction = (transactionHash) => isTransactionRegex.test(transactionHash); var toChecksumAddress2 = (address) => web3Utils.toChecksumAddress(address); var prepareHexPrefix = (value, addOrRemove) => { return value.startsWith("0x") ? addOrRemove ? value : value.substring(2) : addOrRemove ? "0x" + value : value; }; // src/utils/bridgeUtils.ts var bridgeUtils_exports = {}; __export(bridgeUtils_exports, { convertOperationDataToTokenTransferId: () => convertOperationDataToTokenTransferId, convertTokenTransferIdToOperationData: () => convertTokenTransferIdToOperationData, getInitialOperation: () => getInitialOperation, getTokenTransferIdOrInitialOperationHash: () => getTokenTransferIdOrInitialOperationHash, isBridgeTokenTransferOwner: () => isBridgeTokenTransferOwner, parseBridgeTokenTransfer: () => parseBridgeTokenTransfer, stringifyBridgeTokenTransfer: () => stringifyBridgeTokenTransfer }); // src/bridgeCore/bridgeOperations.ts var BridgeTokenTransferKind = /* @__PURE__ */ ((BridgeTokenTransferKind2) => { BridgeTokenTransferKind2[BridgeTokenTransferKind2["Deposit"] = 0] = "Deposit"; BridgeTokenTransferKind2[BridgeTokenTransferKind2["Withdrawal"] = 1] = "Withdrawal"; BridgeTokenTransferKind2[BridgeTokenTransferKind2["DepositRevert"] = 2] = "DepositRevert"; BridgeTokenTransferKind2[BridgeTokenTransferKind2["WithdrawalRevert"] = 3] = "WithdrawalRevert"; return BridgeTokenTransferKind2; })(BridgeTokenTransferKind || {}); var BridgeTokenTransferStatus = /* @__PURE__ */ ((BridgeTokenTransferStatus2) => { BridgeTokenTransferStatus2[BridgeTokenTransferStatus2["Pending"] = 0] = "Pending"; BridgeTokenTransferStatus2[BridgeTokenTransferStatus2["Created"] = 100] = "Created"; BridgeTokenTransferStatus2[BridgeTokenTransferStatus2["Sealed"] = 200] = "Sealed"; BridgeTokenTransferStatus2[BridgeTokenTransferStatus2["Finished"] = 300] = "Finished"; BridgeTokenTransferStatus2[BridgeTokenTransferStatus2["Failed"] = 400] = "Failed"; return BridgeTokenTransferStatus2; })(BridgeTokenTransferStatus || {}); // src/utils/bridgeUtils.ts var tokenTransferIdSeparator = "_"; var isEtherlinkTransaction = (operationHash) => operationHash.startsWith("0x"); var getInitialOperation = (tokenTransfer) => tokenTransfer.kind === 0 /* Deposit */ ? tokenTransfer.tezosOperation : tokenTransfer.etherlinkOperation; var getTokenTransferIdOrInitialOperationHash = (tokenTransfer) => tokenTransfer.status === 0 /* Pending */ ? getInitialOperation(tokenTransfer).hash : tokenTransfer.id; function convertOperationDataToTokenTransferId(operationHash, logIndexOrCounter, nonce) { return !isEtherlinkTransaction(operationHash) && typeof nonce === "number" ? `${operationHash}${tokenTransferIdSeparator}${logIndexOrCounter.toString(10)}${tokenTransferIdSeparator}${nonce.toString(10)}` : `${operationHash}${tokenTransferIdSeparator}${logIndexOrCounter.toString(10)}`; } var convertTokenTransferIdToOperationData = (tokenTransferId) => { if (!tokenTransferId) return null; try { const operationData = tokenTransferId.split(tokenTransferIdSeparator); if (!operationData[0] || !operationData[1]) return null; const counterOrLogIndex = Number.parseInt(operationData[1]); if (isEtherlinkTransaction(tokenTransferId)) return [operationData[0], counterOrLogIndex]; return operationData[2] ? [operationData[0], counterOrLogIndex, Number.parseInt(operationData[2])] : [operationData[0], counterOrLogIndex, null]; } catch { } return null; }; var isBridgeTokenTransferOwner = (tokenTransfer, address) => { return tokenTransfer.source === address || tokenTransfer.receiver === address; }; var operationFieldNames = ["tezosOperation", "etherlinkOperation"]; var bigIntFieldNames = ["amount"]; var jsonStringifyReplacer = (_key, value) => typeof value === "bigint" ? value.toString(10) : value; var stringifyBridgeTokenTransfer = (tokenTransfer, space) => { try { return JSON.stringify(tokenTransfer, jsonStringifyReplacer, space); } catch (error) { return ""; } }; var parseBridgeTokenTransfer = (tokenTransfer) => { try { const transfer = JSON.parse(tokenTransfer); for (const operationName of operationFieldNames) { if (transfer[operationName]) { for (const bigIntFieldName of bigIntFieldNames) { if (transfer[operationName][bigIntFieldName]) { transfer[operationName][bigIntFieldName] = BigInt(transfer[operationName][bigIntFieldName]); } } } } return transfer; } catch (error) { return null; } }; // src/utils/index.ts var emptyFunction = () => { }; // src/common/remoteService.ts var RemoteService = class { constructor(baseUrl) { __publicField(this, "baseUrl"); this.baseUrl = textUtils_exports.trimSlashes(baseUrl); } getUrl(uri) { return new URL(this.baseUrl + "/" + textUtils_exports.trimSlashes(uri)); } async getRequestInit(requestInit = {}) { const headers = new Headers(requestInit.headers); if (!headers.has("Accept")) headers.append("Accept", "application/json"); if (!headers.has("Content-Type")) headers.append("Content-Type", "application/json"); requestInit.headers = headers; return requestInit; } async fetch(uriOrUrl, responseFormat, requestInit, useDefaultRequestInitFields = true) { if (useDefaultRequestInitFields) requestInit = await this.getRequestInit(requestInit); const url = typeof uriOrUrl === "string" ? this.getUrl(uriOrUrl) : uriOrUrl; const response = await fetch(url.href, requestInit); await this.ensureResponseOk(response); return responseFormat === "none" ? void 0 : await (responseFormat === "json" ? response.json() : response.text()); } async ensureResponseOk(response) { if (response.ok) return; let content; try { content = await response.text(); } catch { content = "[unavailable]"; } throw new RemoteServiceResponseError(response.status, content); } }; // src/common/eventEmitter.ts var EventEmitter = class { constructor() { __publicField(this, "listeners", /* @__PURE__ */ new Set()); } addListener(listener) { this.listeners.add(listener); return this; } removeListener(listener) { if (this.listeners.has(listener)) this.listeners.delete(listener); return this; } removeAllListeners() { this.listeners = /* @__PURE__ */ new Set(); return this; } emit(...args) { if (!this.listeners.size) return; if (this.listeners.size === 1) { this.listeners.values().next().value(...args); } else { [...this.listeners].forEach((listener) => listener(...args)); } } }; // src/common/timeoutScheduler.ts var TimeoutScheduler = class { constructor(timeouts, counterExpirationMs) { this.timeouts = timeouts; this.counterExpirationMs = counterExpirationMs; __publicField(this, "counterExpirationWatcherId"); __publicField(this, "actionWatchers", /* @__PURE__ */ new Set()); __publicField(this, "_counter", 0); } get counter() { return this._counter; } set counter(value) { this._counter = value; } [Symbol.dispose]() { if (this.counterExpirationWatcherId) clearTimeout(this.counterExpirationWatcherId); this.actionWatchers.forEach((watcher) => clearTimeout(watcher)); } setTimeout(action) { return new Promise((resolve) => { if (this.counterExpirationMs) this.resetCounterExpiration(); const timeoutIndex = Math.min(this.counter, this.timeouts.length - 1); const timeout = this.timeouts[timeoutIndex]; const watcherId = setTimeout(async () => { this.actionWatchers.delete(watcherId); clearTimeout(watcherId); await action(); resolve(); }, timeout); this.actionWatchers.add(watcherId); this.counter++; }); } resetCounter() { this.counter = 0; } resetCounterExpiration() { if (this.counterExpirationWatcherId) clearTimeout(this.counterExpirationWatcherId); this.counterExpirationWatcherId = setTimeout(() => { this.resetCounter(); this.counterExpirationWatcherId = void 0; }, this.counterExpirationMs); } }; // src/logging/logMessages.ts var getErrorLogMessage = (error) => { if (!error) return `[error is ${error === null ? "null" : "undefined"}]`; if (typeof error === "string") return error; else if (typeof error?.message === "string") return error.message; return "[unknown error type]"; }; var getTokenLogMessage = tokenUtils_exports.toDisplayString; var getBridgeTokenTransferIdLogMessage = (bridgeTokenTransfer) => { return bridgeTokenTransfer ? bridgeTokenTransfer.status !== 0 /* Pending */ ? bridgeTokenTransfer.id : `none (${bridgeUtils_exports.getInitialOperation(bridgeTokenTransfer).hash})` : bridgeTokenTransfer === null ? "null" : "undefined"; }; var getNullOrUndefinedBridgeTokenTransferLogMessage = (bridgeTokenTransfer) => { return `Bridge Token transfer is ${bridgeTokenTransfer === null ? "null" : "undefined"}`; }; var getBridgeTokenTransferLogMessage = (bridgeTokenTransfer) => { return bridgeTokenTransfer ? `Bridge Token Transfer: Id: ${bridgeTokenTransfer && bridgeTokenTransfer.status !== 0 /* Pending */ ? bridgeTokenTransfer.id : "none"} Kind: ${BridgeTokenTransferKind[bridgeTokenTransfer.kind]} Status: ${BridgeTokenTransferStatus[bridgeTokenTransfer.status]} Source: ${bridgeTokenTransfer.source} Receiver: ${bridgeTokenTransfer.receiver} Tezos operation hash: ${bridgeTokenTransfer?.tezosOperation?.hash} Etherlink operation hash: ${bridgeTokenTransfer?.etherlinkOperation?.hash} ` : getNullOrUndefinedBridgeTokenTransferLogMessage(bridgeTokenTransfer); }; var getDetailedBridgeTokenTransferLogMessage = (bridgeTokenTransfer) => { return bridgeTokenTransfer ? `Bridge Token Transfer [${getBridgeTokenTransferIdLogMessage(bridgeTokenTransfer)}]: ${bridgeUtils_exports.stringifyBridgeTokenTransfer(bridgeTokenTransfer, 2)} ` : getNullOrUndefinedBridgeTokenTransferLogMessage(bridgeTokenTransfer); }; // src/logging/logger.ts var LogLevel = /* @__PURE__ */ ((LogLevel2) => { LogLevel2[LogLevel2["None"] = 0] = "None"; LogLevel2[LogLevel2["Debug"] = 1] = "Debug"; LogLevel2[LogLevel2["Information"] = 2] = "Information"; LogLevel2[LogLevel2["Warning"] = 3] = "Warning"; LogLevel2[LogLevel2["Error"] = 4] = "Error"; return LogLevel2; })(LogLevel || {}); var getEmptyLoggerFactory = (noneFunction) => ({ debug: noneFunction, log: noneFunction, warn: noneFunction, error: noneFunction, time: noneFunction, timeEnd: noneFunction, timeLog: noneFunction }); var emptyLogger = getEmptyLoggerFactory(emptyFunction); var emptyLazyLogger = getEmptyLoggerFactory(null); var getDebugLevelLogger = memoize( (loggerInternal) => ({ debug: loggerInternal.debug, log: loggerInternal.log, warn: loggerInternal.warn, error: loggerInternal.error, time: loggerInternal.time, timeEnd: loggerInternal.timeEnd, timeLog: loggerInternal.timeLog }) ); var getDebugLevelLazyLogger = getDebugLevelLogger; var getLogLevelLoggerFactory = (noneFunction) => memoize( (loggerInternal) => ({ debug: noneFunction, log: loggerInternal.log, warn: loggerInternal.warn, error: loggerInternal.error, time: loggerInternal.time, timeEnd: loggerInternal.timeEnd, timeLog: loggerInternal.timeLog }) ); var getLogLevelLogger = getLogLevelLoggerFactory(emptyFunction); var getLogLevelLazyLogger = getLogLevelLoggerFactory(null); var getWarnLevelLoggerFactory = (noneFunction) => memoize( (loggerInternal) => ({ debug: noneFunction, log: noneFunction, warn: loggerInternal.warn, error: loggerInternal.error, time: noneFunction, timeEnd: noneFunction, timeLog: noneFunction }) ); var getWarnLevelLogger = getWarnLevelLoggerFactory(emptyFunction); var getWarnLevelLazyLogger = getWarnLevelLoggerFactory(null); var getErrorLevelLoggerFactory = (noneFunction) => memoize( (loggerInternal) => ({ debug: noneFunction, log: noneFunction, warn: noneFunction, error: loggerInternal.error, time: noneFunction, timeEnd: noneFunction, timeLog: noneFunction }) ); var getErrorLevelLogger = getErrorLevelLoggerFactory(emptyFunction); var getErrorLevelLazyLogger = getErrorLevelLoggerFactory(null); var LoggerProvider = class { constructor(internalLogger, logLevel) { __publicField(this, "internalLogger"); __publicField(this, "_logLevel", 2 /* Information */); __publicField(this, "_logger", emptyLogger); __publicField(this, "_lazyLogger", emptyLazyLogger); this.internalLogger = internalLogger; this._logLevel = logLevel; this.updateLogger(); } get logger() { return this._logger; } get lazyLogger() { return this._lazyLogger; } get logLevel() { return this._logLevel; } setLogger(internalLogger) { this.internalLogger = internalLogger; this.updateLogger(); } setLogLevel(logLevel) { this._logLevel = logLevel; this.updateLogger(); } updateLogger() { switch (this._logLevel) { case 0 /* None */: this._logger = emptyLogger; this._lazyLogger = emptyLazyLogger; break; case 1 /* Debug */: this._logger = getDebugLevelLogger(this.internalLogger); this._lazyLogger = getDebugLevelLazyLogger(this.internalLogger); break; case 2 /* Information */: this._logger = getLogLevelLogger(this.internalLogger); this._lazyLogger = getLogLevelLazyLogger(this.internalLogger); break; case 3 /* Warning */: this._logger = getWarnLevelLogger(this.internalLogger); this._lazyLogger = getWarnLevelLazyLogger(this.internalLogger); break; case 4 /* Error */: this._logger = getErrorLevelLogger(this.internalLogger); this._lazyLogger = getErrorLevelLazyLogger(this.internalLogger); break; } } }; var loggerProvider = new LoggerProvider(console, 0 /* None */); // src/tokenBridge/errors.ts var TokenBridgeDisposed = class extends DisposedError { constructor() { super("The TokenBridge is disposed."); } }; var TokenPairNotFoundError = class _TokenPairNotFoundError extends TokenBridgeError { constructor(token) { super(_TokenPairNotFoundError.getMessage(token)); } static getMessage(token) { return `Token pair not found for the ${tokenUtils_exports.toDisplayString(token)} token`; } }; var InsufficientBalanceError = class _InsufficientBalanceError extends TokenBridgeError { constructor(token, address, balance, requested) { super(_InsufficientBalanceError.getMessage(token, address, balance, requested)); } static getMessage(token, address, balance, requested) { return `${address} has an insufficient balance ${tokenUtils_exports.toDisplayString(token)}. Balance = ${balance}. Requested = ${requested}`; } }; var FailedTokenTransferError = class _FailedTokenTransferError extends TokenBridgeError { constructor(tokenTransfer) { super(_FailedTokenTransferError.getMessage(tokenTransfer)); } static getMessage(tokenTransfer) { return `The ${getBridgeTokenTransferIdLogMessage(tokenTransfer)} token transfer is failed`; } }; var TezosSignerAccountUnavailableError = class extends TokenBridgeError { constructor() { super("The Tezos signer account is unavailable"); } }; var EtherlinkSignerAccountUnavailableError = class extends TokenBridgeError { constructor() { super("The Etherlink signer account is unavailable"); } }; // src/tokenBridge/tokenBridge.ts var _TokenBridge = class _TokenBridge { constructor(options) { __publicField(this, "data"); __publicField(this, "stream"); __publicField(this, "bridgeComponents"); __publicField(this, "events", { tokenTransferCreated: new EventEmitter(), tokenTransferUpdated: new EventEmitter() }); __publicField(this, "tokenTransferStatusWatchers", /* @__PURE__ */ new Map()); __publicField(this, "lastCreatedTokenTransfers", /* @__PURE__ */ new Map()); __publicField(this, "lastCreatedTokenTransfersTimerId"); __publicField(this, "_isDisposed", false); __publicField(this, "handleTransfersBridgeDataProviderTokenTransferCreated", (createdTokenTransfer) => { this.resolveStatusWatcherIfNeeded(createdTokenTransfer); this.emitTokenTransferCreatedOrUpdatedEvent(createdTokenTransfer); }); __publicField(this, "handleTransfersBridgeDataProviderTokenTransferUpdated", (updatedTokenTransfer) => { this.resolveStatusWatcherIfNeeded(updatedTokenTransfer); this.events.tokenTransferUpdated.emit(updatedTokenTransfer); }); this.bridgeComponents = { tezosBridgeBlockchainService: options.tezosBridgeBlockchainService, etherlinkBridgeBlockchainService: options.etherlinkBridgeBlockchainService, tokensBridgeDataProvider: options.bridgeDataProviders.tokens, balancesBridgeDataProvider: options.bridgeDataProviders.balances, transfersBridgeDataProvider: options.bridgeDataProviders.transfers }; this.data = { getBalance: this.getBalance.bind(this), getBalances: this.getBalances.bind(this), getRegisteredTokenPair: this.getRegisteredTokenPair.bind(this), getRegisteredTokenPairs: this.getRegisteredTokenPairs.bind(this), getTokenTransfer: this.getTokenTransfer.bind(this), getTokenTransfers: this.getTokenTransfers.bind(this), getAccountTokenTransfers: this.getAccountTokenTransfers.bind(this), getOperationTokenTransfers: this.getOperationTokenTransfers.bind(this), getSignerBalances: this.getSignerBalances.bind(this), getSignerTokenTransfers: this.getSignerTokenTransfers.bind(this) }; this.stream = { subscribeToTokenTransfer: this.subscribeToTokenTransfer.bind(this), subscribeToTokenTransfers: this.subscribeToTokenTransfers.bind(this), subscribeToAccountTokenTransfers: this.subscribeToAccountTokenTransfers.bind(this), subscribeToOperationTokenTransfers: this.subscribeToOperationTokenTransfers.bind(this), unsubscribeFromTokenTransfer: this.unsubscribeFromTokenTransfer.bind(this), unsubscribeFromTokenTransfers: this.unsubscribeFromTokenTransfers.bind(this), unsubscribeFromAccountTokenTransfers: this.unsubscribeFromAccountTokenTransfers.bind(this), unsubscribeFromOperationTokenTransfers: this.unsubscribeFromOperationTokenTransfers.bind(this), unsubscribeFromAllSubscriptions: this.unsubscribeFromAllSubscriptions.bind(this) }; this.attachEvents(); } get isDisposed() { return this._isDisposed; } addEventListener(type, listener) { this.ensureIsNotDisposed(); this.events[type].addListener(listener); } removeEventListener(type, listener) { this.ensureIsNotDisposed(); this.events[type].removeListener(listener); } removeAllEventListeners(type) { this.ensureIsNotDisposed(); this.events[type].removeAllListeners(); } async waitForStatus(transfer, status) { this.ensureIsNotDisposed(); if (transfer.status >= status) return transfer; const isPending = transfer.status === 0 /* Pending */; const updatedTransfers = await (isPending ? this.bridgeComponents.transfersBridgeDataProvider.getOperationTokenTransfers(transfer) : this.bridgeComponents.transfersBridgeDataProvider.getTokenTransfer(transfer.id)); const updatedTransfer = guards_exports.isArray(updatedTransfers) ? updatedTransfers[0] : updatedTransfers; if (updatedTransfer?.status === status) return updatedTransfer; const statusWatcherKey = isPending ? bridgeUtils_exports.getInitialOperation(transfer).hash : transfer.id; let statusWatchers = this.tokenTransferStatusWatchers.get(statusWatcherKey); if (!statusWatchers) { statusWatchers = /* @__PURE__ */ new Map(); this.tokenTransferStatusWatchers.set(statusWatcherKey, statusWatchers); } const watcher = statusWatchers.get(status); if (watcher) return watcher.promise; const watcherPromise = new Promise((resolve, reject) => { const statusWatchers2 = this.tokenTransferStatusWatchers.get(statusWatcherKey); if (!statusWatchers2) { reject(`Status watchers map not found for the ${statusWatcherKey} token transfer`); return; } setTimeout(() => { try { this.bridgeComponents.transfersBridgeDataProvider[isPending ? "subscribeToOperationTokenTransfers" : "subscribeToTokenTransfer"](statusWatcherKey); statusWatchers2.set(status, { promise: watcherPromise, resolve: (updatedTransfer2) => { this.bridgeComponents.transfersBridgeDataProvider[isPending ? "unsubscribeFromOperationTokenTransfers" : "unsubscribeFromTokenTransfer"](statusWatcherKey); resolve(updatedTransfer2); }, reject: (error) => { this.bridgeComponents.transfersBridgeDataProvider[isPending ? "unsubscribeFromOperationTokenTransfers" : "unsubscribeFromTokenTransfer"](statusWatcherKey); reject(error); } }); } catch (error) { loggerProvider.logger.error(getErrorLogMessage(error)); reject(error); } }, 0); }); return watcherPromise; } async deposit(amount, token, etherlinkReceiverAddressOrOptions, options) { this.ensureIsNotDisposed(); const tezosSourceAddress = await this.getRequiredTezosSignerAddress(); const etherlinkReceiverAddress = typeof etherlinkReceiverAddressOrOptions === "string" ? etherlinkReceiverAddressOrOptions : await this.getRequiredEtherlinkSignerAddress(); const depositOptions = typeof etherlinkReceiverAddressOrOptions !== "string" && etherlinkReceiverAddressOrOptions ? etherlinkReceiverAddressOrOptions : options; loggerProvider.lazyLogger.log?.( `Depositing ${amount.toString(10)} ${getTokenLogMessage(token)} from ${tezosSourceAddress} to ${etherlinkReceiverAddress}` ); const tokenPair = await this.bridgeComponents.tokensBridgeDataProvider.getRegisteredTokenPair(token); if (!tokenPair) { const error = new TokenPairNotFoundError(token); loggerProvider.logger.error(getErrorLogMessage(error)); throw error; } const accountTokenBalance = await this.bridgeComponents.balancesBridgeDataProvider.getBalance(tezosSourceAddress, token); if (amount > accountTokenBalance.balance) { const error = new InsufficientBalanceError(token, tezosSourceAddress, accountTokenBalance.balance, amount); loggerProvider.logger.error(getErrorLogMessage(error)); throw error; } loggerProvider.logger.log(`The ${tezosSourceAddress} has enough tokens to deposit ${amount}`); const operationResult = await (token.type === "native" ? this.bridgeComponents.tezosBridgeBlockchainService.depositNativeToken({ amount, etherlinkReceiverAddress, ticketHelperContractAddress: tokenPair.tezos.ticketHelperContractAddress }) : this.bridgeComponents.tezosBridgeBlockchainService.depositNonNativeToken({ token, amount, etherlinkReceiverAddress, ticketHelperContractAddress: tokenPair.tezos.ticketHelperContractAddress, useApprove: depositOptions?.useApprove, resetFA12Approve: depositOptions?.resetFA12Approve })); loggerProvider.logger.log("The deposit operation has been created:", operationResult.hash); const tokenTransfer = { kind: 0 /* Deposit */, status: 0 /* Pending */, source: tezosSourceAddress, receiver: etherlinkReceiverAddress, tezosOperation: { hash: operationResult.hash, amount: operationResult.amount, timestamp: operationResult.timestamp, token } }; loggerProvider.lazyLogger.log?.(getBridgeTokenTransferLogMessage(tokenTransfer)); loggerProvider.lazyLogger.debug?.(getDetailedBridgeTokenTransferLogMessage(tokenTransfer)); this.emitLocalTokenTransferCreatedEvent(tokenTransfer); return { tokenTransfer, operationResult }; } async startWithdraw(amount, token, tezosReceiverAddress) { this.ensureIsNotDisposed(); const etherlinkSourceAddress = await this.getRequiredEtherlinkSignerAddress(); if (!tezosReceiverAddress) { tezosReceiverAddress = await this.getRequiredTezosSignerAddress(); } const tokenPair = await this.bridgeComponents.tokensBridgeDataProvider.getRegisteredTokenPair(token); if (!tokenPair) { const error = new TokenPairNotFoundError(token); loggerProvider.logger.error(getErrorLogMessage(error)); throw error; } const accountTokenBalance = await this.bridgeComponents.balancesBridgeDataProvider.getBalance(etherlinkSourceAddress, token); if (amount > accountTokenBalance.balance) { const error = new InsufficientBalanceError(token, etherlinkSourceAddress, accountTokenBalance.balance, amount); loggerProvider.logger.error(getErrorLogMessage(error)); throw error; } loggerProvider.logger.log(`The ${etherlinkSourceAddress} has enough tokens to withdraw ${amount}`); let operationResult; if (tokenPair.tezos.type === "native") { operationResult = await this.bridgeComponents.etherlinkBridgeBlockchainService.withdrawNativeToken({ amount, tezosReceiverAddress }); } else { const tezosTicketerAddress = tokenPair.tezos.ticketerContractAddress; const tezosTicketerContent = await this.bridgeComponents.tezosBridgeBlockchainService.getTezosTicketerContent(tezosTicketerAddress); operationResult = await this.bridgeComponents.etherlinkBridgeBlockchainService.withdrawNonNativeToken({ amount, tezosReceiverAddress, tezosTicketerAddress, tezosTicketerContent, token }); } const bridgeTokenWithdrawal = { kind: 1 /* Withdrawal */, status: 0 /* Pending */, source: etherlinkSourceAddress, receiver: tezosReceiverAddress, etherlinkOperation: { hash: operationResult.hash, amount, timestamp: operationResult.timestamp, token } }; this.emitLocalTokenTransferCreatedEvent(bridgeTokenWithdrawal); return { tokenTransfer: bridgeTokenWithdrawal, operationResult }; } async finishWithdraw(sealedBridgeTokenWithdrawal) { this.ensureIsNotDisposed(); const operationResult = await this.bridgeComponents.tezosBridgeBlockchainService.finishWithdraw({ cementedCommitment: sealedBridgeTokenWithdrawal.rollupData.commitment, outputProof: sealedBridgeTokenWithdrawal.rollupData.proof }); return { tokenTransfer: sealedBridgeTokenWithdrawal, operationResult }; } async getTezosSignerAddress() { this.ensureIsNotDisposed(); return this.bridgeComponents.tezosBridgeBlockchainService.getSignerAddress(); } async getEtherlinkSignerAddress() { this.ensureIsNotDisposed(); return this.bridgeComponents.etherlinkBridgeBlockchainService.getSignerAddress(); } [Symbol.dispose]() { if (this._isDisposed) return; if (guards_exports.isDisposable(this.bridgeComponents.tokensBridgeDataProvider)) this.bridgeComponents.tokensBridgeDataProvider[Symbol.dispose](); if (guards_exports.isDisposable(this.bridgeComponents.balancesBridgeDataProvider)) this.bridgeComponents.balancesBridgeDataProvider[Symbol.dispose](); if (guards_exports.isDisposable(this.bridgeComponents.transfersBridgeDataProvider)) this.bridgeComponents.transfersBridgeDataProvider[Symbol.dispose](); this.rejectAndClearAllStatusWatchers(new TokenBridgeDisposed()); this.detachEvents(); clearInterval(this.lastCreatedTokenTransfersTimerId); this.lastCreatedTokenTransfers.clear(); this._isDisposed = true; } // #region Data API getBalance(accountAddress, token) { return this.bridgeComponents.balancesBridgeDataProvider.getBalance(accountAddress, token); } getBalances(accountAddress, tokensOfFetchOptions) { return this.bridgeComponents.balancesBridgeDataProvider.getBalances(accountAddress, tokensOfFetchOptions); } getRegisteredTokenPair(token) { return this.bridgeComponents.tokensBridgeDataProvider.getRegisteredTokenPair(token); } getRegisteredTokenPairs(fetchOptions) { return this.bridgeComponents.tokensBridgeDataProvider.getRegisteredTokenPairs(fetchOptions); } getTokenTransfer(tokenTransferId) { return this.bridgeComponents.transfersBridgeDataProvider.getTokenTransfer(tokenTransferId); } getTokenTransfers(fetchOptions) { return this.bridgeComponents.transfersBridgeDataProvider.getTokenTransfers(fetchOptions); } getAccountTokenTransfers(accountAddressOrAddresses, fetchOptions) { return this.bridgeComponents.transfersBridgeDataProvider.getAccountTokenTransfers(accountAddressOrAddresses, fetchOptions); } getOperationTokenTransfers(operationHashOrTokenTransfer) { return this.bridgeComponents.transfersBridgeDataProvider.getOperationTokenTransfers(operationHashOrTokenTransfer); } async getSignerBalances() { const [tezosSignerBalances, etherlinkSignerBalances] = await Promise.all([ this.getTezosSignerAddress().then((signerAddress) => signerAddress ? this.getBalances(signerAddress) : void 0), this.getEtherlinkSignerAddress().then((signerAddress) => signerAddress ? this.getBalances(signerAddress) : void 0) ]); return { tezosSignerBalances, etherlinkSignerBalances }; } async getSignerTokenTransfers(fetchOptions) { const [tezosSignerAddress, etherlinkSignerAddress] = await Promise.all([ this.getTezosSignerAddress(), this.getEtherlinkSignerAddress() ]); const addresses = tezosSignerAddress && etherlinkSignerAddress ? [tezosSignerAddress, etherlinkSignerAddress] : tezosSignerAddress || etherlinkSignerAddress; return addresses ? this.getAccountTokenTransfers(addresses, fetchOptions) : []; } // #endregion // #region Stream API subscribeToTokenTransfer(tokenTransferId) { this.bridgeComponents.transfersBridgeDataProvider.subscribeToTokenTransfer(tokenTransferId); } unsubscribeFromTokenTransfer(tokenTransferId) { this.bridgeComponents.transfersBridgeDataProvider.unsubscribeFromTokenTransfer(tokenTransferId); } subscribeToTokenTransfers() { this.bridgeComponents.transfersBridgeDataProvider.subscribeToTokenTransfers(); } unsubscribeFromTokenTransfers() { this.bridgeComponents.transfersBridgeDataProvider.unsubscribeFromTokenTransfers(); } subscribeToAccountTokenTransfers(accountAddressOrAddresses) { this.bridgeComponents.transfersBridgeDataProvider.subscribeToAccountTokenTransfers(accountAddressOrAddresses); } unsubscribeFromAccountTokenTransfers(accountAddressOrAddresses) { this.bridgeComponents.transfersBridgeDataProvider.unsubscribeFromAccountTokenTransfers(accountAddressOrAddresses); } subscribeToOperationTokenTransfers(operationHashOrTokenTransfer) { this.bridgeComponents.transfersBridgeDataProvider.subscribeToOperationTokenTransfers(operationHashOrTokenTransfer); } unsubscribeFromOperationTokenTransfers(operationHashOrTokenTransfer) { this.bridgeComponents.transfersBridgeDataProvider.unsubscribeFromOperationTokenTransfers(operationHashOrTokenTransfer); } unsubscribeFromAllSubscriptions() { this.bridgeComponents.transfersBridgeDataProvider.unsubscribeFromAllSubscriptions(); } // #endregion emitLocalTokenTransferCreatedEvent(tokenTransfer) { setTimeout(() => { this.emitTokenTransferCreatedOrUpdatedEvent(tokenTransfer); }, 0); } emitTokenTransferCreatedOrUpdatedEvent(tokenTransfer) { this.ensureLastCreatedTokenTransfersTimerIsStarted(); const initialOperationHash = bridgeUtils_exports.getInitialOperation(tokenTransfer).hash; let eventName; if (this.lastCreatedTokenTransfers.has(initialOperationHash)) { eventName = "tokenTransferUpdated"; } else { eventName = "tokenTransferCreated"; this.lastCreatedTokenTransfers.set(initialOperationHash, [tokenTransfer, Date.now()]); } loggerProvider.logger.log(`Emitting the ${eventName} event...`); this.events[eventName].emit(tokenTransfer); loggerProvider.logger.log(`The ${eventName} event has been emitted`); } resolveStatusWatcherIfNeeded(tokenTransfer) { let statusWatcherKey; let statusWatchers; if (tokenTransfer.status !== 0 /* Pending */) { statusWatcherKey = tokenTransfer.id; statusWatchers = this.tokenTransferStatusWatchers.get(tokenTransfer.id); } if (!statusWatchers) { statusWatcherKey = bridgeUtils_exports.getInitialOperation(tokenTransfer).hash; statusWatchers = this.tokenTransferStatusWatchers.get(statusWatcherKey); if (!statusWatchers) return; } for (const [status, watcher] of statusWatchers) { if (tokenTransfer.status >= status) { if (tokenTransfer.status === 400 /* Failed */) watcher.reject(new FailedTokenTransferError(tokenTransfer)); else watcher.resolve(tokenTransfer); statusWatchers.delete(status); } } if (!statusWatchers.size) this.tokenTransferStatusWatchers.delete(statusWatcherKey); } rejectAndClearAllStatusWatchers(error) { for (const statusWatchers of this.tokenTransferStatusWatchers.values()) { for (const statusWatcher of statusWatchers.values()) { statusWatcher.reject(error); } statusWatchers.clear(); } this.tokenTransferStatusWatchers.clear(); } attachEvents() { this.bridgeComponents.transfersBridgeDataProvider.events.tokenTransferCreated.addListener(this.handleTransfersBridgeDataProviderTokenTransferCreated); this.bridgeComponents.transfersBridgeDataProvider.events.tokenTransferUpdated.addListener(this.handleTransfersBridgeDataProviderTokenTransferUpdated); } detachEvents() { this.bridgeComponents.transfersBridgeDataProvider.events.tokenTransferUpdated.removeListener(this.handleTransfersBridgeDataProviderTokenTransferUpdated); this.bridgeComponents.transfersBridgeDataProvider.events.tokenTransferCreated.removeListener(this.handleTransfersBridgeDataProviderTokenTransferCreated); } ensureIsNotDisposed() { if (!this._isDisposed) return; loggerProvider.logger.error("Attempting to call the disposed TokenBridge instance"); throw new TokenBridgeDisposed(); } ensureLastCreatedTokenTransfersTimerIsStarted() { if (this.lastCreatedTokenTransfersTimerId) return; this.lastCreatedTokenTransfersTimerId = setInterval( () => { const currentTime = Date.now(); for (const entry of this.lastCreatedTokenTransfers) { if (currentTime - entry[1][1] > _TokenBridge.defaultLastCreatedTokenTransferLifetime) { this.lastCreatedTokenTransfers.delete(entry[0]); } } }, _TokenBridge.defaultLastCreatedTokenTransfersTimerPeriod ); } async getRequiredTezosSignerAddress() { const tezosSignerAddress = await this.getTezosSignerAddress(); if (tezosSignerAddress) return tezosSignerAddress; throw new TezosSignerAccountUnavailableError(); } async getRequiredEtherlinkSignerAddress() { const tezosSignerAddress = await this.getEtherlinkSignerAddress(); if (tezosSignerAddress) return tezosSignerAddress; throw new EtherlinkSignerAccountUnavailableError(); } }; __publicField(_TokenBridge, "defaultLastCreatedTokenTransfersTimerPeriod", 6e4); __publicField(_TokenBridge, "defaultLastCreatedTokenTransferLifetime", 3e4); var TokenBridge = _TokenBridge; // src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/taquitoTezosBridgeBlockchainService.ts import { packDataBytes } from "@taquito/michel-codec"; import { Schema } from "@taquito/michelson-encoder"; // src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/errors.ts var TezosSignerAccountUnavailableError2 = class extends TokenBridgeError { constructor() { super("The Tezos signer account is unavailable"); } }; // src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/helpers/fa12helper.ts var fa12helper_exports = {}; __export(fa12helper_exports, { wrapContractCallsWithApprove: () => wrapContractCallsWithApprove }); function wrapContractCallsWithApprove(options) { const batch = options.batch; if (options.isNeedToReset === void 0 || options.isNeedToReset) batch.withContractCall(options.tokenContract.methods.approve(options.approvedAddress, 0n)); batch.withContractCall(options.tokenContract.methods.approve(options.approvedAddress, options.approvedAmount)); if (Array.isArray(options.contractCalls)) options.contractCalls.forEach((call) => batch.withContractCall(call)); else batch.withContractCall(options.contractCalls); return batch; } // src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/helpers/fa2helper.ts var fa2helper_exports = {}; __export(fa2helper_exports, { wrapContractCallsWithApprove: () => wrapContractCallsWithApprove2 }); function wrapContractCallsWithApprove2(options) { const batch = options.batch.withContractCall(options.tokenContract.methods.update_operators([{ add_operator: { owner: options.ownerAddress, operator: options.approvedAddress, token_id: options.tokenId } }])); if (Array.isArray(options.contractCalls)) options.contractCalls.forEach((call) => batch.withContractCall(call)); else batch.withContractCall(options.contractCalls); batch.withContractCall(options.tokenContract.methods.update_operators([{ remove_operator: { owner: options.ownerAddress, operator: options.approvedAddress, token_id: options.tokenId } }])); return batch; } // src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/tezosTicketerContentMichelsonType.ts var tezosTicketerContentMichelsonType = { prim: "pair", annots: ["%content"], args: [ { prim: "nat" }, { prim: "option", args: [{ prim: "bytes" }] } ] }; // src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/taquitoTezosBridgeBlockchainService.ts var TaquitoTezosBridgeBlockchainService = class { constructor(options) { __publicField(this, "smartRollupAddress"); __publicField(this, "tezosToolkit"); __publicField(this, "tezosTicketerContentSchema", new Schema(tezosTicketerContentMichelsonType)); this.smartRollupAddress = options.smartRollupAddress; this.tezosToolkit = options.tezosToolkit; } async getSignerAddress() { try { return await this.tezosToolkit.signer.publicKeyHash(); } catch (error) { loggerProvider.logger.error(getErrorLogMessage(error)); return void 0; } } async createDepositNativeTokenOperation(params) { const ticketHelperContract = await this.getNativeTokenTicketHelperContract(params.ticketHelperContractAddress); const routingInfo = this.packDepositRoutingInfo(params.etherlinkReceiverAddress); const operation = ticketHelperContract.methodsObject.deposit({ evm_address: this.smartRollupAddress, l2_address: routingInfo }); return operation; } async createDepositNonNativeTokenOperation(params) { const ticketHelperContract = await this.getNonNativeTokenTicketHelperContract(params.ticketHelperContractAddress); const routingInfo = this.packDepositRoutingInfo(params.etherlinkReceiverAddress); const operation = ticketHelperContract.methodsObject.deposit({ rollup: this.smartRollupAddress, receiver: routingInfo, amount: params.amount }); return operation; } async getTezosTicketerContent(tezosTicketerAddress) { const storage = await this.tezosToolkit.contract.getStorage(tezosTicketerAddress); const content = [...Object.values(storage.content)]; const contentMichelsonData = this.tezosTicketerContentSchema.Encode(content); return "0x" + packDataBytes(contentMichelsonData, tezosTicketerContentMichelsonType).bytes.slice(2); } async depositNativeTokenInternal(params) { const depositOperation = await this.createDepositNativeTokenOperation(params); return depositOperation.send({ amount: Number(params.amount), mutez: true }); } async depositNonNativeTokenInternal(params) { const useApprove = params.useApprove ?? true; const depositOperation = await this.createDepositNonNativeTokenOperation(params); let batch = this.createBatch(); if (useApprove) { batch = await (params.token.type === "fa2" ? this.wrapContractCallsWithFA2TokenApprove( batch, depositOperation, params.token, params.ticketHelperContractAddress ) : this.wrapContractCallsWithFA12TokenApprove( batch, depositOperation, params.amount, params.token, params.ticketHelperContractAddress, params.resetFA12Approve ?? true )); } return batch.send(); } finishWithdrawInternal(params) { return this.tezosToolkit.contract.smartRollupExecuteOutboxMessage({ rollup: this.smartRollupAddress, cementedCommitment: params.cementedCommitment, outputProof: params.outputProof }); } getCurrentOperationTimestamp() { return (/* @__PURE__ */ new Date()).toISOString(); } async wrapContractCallsWithFA12TokenApprove(batch, contractCalls, amount, token, ticketHelperContractAddress, isNeedToReset) { const tokenContract = await this.getFA12TokenContract(token.address); const resultOperation = fa12helper_exports.wrapContractCallsWithApprove({ batch, approvedAddress: ticketHelperContractAddress, approvedAmount: amount, tokenContract, contractCalls, isNeedToReset }); return resultOperation; } async wrapContractCallsWithFA2TokenApprove(batch, contractCalls, token, ticketHelperContractAddress) { const [tokenContract, tokenOwnerAddress] = await Promise.all([ this.getFA2TokenContract(token.address), this.getSignerAddress() ]); if (!tokenOwnerAddress) throw new TezosSignerAccountUnavailableError2(); const resultOperation = fa2helper_exports.wrapContractCallsWithApprove({ batch, approvedAddress: ticketHelperContractAddress, ownerAddress: tokenOwnerAddress, tokenId: token.tokenId, tokenContract, contractCalls }); return resultOperation; } packDepositRoutingInfo(etherlinkReceiverAddress) { return etherlinkUtils_exports.prepareHexPrefix(etherlinkReceiverAddress, false); } }; // src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/taquitoContractTezosBridgeBlockchainService.ts var TaquitoContractTezosBridgeBlockchainService = class extends TaquitoTezosBridgeBlockchainService { async depositNativeToken(params) { const operation = await this.depositNativeTokenInternal(params); return { amount: params.amount, hash: operation.hash, timestamp: this.getCurrentOperationTimestamp(), operation }; } async depositNonNativeToken(params) { const operation = await this.depositNonNativeTokenInternal(params); return { amount: params.amount, hash: operation.hash, timestamp: this.getCurrentOperationTimestamp(), operation }; } async finishWithdraw(params) { const operation = await this.finishWithdrawInternal(params); return { hash: operation.hash, timestamp: this.getCurrentOperationTimestamp(), operation }; } createBatch(params) { return this.tezosToolkit.contract.batch(params); } getNativeTokenTicketHelperContract(ticketHelperContractAddress) { return this.tezosToolkit.contract.at(ticketHelperContractAddress); } getNonNativeTokenTicketHelperContract(ticketHelperContractAddress) { return this.tezosToolkit.contract.at(ticketHelperContractAddress); } getFA12TokenContract(fa12TokenContractAddress) { return this.tezosToolkit.contract.at(fa12TokenContractAddress); } getFA2TokenContract(fa2TokenContractAddress) { return this.tezosToolkit.contract.at(fa2TokenContractAddress); } }; // src/bridgeBlockchainService/taquitoTezosBridgeBlockchainService/taquitoWalletTezosBridgeBlockchainService.ts var TaquitoWalletTezosBridgeBlockchainService = class extends TaquitoTezosBridgeBlockchainService { async depositNativeToken(params) { const operation = await this.depositNativeTokenInternal(params); return { amount: params.amount, hash: operation.opHash, timestamp: this.getCurrentOperationTimestamp(), operation }; } async depositNonNativeToken(params) { const operation = await this.depositNonNativeTokenInternal(params); return { amount: params.amount, hash: operation.opHash, timestamp: this.getCurrentOperationTimestamp(), operation }; } async finishWithdraw(params) { const operation = await this.finishWithdrawInternal(params); return { hash: operation.hash, timestamp: this.getCurrentOperationTimestamp(), operation }; } createBatch(params) { return this.tezosToolkit.wallet.batch(params); } getNativeTokenTicketHelperContract(ticketHelperCon