95 lines
3.8 KiB
JavaScript
95 lines
3.8 KiB
JavaScript
/**
|
|
* Tezos relay: listen TezosBridgeInitiated -> (mock) Tezos mint/transfer -> confirmTransaction.
|
|
*/
|
|
import { ethers } from 'ethers';
|
|
import { config } from './config.js';
|
|
import { TezosAdapterABI } from './abis.js';
|
|
import * as metrics from './metrics.js';
|
|
|
|
export class TezosRelayService {
|
|
constructor(logger) {
|
|
this.logger = logger;
|
|
this.provider = null;
|
|
this.adapter = null;
|
|
this.signer = null;
|
|
this.queue = [];
|
|
this.inFlight = 0;
|
|
}
|
|
|
|
async start() {
|
|
if (!config.tezosAdapterAddress) {
|
|
throw new Error('TEZOS_ADAPTER_ADDRESS required');
|
|
}
|
|
if (!config.oraclePrivateKey) {
|
|
throw new Error('TEZOS_RELAY_ORACLE_KEY or PRIVATE_KEY required');
|
|
}
|
|
this.provider = new ethers.JsonRpcProvider(config.sourceChain.rpcUrl);
|
|
this.signer = new ethers.Wallet(config.oraclePrivateKey, this.provider);
|
|
this.adapter = new ethers.Contract(config.tezosAdapterAddress, TezosAdapterABI, this.signer);
|
|
this.logger.info('Tezos relay started', { adapter: config.tezosAdapterAddress });
|
|
this.adapter.on('TezosBridgeInitiated', (requestId, sender, token, amount, destination) => {
|
|
this.queue.push({ requestId, sender, token, amount, destination });
|
|
metrics.incrementEventsDetected();
|
|
metrics.setPendingRequests(this.queue.length + this.inFlight);
|
|
this.processQueue();
|
|
});
|
|
}
|
|
|
|
async processQueue() {
|
|
while (this.queue.length > 0 && this.inFlight < config.maxConcurrent) {
|
|
const item = this.queue.shift();
|
|
this.inFlight++;
|
|
metrics.setPendingRequests(this.queue.length + this.inFlight);
|
|
this.handleRequest(item).finally(() => {
|
|
this.inFlight--;
|
|
metrics.setPendingRequests(this.queue.length + this.inFlight);
|
|
});
|
|
}
|
|
}
|
|
|
|
async handleRequest({ requestId, sender, token, amount, destination }) {
|
|
try {
|
|
let tezosTxHash;
|
|
if (config.mockTezosRelay) {
|
|
tezosTxHash = `mock_${requestId.slice(0, 10)}_${Date.now()}`;
|
|
this.logger.info('Mock Tezos tx', { requestId, destination, tezosTxHash });
|
|
} else {
|
|
tezosTxHash = await this.performTezosMintOrTransfer(requestId, sender, token, amount, destination);
|
|
}
|
|
await this.adapter.confirmTransaction(requestId, tezosTxHash);
|
|
metrics.incrementConfirmationsSubmitted();
|
|
} catch (err) {
|
|
this.logger.error('confirmTransaction failed', { requestId, error: err.message });
|
|
metrics.incrementConfirmationsFailed();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Perform actual Tezos mint/transfer via Taquito or Tezos RPC.
|
|
* Real mint: set MOCK_TEZOS_RELAY=false, TEZOS_MINTER_ADDRESS, TEZOS_RPC_URL, TEZOS_ORACLE_SECRET_KEY (edsk...).
|
|
* When @taquito/taquito is installed and config is set, calls minter contract mintOrTransfer(destination, amount); otherwise returns mock hash.
|
|
*/
|
|
async performTezosMintOrTransfer(requestId, sender, token, amount, destination) {
|
|
const { config } = await import('./config.js');
|
|
if (!config.tezosRpcUrl) {
|
|
return `mock_${requestId.slice(0, 10)}_${Date.now()}`;
|
|
}
|
|
try {
|
|
const { Tezos } = await import('@taquito/taquito');
|
|
const { InMemorySigner } = await import('@taquito/signer');
|
|
const tezosSecret = process.env.TEZOS_ORACLE_SECRET_KEY || config.oraclePrivateKey;
|
|
if (config.tezosMinterAddress && tezosSecret) {
|
|
Tezos.setProvider({ rpc: config.tezosRpcUrl });
|
|
Tezos.setSignerProvider(await InMemorySigner.fromSecretKey(tezosSecret));
|
|
const contract = await Tezos.wallet.at(config.tezosMinterAddress);
|
|
const op = await contract.methods.mintOrTransfer(destination, amount.toString()).send();
|
|
await op.confirmation(1);
|
|
return op.hash;
|
|
}
|
|
} catch (e) {
|
|
this.logger.warn('Taquito mint/transfer failed, using mock hash', { error: e.message });
|
|
}
|
|
return `mock_${requestId.slice(0, 10)}_${Date.now()}`;
|
|
}
|
|
}
|