Files
P2Pix-Front-End/src/blockchain/wallet.ts
Arthur Abeilice 00390ab0c3 chore(knip): add config and remove dead code/deps
- knip.json: scope to src, ignore submodule/worktrees, mark
  generated abi.ts as unresolved-allowed, honor @public JSDoc tag
- drop 14 orphaned files (12 ui components, model/Bank, model/Pix)
- drop 18 unused deps (urql, tanstack, wagmi/{core,vue}, graphql,
  permissionless, graphql-codegen suite, axe-core, lighthouse,
  vue/test-utils)
- drop 4 unused exports and de-export 9 internal-only types
- mark NetworksTestnet as @public (in-flight testnet support)
2026-06-02 01:41:01 +00:00

401 lines
11 KiB
TypeScript

import { formatEther, type Address } from 'viem';
import { useUser } from '@/composables/useUser';
import { getPublicClient, getWalletClient, getContract } from './provider';
import { getValidDeposits, getUnreleasedLockById } from './events';
import type { ValidDeposit } from '@/model/ValidDeposit';
import type { WalletTransaction } from '@/model/WalletTransaction';
import type { UnreleasedLock } from '@/model/UnreleasedLock';
import { LockStatus } from '@/model/LockStatus';
export const updateWalletStatus = async (): Promise<void> => {
const user = useUser();
const publicClient = getPublicClient();
const walletClient = getWalletClient();
if (!publicClient || !walletClient) {
console.error('Client not initialized');
return;
}
// Get balance
const [account] = await walletClient.getAddresses();
const balance = await publicClient.getBalance({ address: account });
user.setWalletAddress(account);
user.setBalance(formatEther(balance));
};
export const listValidDepositTransactionsByWalletAddress = async (
walletAddress: Address,
): Promise<ValidDeposit[]> => {
const user = useUser();
const walletDeposits = await getValidDeposits(
user.network.value.tokens[user.selectedToken.value].address,
user.network.value,
);
if (walletDeposits) {
return walletDeposits
.filter((deposit) => deposit.seller == walletAddress)
.sort((a: ValidDeposit, b: ValidDeposit) => {
return b.blockNumber - a.blockNumber;
});
}
return [];
};
const getLockStatus = async (id: bigint): Promise<LockStatus> => {
const { address, abi, client } = await getContract();
const [sortedIDs, status] = await client.readContract({
address,
abi,
functionName: 'getLocksStatus',
args: [[id]],
});
return status[0];
};
export const listAllTransactionByWalletAddress = async (
walletAddress: Address,
): Promise<WalletTransaction[]> => {
const user = useUser();
// Get the current network for the subgraph URL
const network = user.network.value;
// Query subgraph for all relevant transactions
const subgraphQuery = {
query: `
{
depositAddeds(where: {seller: "${walletAddress.toLowerCase()}"}) {
id
seller
token
amount
blockTimestamp
blockNumber
transactionHash
}
lockAddeds(where: {buyer: "${walletAddress.toLowerCase()}"}) {
buyer
lockID
seller
amount
blockTimestamp
blockNumber
transactionHash
}
lockReleaseds(where: {buyer: "${walletAddress.toLowerCase()}"}) {
buyer
lockId
blockTimestamp
blockNumber
transactionHash
}
depositWithdrawns(where: {seller: "${walletAddress.toLowerCase()}"}) {
seller
token
amount
blockTimestamp
blockNumber
transactionHash
}
}
`,
};
const response = await fetch(network.subgraphUrls[0], {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(subgraphQuery),
});
const data = await response.json();
// Convert all transactions to common WalletTransaction format
const transactions: WalletTransaction[] = [];
// Process deposit added events
if (data.data?.depositAddeds) {
for (const deposit of data.data.depositAddeds) {
transactions.push({
token: deposit.token,
blockNumber: parseInt(deposit.blockNumber),
blockTimestamp: parseInt(deposit.blockTimestamp),
amount: parseFloat(formatEther(BigInt(deposit.amount))),
seller: deposit.seller,
buyer: '',
event: 'DepositAdded',
lockStatus: undefined,
transactionHash: deposit.transactionHash,
});
}
}
// Process lock added events
if (data.data?.lockAddeds) {
for (const lock of data.data.lockAddeds) {
// Get lock status from the contract
const lockStatus = await getLockStatus(BigInt(lock.lockID));
transactions.push({
token: lock.token,
blockNumber: parseInt(lock.blockNumber),
blockTimestamp: parseInt(lock.blockTimestamp),
amount: parseFloat(formatEther(BigInt(lock.amount))),
seller: lock.seller,
buyer: lock.buyer,
event: 'LockAdded',
lockStatus: lockStatus,
transactionHash: lock.transactionHash,
transactionID: lock.lockID.toString(),
});
}
}
// Process lock released events
if (data.data?.lockReleaseds) {
for (const release of data.data.lockReleaseds) {
transactions.push({
token: undefined, // Subgraph doesn't provide token in this event, we could enhance this later
blockNumber: parseInt(release.blockNumber),
blockTimestamp: parseInt(release.blockTimestamp),
amount: -1, // Amount not available in this event
seller: '',
buyer: release.buyer,
event: 'LockReleased',
lockStatus: undefined,
transactionHash: release.transactionHash,
transactionID: release.lockId.toString(),
});
}
}
// Process deposit withdrawn events
if (data.data?.depositWithdrawns) {
for (const withdrawal of data.data.depositWithdrawns) {
transactions.push({
token: withdrawal.token,
blockNumber: parseInt(withdrawal.blockNumber),
blockTimestamp: parseInt(withdrawal.blockTimestamp),
amount: parseFloat(formatEther(BigInt(withdrawal.amount))),
seller: withdrawal.seller,
buyer: '',
event: 'DepositWithdrawn',
lockStatus: undefined,
transactionHash: withdrawal.transactionHash,
});
}
}
// Sort transactions by block number (newest first)
return transactions.sort((a, b) => b.blockNumber - a.blockNumber);
};
const listLockTransactionByWalletAddress = async (walletAddress: Address) => {
const user = useUser();
const network = user.network.value;
// Query subgraph for lock added transactions
const subgraphQuery = {
query: `
{
lockAddeds(where: {buyer: "${walletAddress.toLowerCase()}"}) {
buyer
lockID
seller
amount
blockTimestamp
blockNumber
transactionHash
}
}
`,
};
try {
// Fetch data from subgraph
const response = await fetch(network.subgraphUrls[0], {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(subgraphQuery),
});
const data = await response.json();
if (!data.data?.lockAddeds) {
return [];
}
// Transform the subgraph data to match the event log decode format
return data.data.lockAddeds
.sort((a: any, b: any) => {
return parseInt(b.blockNumber) - parseInt(a.blockNumber);
})
.map((lock: any) => {
try {
// Create a structure similar to the decoded event log
return {
eventName: 'LockAdded',
args: {
buyer: lock.buyer,
lockID: BigInt(lock.lockID),
seller: lock.seller,
token: undefined, // Token not available in LockAdded subgraph event
amount: BigInt(lock.amount),
},
// Add other necessary fields to match the original format
blockNumber: BigInt(lock.blockNumber),
transactionHash: lock.transactionHash,
};
} catch (error) {
console.error('Error processing subgraph data', error);
return null;
}
})
.filter((decoded: any) => decoded !== null);
} catch (error) {
console.error('Error fetching from subgraph:', error);
}
};
const listLockTransactionBySellerAddress = async (sellerAddress: Address) => {
const user = useUser();
const network = user.network.value;
// Query subgraph for lock added transactions where seller matches
const subgraphQuery = {
query: `
{
lockAddeds(where: {seller: "${sellerAddress.toLowerCase()}"}) {
buyer
lockID
seller
amount
blockTimestamp
blockNumber
transactionHash
}
}
`,
};
try {
// Fetch data from subgraph
const response = await fetch(network.subgraphUrls[0], {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(subgraphQuery),
});
const data = await response.json();
if (!data.data?.lockAddeds) {
return [];
}
// Transform the subgraph data to match the event log decode format
return data.data.lockAddeds
.sort((a: any, b: any) => {
return parseInt(b.blockNumber) - parseInt(a.blockNumber);
})
.map((lock: any) => {
try {
// Create a structure similar to the decoded event log
return {
eventName: 'LockAdded',
args: {
buyer: lock.buyer,
lockID: BigInt(lock.lockID),
seller: lock.seller,
token: undefined, // Token not available in LockAdded subgraph event
amount: BigInt(lock.amount),
},
// Add other necessary fields to match the original format
blockNumber: BigInt(lock.blockNumber),
transactionHash: lock.transactionHash,
};
} catch (error) {
console.error('Error processing subgraph data', error);
return null;
}
})
.filter((decoded: any) => decoded !== null);
} catch (error) {
console.error('Error fetching from subgraph:', error);
return [];
}
};
export const checkUnreleasedLock = async (
walletAddress: Address,
): Promise<UnreleasedLock | undefined> => {
const { address, abi, client } = await getContract();
const addedLocks = await listLockTransactionByWalletAddress(walletAddress);
if (!addedLocks.length) return undefined;
const lockIds = addedLocks.map((lock: any) => lock.args.lockID);
const [sortedIDs, status] = await client.readContract({
address,
abi,
functionName: 'getLocksStatus',
args: [lockIds],
});
const unreleasedLockId = status.findIndex(
(status: LockStatus) => status == LockStatus.Active,
);
if (unreleasedLockId !== -1)
return getUnreleasedLockById(sortedIDs[unreleasedLockId]);
};
export const getActiveLockAmount = async (
walletAddress: Address,
): Promise<number> => {
const { address, abi, client } = await getContract(true);
const lockSeller = await listLockTransactionBySellerAddress(walletAddress);
if (!lockSeller.length) return 0;
const lockIds = lockSeller.map((lock: any) => lock.args.lockID);
const [sortedIDs, status] = await client.readContract({
address,
abi,
functionName: 'getLocksStatus',
args: [lockIds],
});
const mapLocksRequests = status.map((id: LockStatus) =>
client.readContract({
address: address,
abi,
functionName: 'mapLocks',
args: [BigInt(id)],
}),
);
const mapLocksResults = await client.multicall({
contracts: mapLocksRequests as any,
});
return mapLocksResults.reduce((total: number, lock: any, index: number) => {
if (status[index] === 1) {
return total + Number(formatEther(lock.amount));
}
return total;
}, 0);
};