diff --git a/examples/pragma/pragma-devnet/devnet-script-spot-checkpoint.js b/examples/pragma/pragma-devnet/devnet-script-spot-checkpoint.js new file mode 100644 index 0000000..5e1636b --- /dev/null +++ b/examples/pragma/pragma-devnet/devnet-script-spot-checkpoint.js @@ -0,0 +1,81 @@ +import { hash, shortString } from "https://esm.run/starknet@5.14"; + +const filter = { + // Only request header if any event matches. + header: { + weak: true, + }, + events: [ + { + fromAddress: + "0x753034396b7e59f3579e2beba2f408dd2ed12974e054a1348234bea4b10bd30", + keys: [hash.getSelectorFromName("CheckpointSpotEntry")], + includeTransaction: true, + includeReceipt: false, + }, + ], +}; + +function escapeInvalidCharacters(str) { + return str.replace(/^[\x00-\x1F]+/, ""); +} + +function decodeTransfersInBlock({ header, events }) { + const { blockNumber, blockHash, timestamp } = header; + return events.flatMap(({ event, transaction }) => { + const transactionHash = transaction.meta.hash; + + const invoke_tx = transaction.invokeV1 ?? transaction.invokeV3; + const senderAddress = invoke_tx.senderAddress; + + const dataId = `${transactionHash}_${event.index ?? 0}`; + + const [ + pairId, + checkpointTimestamp, + price, + aggregationMode, + nbSourcesAggregated, + ] = event.data; + + // Convert felts to string + const pairIdName = escapeInvalidCharacters( + shortString.decodeShortString(pairId), + ); + + // Convert to snake_case because it works better with postgres. + return { + network: "pragma-devnet", + pair_id: pairIdName, + data_id: dataId, + block_hash: blockHash, + block_number: +blockNumber, + block_timestamp: timestamp, + transaction_hash: transactionHash, + price: +price, + timestamp: new Date(Number(checkpointTimestamp) * 1000).toISOString(), + aggregation_mode: +aggregationMode, + nb_sources_aggregated: +nbSourcesAggregated, + sender_address: senderAddress, + }; + }); +} + +// Configure indexer for streaming Starknet Goerli data starting at the specified block. +export const config = { + streamUrl: "https://devnet.pragma.a5a.ch", + startingBlock: Number(0), + network: "starknet", + filter, + batchSize: 1, + finality: "DATA_STATUS_PENDING", + sinkType: "postgres", + sinkOptions: { + // Send data as returned by `transform`. + // When `raw = false`, the data is sent together with the starting and end cursor. + raw: true, + }, +}; + +// Transform each block using the function defined in starknet.js. +export default decodeTransfersInBlock; diff --git a/examples/pragma/pragma-devnet/devnet-script-spot.js b/examples/pragma/pragma-devnet/devnet-script-spot.js new file mode 100644 index 0000000..68898a2 --- /dev/null +++ b/examples/pragma/pragma-devnet/devnet-script-spot.js @@ -0,0 +1,80 @@ +import { hash, shortString } from "https://esm.run/starknet@5.14"; + +const filter = { + // Only request header if any event matches. + header: { + weak: true, + }, + events: [ + { + fromAddress: + "0x753034396b7e59f3579e2beba2f408dd2ed12974e054a1348234bea4b10bd30", + keys: [hash.getSelectorFromName("SubmittedSpotEntry")], + includeTransaction: true, + includeReceipt: false, + }, + ], +}; + +function escapeInvalidCharacters(str) { + return str.replace(/^[\x00-\x1F]+/, ""); +} + +function decodeTransfersInBlock({ header, events }) { + const { blockNumber, blockHash, timestamp } = header; + + return events.flatMap(({ event, transaction }) => { + const transactionHash = transaction.meta.hash; + + const dataId = `${transactionHash}_${event.index ?? 0}`; + + const [entryTimestamp, source, publisher, price, pairId, volume] = + event.data; + + // Convert felts to string + const publisherName = escapeInvalidCharacters( + shortString.decodeShortString(publisher), + ); + const sourceName = escapeInvalidCharacters( + shortString.decodeShortString(source), + ); + const pairIdName = escapeInvalidCharacters( + shortString.decodeShortString(pairId), + ); + + // Convert to snake_case because it works better with postgres. + return { + network: "pragma-devnet", + pair_id: pairIdName, + data_id: dataId, + block_hash: blockHash, + block_number: +blockNumber, + block_timestamp: timestamp, + transaction_hash: transactionHash, + price: +price, + timestamp: new Date(Number(entryTimestamp) * 1000).toISOString(), + publisher: publisherName, + source: sourceName, + volume: +volume, + }; + }); +} + +// Configure indexer for streaming Starknet Goerli data starting at the specified block. +export const config = { + streamUrl: "https://devnet.pragma.a5a.ch", + startingBlock: Number(0), + network: "starknet", + filter, + batchSize: 1, + finality: "DATA_STATUS_PENDING", + sinkType: "postgres", + sinkOptions: { + // Send data as returned by `transform`. + // When `raw = false`, the data is sent together with the starting and end cursor. + raw: true, + }, +}; + +// Transform each block using the function defined in starknet.js. +export default decodeTransfersInBlock; diff --git a/examples/pragma/testnet/sepolia-script-oo-requests.js b/examples/pragma/testnet/sepolia-script-oo-requests.js index 446cb54..655777d 100644 --- a/examples/pragma/testnet/sepolia-script-oo-requests.js +++ b/examples/pragma/testnet/sepolia-script-oo-requests.js @@ -1,15 +1,17 @@ import { hash, shortString } from "https://esm.run/starknet@5.14"; // import { hash, shortString } from "starknet"; +const AssertionMadeSelector = + "0x02b74480ce203d4ca6ee46c062a2d658129587cdccb3904c52a6f4dfb406a3f2"; -const AssertionMadeSelector = "0x02b74480ce203d4ca6ee46c062a2d658129587cdccb3904c52a6f4dfb406a3f2"; +const AssertionDisputedSelector = + "0x033efc8167fe91406d16c1680e593c3c2cad864220645cc2a48efd67e8f7ca73"; -const AssertionDisputedSelector = "0x033efc8167fe91406d16c1680e593c3c2cad864220645cc2a48efd67e8f7ca73" - -const AssertionSettledSelector = "0x00ed635610f8859765fb89b19bec7866c02bbc2f03bb048acec6ba6536aa7cb9"; - -const ContractAddress = "0x044ac84b04789b0a2afcdd2eb914f0f9b767a77a95a019ebaadc28d6cacbaeeb" +const AssertionSettledSelector = + "0x00ed635610f8859765fb89b19bec7866c02bbc2f03bb048acec6ba6536aa7cb9"; +const ContractAddress = + "0x044ac84b04789b0a2afcdd2eb914f0f9b767a77a95a019ebaadc28d6cacbaeeb"; const filter = { // Only request header if any event matches. @@ -18,22 +20,19 @@ const filter = { }, events: [ { - fromAddress: - ContractAddress, + fromAddress: ContractAddress, keys: [AssertionMadeSelector], includeTransaction: true, includeReceipt: false, }, { - fromAddress: - ContractAddress, + fromAddress: ContractAddress, keys: [AssertionSettledSelector], includeTransaction: true, includeReceipt: false, }, { - fromAddress: - ContractAddress, + fromAddress: ContractAddress, keys: [AssertionDisputedSelector], includeTransaction: true, includeReceipt: false, @@ -47,86 +46,89 @@ function escapeInvalidCharacters(str) { function trimLeadingZeros(hexString) { // Check if the string starts with '0x' - if (hexString.startsWith('0x')) { + if (hexString.startsWith("0x")) { // Remove '0x', trim zeros, then add '0x' back - const trimmed = hexString.slice(2).replace(/^0+/, ''); - return '0x' + trimmed; + const trimmed = hexString.slice(2).replace(/^0+/, ""); + return "0x" + trimmed; } // If it doesn't start with '0x', just trim zeros - return hexString.replace(/^0+/, ''); + return hexString.replace(/^0+/, ""); } function combineU256(low, high) { // Convert hex strings to BigInt const lowBigInt = BigInt(low); const highBigInt = BigInt(high); - + // Combine the low and high parts return (highBigInt << BigInt(128)) + lowBigInt; } - function concatenateHexStrings(hexArray) { - return hexArray.map(str => str.replace(/^0x/, '')).join(''); + return hexArray.map((str) => str.replace(/^0x/, "")).join(""); } function decodeTransfersInBlock({ header, events }) { const { blockNumber, blockHash, timestamp } = header; return events.flatMap(({ event, transaction }) => { if (event.keys[0] == AssertionMadeSelector) { - const transactionHash = transaction.meta.hash; - const dataId = `${transactionHash}_${event.index ?? 0}`; - - // Parse the claim data dynamically - const claimLength = parseInt(event.data[3], 16); // Convert hex to integer - const claimData = event.data.slice(4, 4 + claimLength+1); - const remainingData = event.data.slice(6 + claimLength); - claimData[claimData.length - 1] = trimLeadingZeros(claimData[claimData.length - 1]); - const [ - assertionId, - domainIdLow, - domainIdHigh, - , // Skip the claim length as we've already used it - ...rest - ] = event.data; - - const [ - asserter, - callbackRecipient, - escalationManager, - caller, - expirationTimestamp, - currency, - bondLow, - bondHigh, - identifier - ] = remainingData; - - const bondBigInt = combineU256(bondLow, bondHigh); - const bond = bondBigInt.toString(); // Convert to string for database storage - - const domainIdBigInt = combineU256(domainIdLow, domainIdHigh); - const domainId = domainIdBigInt.toString(); // Convert to string for database storage - return { - insert: { - network: "starknet-sepolia", - data_id: dataId, - assertion_id: assertionId.toString(), - domain_id: domainId, - claim:concatenateHexStrings(claimData), // Store claim as a JSON string - asserter: asserter, - callback_recipient: callbackRecipient, - escalation_manager: escalationManager, - caller: caller, - expiration_timestamp: new Date(Number(expirationTimestamp) * 1000).toISOString(), - currency: currency, - bond: bond, - identifier: trimLeadingZeros(identifier), - updated_at: timestamp, - updated_at_tx: transaction.meta.hash, - }, - }; - } else if(event.keys[0] == AssertionDisputedSelector) { + const transactionHash = transaction.meta.hash; + const dataId = `${transactionHash}_${event.index ?? 0}`; + + // Parse the claim data dynamically + const claimLength = parseInt(event.data[3], 16); // Convert hex to integer + const claimData = event.data.slice(4, 4 + claimLength + 1); + const remainingData = event.data.slice(6 + claimLength); + claimData[claimData.length - 1] = trimLeadingZeros( + claimData[claimData.length - 1], + ); + const [ + assertionId, + domainIdLow, + domainIdHigh, // Skip the claim length as we've already used it + , + ...rest + ] = event.data; + + const [ + asserter, + callbackRecipient, + escalationManager, + caller, + expirationTimestamp, + currency, + bondLow, + bondHigh, + identifier, + ] = remainingData; + + const bondBigInt = combineU256(bondLow, bondHigh); + const bond = bondBigInt.toString(); // Convert to string for database storage + + const domainIdBigInt = combineU256(domainIdLow, domainIdHigh); + const domainId = domainIdBigInt.toString(); // Convert to string for database storage + return { + insert: { + network: "starknet-sepolia", + data_id: dataId, + assertion_id: assertionId.toString(), + domain_id: domainId, + claim: concatenateHexStrings(claimData), // Store claim as a JSON string + asserter: asserter, + callback_recipient: callbackRecipient, + escalation_manager: escalationManager, + caller: caller, + expiration_timestamp: new Date( + Number(expirationTimestamp) * 1000, + ).toISOString(), + currency: currency, + bond: bond, + identifier: trimLeadingZeros(identifier), + updated_at: timestamp, + updated_at_tx: transaction.meta.hash, + }, + }; + } else if (event.keys[0] == AssertionDisputedSelector) { // Update request entity. const assertionId = event.data[0]; const caller = event.data[1]; @@ -143,19 +145,27 @@ function decodeTransfersInBlock({ header, events }) { dispute_id: request_id, }, }; - } else if (event.keys[0] == AssertionSettledSelector){ + } else if (event.keys[0] == AssertionSettledSelector) { const assertionId = event.data[0]; const bondRecipient = event.data[1]; - const disputed = event.data[2]=='0x0000000000000000000000000000000000000000000000000000000000000000' ? false: true; - const settlementResolution = event.data[3]=='0x0000000000000000000000000000000000000000000000000000000000000000' ? false: true; + const disputed = + event.data[2] == + "0x0000000000000000000000000000000000000000000000000000000000000000" + ? false + : true; + const settlementResolution = + event.data[3] == + "0x0000000000000000000000000000000000000000000000000000000000000000" + ? false + : true; const settleCaller = event.data[4]; return { entity: { assertion_id: assertionId, }, update: { - settlement_resolution: settlementResolution, - disputed: disputed, + settlement_resolution: settlementResolution, + disputed: disputed, settle_caller: settleCaller, settled: true, updated_at: timestamp, @@ -188,4 +198,3 @@ export const config = { // Transform each block using the function defined in starknet.js. export default decodeTransfersInBlock; -