Skip to content

Commit

Permalink
Merge pull request #256 from hypercerts-org/develop
Browse files Browse the repository at this point in the history
Push to PRD
  • Loading branch information
Jipperism authored Jan 30, 2025
2 parents febff39 + 3e79245 commit 6bd2e9f
Show file tree
Hide file tree
Showing 11 changed files with 1,183 additions and 351 deletions.
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
Loading

0 comments on commit 6bd2e9f

Please sign in to comment.