kleros-escrow-data-service
Version:
Data service for interacting with Kleros Escrow
162 lines (161 loc) • 7.09 kB
JavaScript
import { ethers } from "ethers";
import { TokenTransactionStatus } from "../types/token";
import { BaseService } from "../base/BaseService";
/**
* Service for reading token transaction data from the Kleros Escrow Token contract
*/
export class TokenTransactionService extends BaseService {
/**
* Creates a new TokenTransactionService instance
* @param config The Kleros Escrow configuration
* @param provider Optional provider for read operations
*/
constructor(config, provider) {
super(config, provider);
/**
* Gets a token transaction by its ID
* @param transactionId The ID of the transaction to fetch
* @returns The token transaction data
*/
this.getTokenTransaction = async (transactionId) => {
const tx = await this.tokenContract.transactions(transactionId);
return {
id: transactionId,
sender: tx.sender,
receiver: tx.receiver,
amount: tx.amount.toString(),
token: tx.token, // ERC20 token contract address
status: this.mapStatus(tx.status),
timeoutPayment: tx.timeoutPayment.toNumber(),
lastInteraction: tx.lastInteraction.toNumber(),
createdAt: 0, // Not directly available from contract, would need to get from events
disputeId: tx.disputeId.toNumber() > 0 ? tx.disputeId.toNumber() : undefined,
senderFee: tx.senderFee.toString(),
receiverFee: tx.receiverFee.toString(),
};
};
/**
* Gets all token transactions for a specific address
* @param address The address to get transactions for
* @returns Array of token transactions where the address is sender or receiver
*/
this.getTransactionsByAddress = async (address) => {
const transactionIds = await this.tokenContract.getTransactionIDsByAddress(address);
const transactions = [];
for (const id of transactionIds) {
const tx = await this.getTokenTransaction(id.toString());
transactions.push(tx);
}
return transactions;
};
/**
* Gets the total number of token transactions in the contract
* @returns The count of transactions
*/
this.getTransactionCount = async () => {
const count = await this.tokenContract.getCountTransactions();
return count.toNumber();
};
/**
* Checks if a token transaction can be executed (timeout has passed)
* @param transactionId The ID of the transaction to check
* @returns True if the transaction can be executed
*/
this.canExecuteTransaction = async (transactionId) => {
const tx = await this.getTokenTransaction(transactionId);
const currentTime = Math.floor(Date.now() / 1000);
return (tx.status === TokenTransactionStatus.NoDispute &&
currentTime - tx.lastInteraction >= tx.timeoutPayment);
};
/**
* Checks if a party can be timed out for not paying arbitration fees
* @param transactionId The ID of the transaction to check
* @returns Object indicating which party can be timed out, if any
*/
this.canTimeOut = async (transactionId) => {
const tx = await this.getTokenTransaction(transactionId);
const currentTime = Math.floor(Date.now() / 1000);
const feeTimeout = await this.tokenContract.feeTimeout();
return {
canSenderTimeOut: tx.status === TokenTransactionStatus.WaitingReceiver &&
currentTime - tx.lastInteraction >= feeTimeout,
canReceiverTimeOut: tx.status === TokenTransactionStatus.WaitingSender &&
currentTime - tx.lastInteraction >= feeTimeout,
};
};
/**
* Get basic ERC20 token information
* @param tokenAddress The ERC20 token contract address
* @returns Token information (name, symbol, decimals)
*/
this.getTokenInfo = async (tokenAddress) => {
const tokenAbi = [
"function name() view returns (string)",
"function symbol() view returns (string)",
"function decimals() view returns (uint8)",
];
const tokenContract = new ethers.Contract(tokenAddress, tokenAbi, this.provider);
try {
const [name, symbol, decimals] = await Promise.all([
tokenContract.name(),
tokenContract.symbol(),
tokenContract.decimals(),
]);
return {
name,
symbol,
decimals,
};
}
catch (error) {
console.warn(`Could not get token info for ${tokenAddress}:`, error);
return {
name: "Unknown",
symbol: "UNKNOWN",
decimals: 18,
};
}
};
/**
* Gets the fee timeout period from the token contract
* @returns The fee timeout in seconds
*/
this.getFeeTimeout = async () => {
const timeout = await this.tokenContract.feeTimeout();
return timeout.toNumber();
};
/**
* Gets the arbitrator address from the token contract
* @returns The arbitrator contract address
*/
this.getArbitratorAddress = async () => {
return await this.tokenContract.arbitrator();
};
/**
* Gets the arbitrator extra data from the token contract
* @returns The arbitrator extra data as bytes
*/
this.getArbitratorExtraData = async () => {
return await this.tokenContract.arbitratorExtraData();
};
/**
* Maps numeric status from contract to enum
* @param status The numeric status from the contract
* @returns The corresponding TokenTransactionStatus enum value
*/
this.mapStatus = (status) => {
const statusMap = {
0: TokenTransactionStatus.NoDispute,
1: TokenTransactionStatus.WaitingSender,
2: TokenTransactionStatus.WaitingReceiver,
3: TokenTransactionStatus.DisputeCreated,
4: TokenTransactionStatus.Resolved,
};
return statusMap[status] || TokenTransactionStatus.NoDispute;
};
if (!config.multipleArbitrableTransactionToken) {
throw new Error("multipleArbitrableTransactionToken configuration is required");
}
this.tokenContract = new ethers.Contract(config.multipleArbitrableTransactionToken.address, config.multipleArbitrableTransactionToken.abi, provider || this.provider);
}
}