Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,14 @@
"prool": "^0.0.15",
"rimraf": "^5.0.5",
"sinon": "^17.0.1",
"supabase": "^2.2.1",
"supabase": "^2.6.8",
"supertest": "^6.3.4",
"ts-mockito": "^2.6.1",
"tsx": "^4.7.1",
"typescript": "^5.5.2",
"typescript-eslint": "^7.0.2",
"vite-tsconfig-paths": "^4.3.1",
"vitest": "^1.3.1",
"vitest": "^1.6.0",
"vitest-mock-extended": "^1.3.1",
"wait-on": "^7.2.0"
},
Expand Down
908 changes: 728 additions & 180 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

190 changes: 190 additions & 0 deletions scripts/update_sales_values.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import "dotenv/config";

import { createClient } from "@supabase/supabase-js";
import { parseClaimOrFractionId } from "@hypercerts-org/sdk";
import { createPublicClient, erc20Abi, zeroAddress } from "viem";
import { http } from "viem";
import {
arbitrum,
arbitrumSepolia,
base,
baseSepolia,
celo,
filecoin,
filecoinCalibration,
optimism,
} from "viem/chains";
import { Chain } from "viem";
import { sepolia } from "viem/chains";
import { HypercertMinterAbi } from "@hypercerts-org/sdk";
import { parseEventLogs } from "viem";
import { TakerBid } from "../src/storage/storeTakerBid.js";
import { getAddress } from "viem";
import { getDeployment } from "../src/utils/getDeployment.js";
import { getHypercertTokenId } from "../src/utils/tokenIds.js";
import { HypercertExchangeAbi } from "@hypercerts-org/sdk";
import { EvmClientFactory } from "../src/clients/evmClient.js";

const getChain = (chainId: number) => {
const chains: Record<number, Chain> = {
10: optimism,
314: filecoin,
8453: base,
42161: arbitrum,
42220: celo,
84532: baseSepolia,
314159: filecoinCalibration,
421614: arbitrumSepolia,
11155111: sepolia,
};

const chain = chains[chainId];
if (!chain) throw new Error(`Unsupported chain ID: ${chainId}`);
return chain;
};

const main = async () => {
console.log("update_sales_values");
// Get all sales rows
// Create supabase client
const supabase = createClient(
process.env.SUPABASE_CACHING_DB_URL!,
process.env.SUPABASE_CACHING_SERVICE_API_KEY!,
);
const salesResponse = await supabase.from("sales").select("*");
const sales = salesResponse.data;

if (!sales) {
console.log("No sales found");
return;
}

const results: TakerBid[] = [];
for (const sale of sales) {
const tokenId = BigInt(sale.item_ids[0]);
const claimId = getHypercertTokenId(tokenId);
const hypercert_id = sale.hypercert_id.replace("undefined", claimId);
const chainId = parseClaimOrFractionId(hypercert_id).chainId;

if (!chainId) {
throw new Error(
`No chainId found for sale ${sale.transaction_hash} ${hypercert_id}`,
);
}

// Get transaction and parse logs using viem
const client = EvmClientFactory.createClient(Number(chainId));
const { addresses } = getDeployment(Number(chainId));
const { currency } = sale;

try {
const transactionReceipt = await client.getTransactionReceipt({
hash: sale.transaction_hash as `0x${string}`,
});

// parse logs to get claimID, contractAddress and cid
const transactionLogsHypercertMinter = transactionReceipt.logs.filter(
(log) =>
log.address.toLowerCase() ===
addresses?.HypercertMinterUUPS?.toLowerCase(),
);

let currencyAmount = 0n;
if (currency === zeroAddress) {
// Get value of the transaction
const transaction = await client.getTransaction({
hash: sale.transaction_hash as `0x${string}`,
});
currencyAmount = transaction.value;
} else {
const currencyLogs = transactionReceipt.logs.filter(
(log) => log.address.toLowerCase() === currency.toLowerCase(),
);
const parsedCurrencyLogs = parseEventLogs({
abi: erc20Abi,
logs: currencyLogs,
});
const transferLogs = parsedCurrencyLogs.filter(
(log) => log.eventName === "Transfer",
);
currencyAmount = transferLogs.reduce(
(acc, transferLog) => acc + (transferLog?.args?.value ?? 0n),
0n,
);
}

const exchangeLogs = transactionReceipt.logs.filter(
(log) =>
log.address.toLowerCase() ===
addresses?.HypercertExchange?.toLowerCase(),
);

const parsedExchangeLog = parseEventLogs({
abi: HypercertExchangeAbi,
logs: exchangeLogs,
// @ts-expect-error eventName is missing in the type
}).find((log) => log.eventName === "TakerBid");

// @ts-expect-error args is missing in the type
const fee_amounts = parsedExchangeLog?.args?.feeAmounts;
// @ts-expect-error args is missing in the type
const fee_recipients = parsedExchangeLog?.args?.feeRecipients;

results.push(
TakerBid.parse({
amounts: sale.amounts.map((amount) => BigInt(amount)),
seller: getAddress(sale.seller),
buyer: getAddress(sale.buyer),
currency: getAddress(sale.currency),
collection: getAddress(sale.collection),
item_ids: sale.item_ids.map((item_id) => BigInt(item_id)),
strategy_id: BigInt(sale.strategy_id),
hypercert_id: hypercert_id,
transaction_hash: sale.transaction_hash,
currency_amount: currencyAmount,
fee_amounts: fee_amounts,
fee_recipients: fee_recipients,
}),
);
} catch (e) {
console.log("Error parsing transaction", JSON.stringify(sale, null, 2));
console.log(e);
continue;
}
}

// Combine parsed results with original sales data by matching transaction hashes
const rowsToUpsert = results.map((result) => {
const originalSale = sales.find(
(sale) => sale.transaction_hash === result.transaction_hash,
);
if (!originalSale) {
throw new Error(
`Could not find original sale for transaction ${result.transaction_hash}`,
);
}
return {
...originalSale,
...result,
strategy_id: result.strategy_id.toString(),
item_ids: result.item_ids.map((id) => id.toString()),
amounts: result.amounts.map((amount) => amount.toString()),
currency_amount: result.currency_amount.toString(),
fee_amounts: result.fee_amounts.map((amount) => amount.toString()),
};
});


// Upsert rows
console.log("Upserting rows");
console.log(JSON.stringify(rowsToUpsert, null, 2));
const res = await supabase
.from("sales")
.upsert(rowsToUpsert)
.select("*")
.throwOnError();
console.log("Rows after upsert");
console.log(JSON.stringify(res.data, null, 2));
};

main();
138 changes: 95 additions & 43 deletions src/parsing/parseTakerBidEvent.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { getAddress, isAddress, parseEventLogs } from "viem";
import {
erc20Abi,
getAddress,
isAddress,
parseEventLogs,
zeroAddress,
} from "viem";
import { z } from "zod";
import { messages } from "@/utils/validation.js";
import { getEvmClient } from "@/clients/evmClient.js";
import { HypercertMinterAbi } from "@hypercerts-org/sdk";
import { HypercertExchangeAbi, HypercertMinterAbi } from "@hypercerts-org/sdk";
import { getDeployment } from "@/utils/getDeployment.js";
import { TakerBid } from "@/storage/storeTakerBid.js";
import { ParserMethod } from "@/indexer/LogParser.js";
Expand Down Expand Up @@ -40,7 +46,7 @@ import { getHypercertTokenId } from "@/utils/tokenIds.js";
* console.log(parsedEvent); // { bidUser: "0x5678", bidRecipient: "0x5678", strategyId: 1234n, ... }/
**/

const TakerBidEventSchema = z.object({
export const TakerBidEventSchema = z.object({
address: z.string().refine(isAddress, { message: messages.INVALID_ADDRESS }),
params: z.object({
nonceInvalidationParameters: z.object({
Expand Down Expand Up @@ -83,52 +89,95 @@ export const parseTakerBidEvent: ParserMethod<TakerBid> = async ({
const bid = TakerBidEventSchema.parse(event);

// parse logs to get claimID, contractAddress and cid
const transactionLogs = await client
.getTransactionReceipt({
const transactionReceipt = await client.getTransactionReceipt({
hash: bid.transactionHash as `0x${string}`,
});

// parse logs to get claimID, contractAddress and cid
const transactionLogsHypercertMinter = transactionReceipt.logs.filter(
(log) =>
log.address.toLowerCase() ===
addresses?.HypercertMinterUUPS?.toLowerCase(),
);

const parsedLogs = parseEventLogs({
abi: HypercertMinterAbi,
logs: transactionLogsHypercertMinter,
});

// Look for both BatchValueTransfer and TransferSingle events
const batchValueTransferLog = parsedLogs.find(
// @ts-expect-error eventName is missing in the type
(log) => log.eventName === "BatchValueTransfer",
);
const transferSingleLog = parsedLogs.find(
// @ts-expect-error eventName is missing in the type
(log) => log.eventName === "TransferSingle",
);

// Get the claim ID from either event type
let claimId;
// @ts-expect-error args is missing in the type
if (batchValueTransferLog?.args?.claimIDs?.[0]) {
// @ts-expect-error args is missing in the type
claimId = batchValueTransferLog.args.claimIDs[0];
// @ts-expect-error args is missing in the type
} else if (transferSingleLog?.args?.id) {
// In this case, the ID from the transferSingleLog is a fraction token ID
// We need to get the claim ID from the fraction token ID
// @ts-expect-error args is missing in the type
claimId = getHypercertTokenId(transferSingleLog.args.id);
}

if (!claimId) {
throw new Error(
"Failed to find claim ID in BatchValueTransfer or TransferSingle events",
);
}

const hypercertId = `${chain_id}-${getAddress(bid.params?.collection)}-${claimId}`;

let currencyAmount = 0n;
const currency = getAddress(bid.params.currency);
if (currency === zeroAddress) {
// Get value of the transaction
const transaction = await client.getTransaction({
hash: bid.transactionHash as `0x${string}`,
})
.then((res) => {
return res.logs.filter(
(log) =>
log.address.toLowerCase() ===
addresses?.HypercertMinterUUPS?.toLowerCase(),
);
});

const parsedLogs = parseEventLogs({
abi: HypercertMinterAbi,
logs: transactionLogs,
currencyAmount = transaction.value;
} else {
const currencyLogs = transactionReceipt.logs.filter(
(log) => log.address.toLowerCase() === currency.toLowerCase(),
);
const parsedCurrencyLogs = parseEventLogs({
abi: erc20Abi,
logs: currencyLogs,
});

// Look for both BatchValueTransfer and TransferSingle events
const batchValueTransferLog = parsedLogs.find(
// @ts-expect-error eventName is missing in the type
(log) => log.eventName === "BatchValueTransfer"
const transferLogs = parsedCurrencyLogs.filter(
(log) => log.eventName === "Transfer",
);
const transferSingleLog = parsedLogs.find(
// @ts-expect-error eventName is missing in the type
(log) => log.eventName === "TransferSingle"
currencyAmount = transferLogs.reduce(
(acc, transferLog) => acc + (transferLog?.args?.value ?? 0n),
0n,
);
}

const exchangeLogs = transactionReceipt.logs.filter(
(log) =>
log.address.toLowerCase() ===
addresses?.HypercertExchange?.toLowerCase(),
);

const parsedExchangeLog = parseEventLogs({
abi: HypercertExchangeAbi,
logs: exchangeLogs,
// @ts-expect-error eventName is missing in the type
}).find((log) => log.eventName === "TakerBid");

// Get the claim ID from either event type
let claimId;
if (batchValueTransferLog?.args?.claimIDs?.[0]) {
// @ts-expect-error args is missing in the type
claimId = batchValueTransferLog.args.claimIDs[0];
} else if (transferSingleLog?.args?.id) {
// In this case, the ID from the transferSingleLog is a fraction token ID
// We need to get the claim ID from the fraction token ID
// @ts-expect-error args is missing in the type
claimId = getHypercertTokenId(transferSingleLog.args.id);
}

if (!claimId) {
throw new Error(
"Failed to find claim ID in BatchValueTransfer or TransferSingle events"
);
}

const hypercertId = `${chain_id}-${getAddress(bid.params?.collection)}-${claimId}`;
// @ts-expect-error args is missing in the type
const fee_amounts = parsedExchangeLog?.args?.feeAmounts;
// @ts-expect-error args is missing in the type
const fee_recipients = parsedExchangeLog?.args?.feeRecipients;

return [
TakerBid.parse({
Expand All @@ -141,6 +190,9 @@ export const parseTakerBidEvent: ParserMethod<TakerBid> = async ({
strategy_id: bid.params.strategyId,
hypercert_id: hypercertId,
transaction_hash: bid.transactionHash,
currency_amount: currencyAmount,
fee_amounts: fee_amounts,
fee_recipients: fee_recipients,
}),
];
} catch (e) {
Expand Down
4 changes: 3 additions & 1 deletion src/storage/storeTakerBid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ export const TakerBid = z.object({
hypercert_id: z.string(),
amounts: z.array(z.bigint()),
transaction_hash: z.string(),
currency_amount: z.bigint(),
fee_amounts: z.array(z.bigint()),
fee_recipients: z.array(z.string().refine(isAddress, { message: "Invalid fee recipient address" })),
});

export type TakerBid = z.infer<typeof TakerBid>;
Expand Down Expand Up @@ -100,7 +103,6 @@ export const storeTakerBid: StorageMethod<TakerBid> = async ({
const rpcUrl = getRpcUrl(Number(chain_id));
const hypercertsExchange = new HypercertExchangeClient(
Number(chain_id),
// @ts-expect-error - No types available
new ethers.JsonRpcProvider(rpcUrl),
);

Expand Down
Loading
Loading