| 2 | "code": "\\\\n const rfpOps = getRFPOps(block);\\\\n const proposalOps = getProposalOps(block);\\\\n\\\\n const rfpOpsLen = rfpOps.length;\\\\n const proposalOpsLen = proposalOps.length;\\\\n\\\\n if (rfpOpsLen > 0 || proposalOpsLen > 0) {\\\\n const authorToRFPId = buildAuthorToRFPIdMap(block);\\\\n const authorToProposalId = buildAuthorToProposalIdMap(block);\\\\n const blockHeight = block.blockHeight;\\\\n const blockTimestamp = block.header().timestampNanosec;\\\\n try {\\\\n await Promise.all(\\\\n rfpOps\\\\n .map((op) =>\\\\n indexRFPsOp(op, authorToRFPId, blockHeight, blockTimestamp, context)\\\\n )\\\\n .concat(\\\\n proposalOps.map((op) =>\\\\n indexProposalsOp(\\\\n op,\\\\n authorToProposalId,\\\\n blockHeight,\\\\n blockTimestamp,\\\\n context\\\\n )\\\\n )\\\\n )\\\\n );\\\\n } catch (error) {\\\\n console.error(\\\\\\\"Error processing block operations:\\\\\\\", error);\\\\n }\\\\n }\\\\n}\\\\n\\\\nfunction getAddOrEditObject(block, startsWith) {\\\\n const stateChanges = block.streamerMessage.shards\\\\n .flatMap((e) => e.stateChanges)\\\\n .filter(\\\\n (stateChange) =>\\\\n stateChange.change.accountId === \\\\\\\"treasury-templar.near\\\\\\\" &&\\\\n stateChange.type === \\\\\\\"data_update\\\\\\\"\\\\n );\\\\n\\\\n const addOrEditObject = stateChanges\\\\n .map((stateChange) => stateChange.change)\\\\n // In devhub contract there is a field rfps: Vector<VersionedRFP> ( initially = rfps: Vector::new(StorageKey::RFPs) )\\\\n // In StorageKey enum it comes on 17th position (0x11 in hex).\\\\n // So 0x11 is used as a prefix for the collection keys (https://docs.near.org/sdk/rust/contract-structure/collections).\\\\n .filter((change) => base64toHex(change.keyBase64).startsWith(startsWith))\\\\n .map((c) => ({\\\\n k: Buffer.from(c.keyBase64, \\\\\\\"base64\\\\\\\"),\\\\n v: Buffer.from(c.valueBase64, \\\\\\\"base64\\\\\\\"),\\\\n }));\\\\n\\\\n return addOrEditObject;\\\\n}\\\\n\\\\n// Borsh https://github.com/near/borsh#specification\\\\nfunction buildAuthorToRFPIdMap(block) {\\\\n return Object.fromEntries(\\\\n getAddOrEditObject(block, \\\\\\\"11\\\\\\\").map((kv) => {\\\\n let prefix_len = 1;\\\\n let id_len = 4;\\\\n let author_id_size_len = 4;\\\\n let author_id_string_len = kv.v\\\\n .slice(prefix_len + id_len, prefix_len + id_len + author_id_size_len)\\\\n .readUInt32LE();\\\\n let social_db_post_block_height_len = 8;\\\\n let before_editor_id =\\\\n prefix_len +\\\\n id_len +\\\\n author_id_size_len +\\\\n author_id_string_len +\\\\n social_db_post_block_height_len;\\\\n let editor_id_size_len = 4;\\\\n let editor_id_string_len = kv.v\\\\n .slice(before_editor_id, before_editor_id + editor_id_size_len)\\\\n .readUInt32LE();\\\\n let editor_id_string = kv.v\\\\n .slice(\\\\n before_editor_id + editor_id_size_len,\\\\n before_editor_id + editor_id_size_len + editor_id_string_len\\\\n )\\\\n .toString(\\\\\\\"utf-8\\\\\\\");\\\\n\\\\n return [\\\\n // Here we read enum VersionedRFP. So we skip enum byte. This enum has just one variant RFP.\\\\n // It contains id: u32 (4 bytes) and then account_id which is string.\\\\n // String is serialized as length: u32 (4 bytes) and then content of the string\\\\n editor_id_string,\\\\n // In Vector, key is prefix + index, where index is u32 in little-endian format.\\\\n // So we skip prefix with slice(1) and read index with readBigUint64LE().\\\\n Number(kv.k.slice(1).readBigUInt64LE()),\\\\n ];\\\\n })\\\\n );\\\\n}\\\\n\\\\nfunction buildAuthorToProposalIdMap(block) {\\\\n return Object.fromEntries(\\\\n getAddOrEditObject(block, \\\\\\\"0e\\\\\\\").map((kv) => {\\\\n let prefix_len = 1;\\\\n let id_len = 4;\\\\n let author_id_size_len = 4;\\\\n let author_id_string_len = kv.v\\\\n .slice(prefix_len + id_len, prefix_len + id_len + author_id_size_len)\\\\n .readUInt32LE();\\\\n let social_db_post_block_height_len = 8;\\\\n let before_editor_id =\\\\n prefix_len +\\\\n id_len +\\\\n author_id_size_len +\\\\n author_id_string_len +\\\\n social_db_post_block_height_len;\\\\n let editor_id_size_len = 4;\\\\n let editor_id_string_len = kv.v\\\\n .slice(before_editor_id, before_editor_id + editor_id_size_len)\\\\n .readUInt32LE();\\\\n let editor_id_string = kv.v\\\\n .slice(\\\\n before_editor_id + editor_id_size_len,\\\\n before_editor_id + editor_id_size_len + editor_id_string_len\\\\n )\\\\n .toString(\\\\\\\"utf-8\\\\\\\");\\\\n\\\\n return [\\\\n // Here we read enum VersionedRFP. So we skip enum byte. This enum has just one variant RFP.\\\\n // It contains id: u32 (4 bytes) and then account_id which is string.\\\\n // String is serialized as length: u32 (4 bytes) and then content of the string\\\\n editor_id_string,\\\\n // In Vector, key is prefix + index, where index is u32 in little-endian format.\\\\n // So we skip prefix with slice(1) and read index with readBigUint64LE().\\\\n Number(kv.k.slice(1).readBigUInt64LE()),\\\\n ];\\\\n })\\\\n );\\\\n}\\\\n\\\\nfunction base64decode(encodedValue) {\\\\n let buff = Buffer.from(encodedValue, \\\\\\\"base64\\\\\\\");\\\\n return JSON.parse(buff.toString(\\\\\\\"utf-8\\\\\\\"));\\\\n}\\\\n\\\\nfunction base64toHex(encodedValue) {\\\\n let buff = Buffer.from(encodedValue, \\\\\\\"base64\\\\\\\");\\\\n return buff.toString(\\\\\\\"hex\\\\\\\");\\\\n}\\\\n\\\\nfunction getDevHubOps(block, methodNames, callbackNames) {\\\\n return block\\\\n .actions()\\\\n .filter((action) => action.receiverId === \\\\\\\"treasury-templar.near\\\\\\\")\\\\n .flatMap((action) =>\\\\n action.operations\\\\n .filter((operation) => operation[\\\\\\\"FunctionCall\\\\\\\"])\\\\n .map((operation) => ({\\\\n ...operation[\\\\\\\"FunctionCall\\\\\\\"],\\\\n caller: action.predecessorId,\\\\n }))\\\\n .map((operation) => ({\\\\n ...operation,\\\\n methodName: operation.methodName || operation.method_name,\\\\n }))\\\\n .filter(\\\\n (operation) =>\\\\n methodNames.includes(operation.methodName) ||\\\\n (callbackNames.includes(operation.methodName) &&\\\\n operation.caller === \\\\\\\"treasury-templar.near\\\\\\\")\\\\n )\\\\n .map((functionCallOperation) => ({\\\\n ...functionCallOperation,\\\\n args: base64decode(functionCallOperation.args),\\\\n receiptId: action.receiptId,\\\\n }))\\\\n );\\\\n}\\\\n\\\\nfunction getProposalOps(block) {\\\\n return getDevHubOps(\\\\n block,\\\\n [\\\\n \\\\\\\"edit_proposal\\\\\\\",\\\\n \\\\\\\"edit_proposal_internal\\\\\\\",\\\\n \\\\\\\"edit_proposal_linked_rfp\\\\\\\",\\\\n \\\\\\\"edit_proposal_timeline\\\\\\\",\\\\n ],\\\\n [\\\\\\\"set_block_height_callback\\\\\\\"]\\\\n );\\\\n}\\\\n\\\\nfunction getRFPOps(block) {\\\\n return getDevHubOps(\\\\n block,\\\\n [\\\\\\\"edit_rfp\\\\\\\", \\\\\\\"edit_rfp_timeline\\\\\\\", \\\\\\\"edit_rfp_internal\\\\\\\", \\\\\\\"cancel_rfp\\\\\\\"],\\\\n [\\\\\\\"set_rfp_block_height_callback\\\\\\\"]\\\\n );\\\\n}\\\\n\\\\nfunction strArray(arr) {\\\\n return arr && arr.length ? arr.join(\\\\\\\",\\\\\\\") : \\\\\\\"\\\\\\\"; // Vec<ProposalId>\\\\n}\\\\n\\\\nasync function indexProposalsOp(\\\\n op,\\\\n authorToProposalId,\\\\n blockHeight,\\\\n blockTimestamp,\\\\n context\\\\n) {\\\\n let receipt_id = op.receiptId;\\\\n let args = op.args;\\\\n let author = Object.keys(authorToProposalId)[0];\\\\n console.log(`Indexing ${op.methodName} by ${author} at ${blockHeight}`);\\\\n let proposal_id = authorToProposalId[author] ?? null;\\\\n let method_name = op.methodName;\\\\n\\\\n let err = await createDump(context, {\\\\n receipt_id,\\\\n method_name,\\\\n block_height: blockHeight,\\\\n block_timestamp: blockTimestamp,\\\\n args: JSON.stringify(args),\\\\n author,\\\\n proposal_id,\\\\n });\\\\n if (err !== null) {\\\\n return;\\\\n }\\\\n\\\\n // currently Query API cannot tell if it\\\\\\'s a failed receipt, so we estimate by looking the state changes.\\\\n if (proposal_id === null) {\\\\n console.log(\\\\n `Receipt to ${method_name} with receipt_id ${receipt_id} at ${blockHeight} doesn\\\\\\'t result in a state change, it\\\\\\'s probably a failed receipt, please check`\\\\n );\\\\n return;\\\\n }\\\\n\\\\n if (method_name === \\\\\\\"set_block_height_callback\\\\\\\") {\\\\n let proposal = {\\\\n id: proposal_id,\\\\n author_id: author,\\\\n };\\\\n\\\\n let err = await createProposal(context, proposal);\\\\n if (err !== null) {\\\\n return;\\\\n }\\\\n\\\\n let linked_rfp = args.proposal.snapshot.linked_rfp;\\\\n let linked_proposals = args.proposal.snapshot.linked_proposals;\\\\n\\\\n let proposal_snapshot = {\\\\n ...args.proposal.snapshot,\\\\n timeline: JSON.stringify(args.proposal.snapshot.timeline),\\\\n proposal_id,\\\\n block_height: blockHeight,\\\\n proposal_version: args.proposal.proposal_version,\\\\n social_db_post_block_height: 0,\\\\n ts: blockTimestamp,\\\\n views: 1,\\\\n linked_proposals,\\\\n };\\\\n await createProposalSnapshot(context, proposal_snapshot);\\\\n await checkAndUpdateLinkedProposals(\\\\n proposal_id,\\\\n linked_rfp,\\\\n blockHeight,\\\\n blockTimestamp,\\\\n author\\\\n );\\\\n }\\\\n\\\\n if (method_name === \\\\\\\"edit_proposal\\\\\\\") {\\\\n let linked_rfp = args.body.linked_rfp;\\\\n\\\\n let latest_snapshot = await queryLatestProposalSnapshot(\\\\n proposal_id,\\\\n blockTimestamp\\\\n );\\\\n let labels =\\\\n linked_rfp === undefined || linked_rfp === null\\\\n ? args.labels\\\\n : latest_snapshot.labels;\\\\n\\\\n let proposal_snapshot = {\\\\n ...args.body,\\\\n timeline: JSON.stringify(args.body.timeline),\\\\n proposal_id,\\\\n proposal_version: latest_snapshot.proposal_version,\\\\n social_db_post_block_height: latest_snapshot.social_db_post_block_height,\\\\n block_height: blockHeight,\\\\n ts: blockTimestamp, // Timestamp\\\\n editor_id: author,\\\\n labels,\\\\n linked_rfp,\\\\n views: latest_snapshot.views + 1,\\\\n };\\\\n await createProposalSnapshot(context, proposal_snapshot);\\\\n await checkAndUpdateLinkedProposals(\\\\n proposal_id,\\\\n linked_rfp,\\\\n blockHeight,\\\\n blockTimestamp,\\\\n author\\\\n );\\\\n }\\\\n\\\\n if (method_name === \\\\\\\"edit_proposal_internal\\\\\\\") {\\\\n let linked_rfp = args.body.linked_rfp;\\\\n\\\\n let latest_snapshot = await queryLatestProposalSnapshot(\\\\n proposal_id,\\\\n blockTimestamp\\\\n );\\\\n let labels =\\\\n linked_rfp === undefined || linked_rfp === null\\\\n ? args.labels\\\\n : latest_snapshot.labels;\\\\n\\\\n let proposal_snapshot = {\\\\n ...args.body,\\\\n timeline: JSON.stringify(args.body.timeline),\\\\n proposal_id,\\\\n proposal_version: latest_snapshot.proposal_version,\\\\n social_db_post_block_height: latest_snapshot.social_db_post_block_height,\\\\n block_height: blockHeight,\\\\n ts: blockTimestamp, // Timestamp\\\\n editor_id: author,\\\\n labels,\\\\n linked_rfp,\\\\n views: latest_snapshot.views + 1,\\\\n };\\\\n await createProposalSnapshot(context, proposal_snapshot);\\\\n await checkAndUpdateLinkedProposals(\\\\n proposal_id,\\\\n linked_rfp,\\\\n blockHeight,\\\\n blockTimestamp,\\\\n author\\\\n );\\\\n }\\\\n\\\\n if (method_name === \\\\\\\"edit_proposal_timeline\\\\\\\") {\\\\n editProposalTimeline(\\\\n context,\\\\n proposal_id,\\\\n author,\\\\n blockHeight,\\\\n blockTimestamp,\\\\n args.timeline\\\\n );\\\\n }\\\\n if (method_name == \\\\\\\"edit_proposal_linked_rfp\\\\\\\") {\\\\n editProposalLinkedRFP(\\\\n context,\\\\n proposal_id,\\\\n args.rfp_id,\\\\n author,\\\\n blockHeight,\\\\n blockTimestamp,\\\\n true\\\\n );\\\\n }\\\\n}\\\\n\\\\nasync function indexRFPsOp(\\\\n op,\\\\n authorToRFPId,\\\\n blockHeight,\\\\n blockTimestamp,\\\\n context\\\\n) {\\\\n let receipt_id = op.receiptId;\\\\n let author = Object.keys(authorToRFPId)[0];\\\\n let args = op.args;\\\\n let rfp_id = authorToRFPId[author] ?? null;\\\\n let method_name = op.methodName;\\\\n\\\\n console.log(\\\\n `Indexing ${method_name} by ${author} at ${blockHeight}, rfp_id = ${rfp_id}`\\\\n );\\\\n\\\\n let err = await createRFPDump(context, {\\\\n receipt_id,\\\\n method_name,\\\\n block_height: blockHeight,\\\\n block_timestamp: blockTimestamp,\\\\n args: JSON.stringify(args),\\\\n author,\\\\n rfp_id,\\\\n });\\\\n if (err !== null) {\\\\n return;\\\\n }\\\\n\\\\n // currently Query API cannot tell if it\\\\\\'s a failed receipt, so we estimate by looking the state changes.\\\\n if (rfp_id === null) {\\\\n console.log(\\\\n `Receipt to ${method_name} with receipt_id ${receipt_id} at ${blockHeight} doesn\\\\\\'t result in a state change, it\\\\\\'s probably a failed receipt, please check`\\\\n );\\\\n return;\\\\n }\\\\n\\\\n if (method_name === \\\\\\\"set_rfp_block_height_callback\\\\\\\") {\\\\n let rfp = {\\\\n id: rfp_id,\\\\n author_id: author,\\\\n };\\\\n\\\\n let err = await createRFP(context, rfp);\\\\n if (err !== null) {\\\\n return;\\\\n }\\\\n\\\\n await createrfpSnapshot(context, {\\\\n ...args.rfp.snapshot,\\\\n timeline: JSON.stringify(args.rfp.snapshot.timeline),\\\\n rfp_id,\\\\n linked_proposals: [],\\\\n block_height: blockHeight,\\\\n rfp_version: args.rfp.rfp_version,\\\\n social_db_post_block_height: 0,\\\\n ts: blockTimestamp,\\\\n views: 1,\\\\n });\\\\n }\\\\n\\\\n if (method_name === \\\\\\\"edit_rfp\\\\\\\") {\\\\n let labels = args.labels;\\\\n let latest_snapshot = await queryLatestRFPSnapshot(rfp_id, blockTimestamp);\\\\n let rfp_snapshot = {\\\\n ...args.body,\\\\n timeline: JSON.stringify(args.body.timeline),\\\\n rfp_id,\\\\n block_height: blockHeight,\\\\n social_db_post_block_height: latest_snapshot.social_db_post_block_height,\\\\n rfp_version: latest_snapshot.rfp_version,\\\\n ts: blockTimestamp, // Timestamp\\\\n editor_id: author,\\\\n linked_proposals: latest_snapshot.linked_proposals,\\\\n labels,\\\\n views: latest_snapshot.views + 1,\\\\n };\\\\n await createrfpSnapshot(context, rfp_snapshot);\\\\n await checkAndUpdateLabels(\\\\n latest_snapshot.labels,\\\\n labels,\\\\n latest_snapshot.linked_proposals,\\\\n blockHeight,\\\\n blockTimestamp,\\\\n author\\\\n );\\\\n }\\\\n\\\\n if (method_name === \\\\\\\"edit_rfp_timeline\\\\\\\") {\\\\n try {\\\\n let latest_rfp_snapshot = await queryLatestRFPSnapshot(\\\\n rfp_id,\\\\n blockTimestamp\\\\n );\\\\n if (latest_rfp_snapshot) {\\\\n let rfp_snapshot = {\\\\n ...latest_rfp_snapshot,\\\\n rfp_id,\\\\n block_height: blockHeight,\\\\n ts: blockTimestamp,\\\\n editor_id: author,\\\\n timeline: JSON.stringify(args.timeline), // TimelineStatus\\\\n views: latest_rfp_snapshot.views + 1,\\\\n };\\\\n await createrfpSnapshot(context, rfp_snapshot);\\\\n } else {\\\\n console.log(\\\\\\\"Empty object latest_rfp_snapshot result\\\\\\\", {\\\\n latest_rfp_snapshot,\\\\n });\\\\n }\\\\n } catch (error) {\\\\n console.error(\\\\\\\"Error editing rfp timeline:\\\\\\\", error);\\\\n }\\\\n }\\\\n if (method_name === \\\\\\\"cancel_rfp\\\\\\\") {\\\\n try {\\\\n let proposals_to_cancel = args.proposals_to_cancel;\\\\n let proposals_to_unlink = args.proposals_to_unlink;\\\\n\\\\n let latest_rfp_snapshot = await queryLatestRFPSnapshot(\\\\n rfp_id,\\\\n blockTimestamp\\\\n );\\\\n if (latest_rfp_snapshot) {\\\\n let linked_proposals = latest_rfp_snapshot.linked_proposals;\\\\n for (let proposal_id of proposals_to_unlink) {\\\\n linked_proposals = removeFromLinkedProposals(\\\\n linked_proposals,\\\\n proposal_id\\\\n );\\\\n await editProposalLinkedRFP(\\\\n context,\\\\n proposal_id,\\\\n null,\\\\n author,\\\\n blockHeight,\\\\n blockTimestamp,\\\\n false\\\\n );\\\\n }\\\\n\\\\n for (let proposal_id of proposals_to_cancel) {\\\\n await editProposalTimeline(\\\\n context,\\\\n proposal_id,\\\\n author,\\\\n blockHeight,\\\\n blockTimestamp,\\\\n { status: \\\\\\\"CANCELLED\\\\\\\" }\\\\n );\\\\n }\\\\n\\\\n let rfp_snapshot = {\\\\n ...latest_rfp_snapshot,\\\\n rfp_id,\\\\n block_height: blockHeight,\\\\n ts: blockTimestamp,\\\\n editor_id: author,\\\\n timeline: JSON.stringify({ status: \\\\\\\"CANCELLED\\\\\\\" }), // TimelineStatus\\\\n linked_proposals,\\\\n views: latest_rfp_snapshot.views + 1,\\\\n };\\\\n await createrfpSnapshot(context, rfp_snapshot);\\\\n } else {\\\\n console.log(\\\\\\\"Empty object latest_rfp_snapshot result\\\\\\\", {\\\\n latest_rfp_snapshot,\\\\n });\\\\n }\\\\n } catch (error) {\\\\n console.error(\\\\\\\"Error editing rfp timeline:\\\\\\\", error);\\\\n }\\\\n }\\\\n}\\\\n\\\\nfunction arrayFromStr(str) {\\\\n return str ? str.split(\\\\\\\",\\\\\\\").filter((x) => x !== \\\\\\\"\\\\\\\") : [];\\\\n}\\\\n\\\\nfunction addToLinkedProposals(linked_proposals, proposal_id) {\\\\n linked_proposals.push(proposal_id);\\\\n return linked_proposals;\\\\n}\\\\n\\\\nfunction removeFromLinkedProposals(linked_proposals, proposal_id) {\\\\n return linked_proposals.filter((id) => id !== proposal_id);\\\\n}\\\\n\\\\nasync function modifySnapshotLinkedProposal(\\\\n rfp_id,\\\\n proposal_id,\\\\n blockHeight,\\\\n blockTimestamp,\\\\n modifyCallback,\\\\n editor_id\\\\n) {\\\\n let latest_rfp_snapshot = await queryLatestRFPSnapshot(\\\\n rfp_id,\\\\n blockTimestamp\\\\n );\\\\n if (latest_rfp_snapshot) {\\\\n let linked_proposals = modifyCallback(\\\\n latest_rfp_snapshot.linked_proposals,\\\\n proposal_id\\\\n );\\\\n let rfp_snapshot = {\\\\n ...latest_rfp_snapshot,\\\\n rfp_id,\\\\n linked_proposals: linked_proposals,\\\\n block_height: blockHeight,\\\\n ts: blockTimestamp,\\\\n editor_id: editor_id,\\\\n };\\\\n await createrfpSnapshot(context, rfp_snapshot);\\\\n } else {\\\\n console.log(\\\\\\\"Empty object latest_rfp_snapshot result\\\\\\\", {\\\\n latest_rfp_snapshot,\\\\n });\\\\n }\\\\n}\\\\n\\\\nasync function addLinkedProposalToSnapshot(\\\\n rfp_id,\\\\n new_linked_proposal,\\\\n blockHeight,\\\\n blockTimestamp,\\\\n editor_id\\\\n) {\\\\n await modifySnapshotLinkedProposal(\\\\n rfp_id,\\\\n new_linked_proposal,\\\\n blockHeight,\\\\n blockTimestamp,\\\\n addToLinkedProposals,\\\\n editor_id\\\\n );\\\\n}\\\\n\\\\nasync function removeLinkedProposalFromSnapshot(\\\\n rfp_id,\\\\n proposal_id,\\\\n blockHeight,\\\\n blockTimestamp,\\\\n editor_id\\\\n) {\\\\n await modifySnapshotLinkedProposal(\\\\n rfp_id,\\\\n proposal_id,\\\\n blockHeight,\\\\n blockTimestamp,\\\\n removeFromLinkedProposals,\\\\n editor_id\\\\n );\\\\n}\\\\n\\\\nasync function checkAndUpdateLinkedProposals(\\\\n proposal_id,\\\\n new_linked_rfp,\\\\n blockHeight,\\\\n blockTimestamp,\\\\n editor_id\\\\n) {\\\\n try {\\\\n let last_snapshot = await queryLatestProposalSnapshot(\\\\n proposal_id,\\\\n blockTimestamp\\\\n );\\\\n let latest_linked_rfp_id = undefined;\\\\n if (last_snapshot != undefined) {\\\\n latest_linked_rfp_id = last_snapshot.linked_rfp;\\\\n }\\\\n\\\\n if (new_linked_rfp !== latest_linked_rfp_id) {\\\\n if (new_linked_rfp !== undefined && new_linked_rfp !== null) {\\\\n console.log(\\\\n `Adding linked_rfp ${new_linked_rfp} to proposal ${proposal_id}`\\\\n );\\\\n await addLinkedProposalToSnapshot(\\\\n new_linked_rfp,\\\\n proposal_id,\\\\n blockHeight,\\\\n blockTimestamp,\\\\n editor_id\\\\n );\\\\n console.log(`Proposal added to new RFP snapshot`);\\\\n }\\\\n if (latest_linked_rfp_id !== undefined && latest_linked_rfp_id !== null) {\\\\n console.log(\\\\n `Removing linked_rfp ${latest_linked_rfp_id} from proposal ${proposal_id}`\\\\n );\\\\n await removeLinkedProposalFromSnapshot(\\\\n latest_linked_rfp_id,\\\\n proposal_id,\\\\n blockHeight,\\\\n blockTimestamp,\\\\n editor_id\\\\n );\\\\n console.log(`Proposal removed from old RFP snapshot`);\\\\n }\\\\n }\\\\n } catch (error) {\\\\n console.error(\\\\\\\"Error checking and updating linked proposals:\\\\\\\", error);\\\\n }\\\\n}\\\\n\\\\nasync function editProposalLinkedRFP(\\\\n context,\\\\n proposal_id,\\\\n new_rfp_id,\\\\n author,\\\\n blockHeight,\\\\n blockTimestamp,\\\\n updateRfpSnapshot\\\\n) {\\\\n let latest_proposal_snapshot = await queryLatestProposalSnapshot(\\\\n proposal_id,\\\\n blockTimestamp\\\\n );\\\\n\\\\n if (latest_proposal_snapshot) {\\\\n let linked_rfp = new_rfp_id;\\\\n let proposal_snapshot = {\\\\n ...latest_proposal_snapshot,\\\\n proposal_id,\\\\n linked_rfp: linked_rfp,\\\\n block_height: blockHeight,\\\\n ts: blockTimestamp,\\\\n editor_id: author,\\\\n views: latest_proposal_snapshot.views + 1,\\\\n };\\\\n await createProposalSnapshot(context, proposal_snapshot);\\\\n if (updateRfpSnapshot) {\\\\n await checkAndUpdateLinkedProposals(\\\\n proposal_id,\\\\n linked_rfp,\\\\n blockHeight,\\\\n blockTimestamp,\\\\n author\\\\n );\\\\n }\\\\n }\\\\n}\\\\n\\\\nasync function editProposalTimeline(\\\\n context,\\\\n proposal_id,\\\\n author,\\\\n blockHeight,\\\\n blockTimestamp,\\\\n timeline\\\\n) {\\\\n let latest_proposal_snapshot = await queryLatestProposalSnapshot(\\\\n proposal_id,\\\\n blockTimestamp\\\\n );\\\\n\\\\n if (latest_proposal_snapshot) {\\\\n let proposal_snapshot = {\\\\n ...latest_proposal_snapshot,\\\\n proposal_id,\\\\n block_height: blockHeight,\\\\n ts: blockTimestamp,\\\\n editor_id: author,\\\\n timeline: JSON.stringify(timeline), // TimelineStatus\\\\n views: latest_proposal_snapshot.views + 1,\\\\n };\\\\n await createProposalSnapshot(context, proposal_snapshot);\\\\n } else {\\\\n console.log(\\\\\\\"Empty object latest_proposal_snapshot result\\\\\\\", {\\\\n latest_proposal_snapshot,\\\\n });\\\\n }\\\\n}\\\\n\\\\nasync function checkAndUpdateLabels(\\\\n old_labels,\\\\n new_labels,\\\\n linked_proposals,\\\\n blockHeight,\\\\n blockTimestamp,\\\\n editor_id\\\\n) {\\\\n try {\\\\n const eqSet = (xs, ys) =>\\\\n xs.size === ys.size && [...xs].every((x) => ys.has(x));\\\\n\\\\n if (old_labels == undefined) {\\\\n old_labels = [];\\\\n }\\\\n\\\\n if (!eqSet(new Set(old_labels), new Set(new_labels))) {\\\\n for (let proposal_id of linked_proposals) {\\\\n let latest_proposal_snapshot = await queryLatestProposalSnapshot(\\\\n proposal_id,\\\\n blockTimestamp\\\\n );\\\\n if (latest_proposal_snapshot) {\\\\n let proposal_snapshot = {\\\\n ...latest_proposal_snapshot,\\\\n labels: new_labels,\\\\n block_height: blockHeight,\\\\n ts: blockTimestamp,\\\\n editor_id: editor_id,\\\\n };\\\\n await createProposalSnapshot(context, proposal_snapshot);\\\\n } else {\\\\n console.log(\\\\\\\"Empty object latest_proposal_snapshot result\\\\\\\", {\\\\n latest_proposal_snapshot,\\\\n });\\\\n }\\\\n }\\\\n }\\\\n } catch (error) {\\\\n console.error(\\\\\\\"Error checking and updating labels:\\\\\\\", error);\\\\n }\\\\n}\\\\n\\\\nasync function createDump(\\\\n context,\\\\n {\\\\n receipt_id,\\\\n method_name,\\\\n block_height,\\\\n block_timestamp,\\\\n args,\\\\n author,\\\\n proposal_id,\\\\n }\\\\n) {\\\\n const dump = {\\\\n receipt_id,\\\\n method_name,\\\\n block_height,\\\\n block_timestamp,\\\\n args,\\\\n author,\\\\n proposal_id,\\\\n };\\\\n try {\\\\n console.log(\\\\\\\"Creating a dump...\\\\\\\");\\\\n await context.db.Dumps.insert(dump);\\\\n console.log(\\\\n `Dump ${author} ${method_name} proposal ${proposal_id} has been added to the database`\\\\n );\\\\n return null;\\\\n } catch (e) {\\\\n console.log(\\\\n `Error creating ${author} ${method_name} proposal ${proposal_id}: ${e}`\\\\n );\\\\n return e;\\\\n }\\\\n}\\\\n\\\\nasync function createProposal(context, { id, author_id }) {\\\\n const proposal = { id, author_id };\\\\n try {\\\\n console.log(\\\\\\\"Creating a Proposal\\\\\\\");\\\\n await context.db.Proposals.insert(proposal);\\\\n console.log(`Proposal ${id} has been added to the database`);\\\\n return null;\\\\n } catch (e) {\\\\n console.log(`Error creating Proposal with id ${id}: ${e}`);\\\\n return e;\\\\n }\\\\n}\\\\n\\\\nasync function createProposalSnapshot(\\\\n context,\\\\n {\\\\n proposal_id,\\\\n social_db_post_block_height,\\\\n proposal_version,\\\\n block_height,\\\\n ts, // Timestamp\\\\n editor_id,\\\\n labels,\\\\n proposal_body_version,\\\\n name,\\\\n category,\\\\n summary,\\\\n description,\\\\n linked_proposals, // Vec<ProposalId>\\\\n linked_rfp, // Option<RFPId>\\\\n requested_sponsorship_usd_amount, // u32\\\\n requested_sponsorship_paid_in_currency, // ProposalFundingCurrency\\\\n requested_sponsor, // AccountId\\\\n receiver_account, // AccountId\\\\n supervisor, // Option\\\\n timeline, // TimelineStatus\\\\n views,\\\\n }\\\\n) {\\\\n const proposal_snapshot = {\\\\n proposal_id,\\\\n social_db_post_block_height,\\\\n proposal_version,\\\\n block_height,\\\\n ts,\\\\n editor_id,\\\\n labels: JSON.stringify(labels),\\\\n proposal_body_version,\\\\n name,\\\\n category,\\\\n summary,\\\\n description,\\\\n linked_proposals: JSON.stringify(linked_proposals),\\\\n linked_rfp, // Option<RFPId>\\\\n requested_sponsorship_usd_amount, // u32\\\\n requested_sponsorship_paid_in_currency, // ProposalFundingCurrency\\\\n requested_sponsor, // AccountId\\\\n receiver_account, // AccountId\\\\n supervisor, // Option<AccountId>\\\\n views,\\\\n timeline: JSON.stringify(timeline), // TimelineStatus\\\\n };\\\\n try {\\\\n console.log(\\\\\\\"Creating a ProposalSnapshot\\\\\\\");\\\\n await context.db.ProposalSnapshots.insert(proposal_snapshot);\\\\n console.log(\\\\n `Proposal Snapshot with proposal_id ${proposal_id} at block_height ${block_height} has been added to the database`\\\\n );\\\\n return null;\\\\n } catch (e) {\\\\n console.log(\\\\n `Error creating Proposal Snapshot with proposal_id ${proposal_id} at block_height ${block_height}: ${e}`\\\\n );\\\\n return e;\\\\n }\\\\n}\\\\n\\\\nfunction getLatestObject(array, blockTimestamp) {\\\\n if (array == null || array.length === 0) {\\\\n return null;\\\\n }\\\\n let result = array.reduce((prev, current) =>\\\\n (prev.ts > current.ts && prev.ts < blockTimestamp) ||\\\\n current.ts >= blockTimestamp\\\\n ? prev\\\\n : current\\\\n );\\\\n if (result == null || result.ts >= blockTimestamp) {\\\\n return null;\\\\n }\\\\n return result;\\\\n}\\\\n\\\\nconst queryLatestProposalSnapshot = async (proposal_id, blockTimestamp) => {\\\\n try {\\\\n let snapshots = await context.db.ProposalSnapshots.select(\\\\n { proposal_id: proposal_id },\\\\n (limit = null)\\\\n );\\\\n let latest_snapshot = getLatestObject(snapshots, blockTimestamp);\\\\n return latest_snapshot;\\\\n } catch (e) {\\\\n console.log(\\\\\\\"Error retrieving latest Proposal snapshot:\\\\\\\", e);\\\\n return null;\\\\n }\\\\n};\\\\n\\\\nasync function createRFPDump(\\\\n context,\\\\n {\\\\n receipt_id,\\\\n method_name,\\\\n block_height,\\\\n block_timestamp,\\\\n args,\\\\n author,\\\\n rfp_id,\\\\n }\\\\n) {\\\\n const dump = {\\\\n receipt_id,\\\\n method_name,\\\\n block_height,\\\\n block_timestamp,\\\\n args,\\\\n author,\\\\n rfp_id,\\\\n };\\\\n try {\\\\n console.log(\\\\\\\"Creating a dump...\\\\\\\");\\\\n context.db.RfpDumps.insert(dump);\\\\n console.log(\\\\n `Dump ${author} ${method_name} rfp ${rfp_id} has been added to the database`\\\\n );\\\\n return null;\\\\n } catch (e) {\\\\n console.log(`Error creating ${author} ${method_name} rfp ${rfp_id}: ${e}`);\\\\n return e;\\\\n }\\\\n}\\\\n\\\\nasync function createRFP(context, { id, author_id }) {\\\\n const rfp = { id, author_id };\\\\n try {\\\\n console.log(\\\\\\\"Creating a rfp\\\\\\\");\\\\n await context.db.Rfps.insert(rfp);\\\\n console.log(`rfp ${id} has been added to the database`);\\\\n return null;\\\\n } catch (e) {\\\\n console.log(`Error creating rfp with id ${id}: ${e}`);\\\\n return e;\\\\n }\\\\n}\\\\n\\\\nasync function createrfpSnapshot(\\\\n context,\\\\n {\\\\n rfp_id,\\\\n block_height,\\\\n social_db_post_block_height,\\\\n rfp_version,\\\\n ts, // Timestamp\\\\n editor_id,\\\\n labels,\\\\n linked_proposals,\\\\n rfp_body_version,\\\\n name,\\\\n summary,\\\\n description,\\\\n timeline, // TimelineStatus\\\\n submission_deadline,\\\\n views,\\\\n }\\\\n) {\\\\n const rfp_snapshot = {\\\\n rfp_id,\\\\n block_height,\\\\n social_db_post_block_height,\\\\n rfp_version,\\\\n ts,\\\\n editor_id,\\\\n labels: JSON.stringify(labels),\\\\n linked_proposals: JSON.stringify(linked_proposals),\\\\n rfp_body_version,\\\\n name,\\\\n summary,\\\\n description,\\\\n views,\\\\n timeline: JSON.stringify(timeline), // TimelineStatus\\\\n submission_deadline,\\\\n };\\\\n try {\\\\n console.log(\\\\\\\"Creating a rfpSnapshot\\\\\\\");\\\\n await context.db.RfpSnapshots.insert(rfp_snapshot);\\\\n console.log(\\\\n `rfp Snapshot with rfp_id ${rfp_id} at block_height ${block_height} has been added to the database`\\\\n );\\\\n return null;\\\\n } catch (e) {\\\\n console.log(\\\\n `Error creating rfp Snapshot with rfp_id ${rfp_id} at block_height ${block_height}: ${e}`\\\\n );\\\\n return e;\\\\n }\\\\n}\\\\n\\\\nconst queryLatestRFPSnapshot = async (rfp_id, blockTimestamp) => {\\\\n try {\\\\n let snapshots = await context.db.RfpSnapshots.select(\\\\n { rfp_id: rfp_id },\\\\n (limit = null)\\\\n );\\\\n let latest_snapshot = getLatestObject(snapshots, blockTimestamp);\\\\n return latest_snapshot;\\\\n } catch (e) {\\\\n console.log(\\\\\\\"Error retrieving latest RFP snapshot:\\\\\\\", e);\\\\n return null;\\\\n }\\\\n", |
| 8 | "schema": "CREATE TABLE\\\\n proposals (id serial primary key, author_id VARCHAR not null);\\\\n\\\\nCREATE TABLE\\\\n proposal_snapshots (\\\\n -- due to how query api runs, an edit_proposal can be processed by the worker before corresponding add_proposal, so we can\\\\\\'t enforce proposal_id as foreign key\\\\n proposal_id int,\\\\n block_height bigint,\\\\n ts decimal(20, 0),\\\\n editor_id varchar,\\\\n social_db_post_block_height bigint,\\\\n labels jsonb,\\\\n proposal_version varchar,\\\\n proposal_body_version varchar,\\\\n \\\\\\\"name\\\\\\\" text,\\\\n category varchar,\\\\n summary text,\\\\n description text,\\\\n linked_proposals jsonb,\\\\n linked_rfp int,\\\\n requested_sponsorship_usd_amount decimal,\\\\n requested_sponsorship_paid_in_currency varchar,\\\\n requested_sponsor varchar,\\\\n receiver_account varchar,\\\\n supervisor varchar,\\\\n timeline jsonb,\\\\n views int,\\\\n primary key (proposal_id, ts)\\\\n );\\\\n\\\\nCREATE TABLE\\\\n dumps (\\\\n receipt_id varchar primary key,\\\\n method_name varchar,\\\\n block_height bigint,\\\\n block_timestamp decimal(20, 0),\\\\n args varchar,\\\\n author varchar,\\\\n proposal_id bigint\\\\n );\\\\n\\\\nCREATE INDEX\\\\n idx_proposals_author_id ON proposals (author_id);\\\\n\\\\nCREATE INDEX\\\\n idx_proposal_snapshots_proposal_id ON proposal_snapshots (proposal_id);\\\\n\\\\nCREATE INDEX\\\\n idx_proposal_snapshots_category ON proposal_snapshots (category);\\\\n\\\\nCREATE INDEX\\\\n idx_proposal_snapshots_ts ON proposal_snapshots (ts);\\\\n\\\\nCREATE INDEX\\\\n idx_proposal_snapshots_editor_id ON proposal_snapshots (editor_id);\\\\n\\\\nCREATE INDEX\\\\n idx_proposal_snapshots_labels ON proposal_snapshots USING GIN (labels);\\\\n\\\\nCREATE INDEX\\\\n idx_fulltext_proposal_snapshots_description ON proposal_snapshots USING gin (to_tsvector(\\\\\\'english\\\\\\', description));\\\\n\\\\nCREATE INDEX\\\\n idx_fulltext_proposal_snapshots_summary ON proposal_snapshots USING gin (to_tsvector(\\\\\\'english\\\\\\', summary));\\\\n\\\\nCREATE INDEX\\\\n idx_fulltext_proposal_snapshots_timeline ON proposal_snapshots USING gin (to_tsvector(\\\\\\'english\\\\\\', timeline));\\\\n\\\\nCREATE INDEX\\\\n idx_fulltext_proposal_snapshots_name ON proposal_snapshots USING gin (to_tsvector(\\\\\\'english\\\\\\', name));\\\\n\\\\nCREATE INDEX\\\\n idx_proposal_snapshots_sponsorship_supervisor ON proposal_snapshots (supervisor);\\\\n\\\\nCREATE INDEX\\\\n idx_proposal_snapshots_sponsorship_receiver_account ON proposal_snapshots (receiver_account);\\\\n\\\\nCREATE INDEX\\\\n idx_proposal_snapshots_views ON proposal_snapshots (views);\\\\n\\\\nCREATE VIEW\\\\n proposals_with_latest_snapshot AS\\\\nSELECT\\\\n ps.proposal_id,\\\\n p.author_id,\\\\n ps.block_height,\\\\n ps.ts,\\\\n ps.editor_id,\\\\n ps.social_db_post_block_height,\\\\n ps.labels,\\\\n ps.proposal_version,\\\\n ps.proposal_body_version,\\\\n ps.name,\\\\n ps.category,\\\\n ps.summary,\\\\n ps.description,\\\\n ps.linked_proposals,\\\\n ps.linked_rfp,\\\\n ps.requested_sponsorship_usd_amount,\\\\n ps.requested_sponsorship_paid_in_currency,\\\\n ps.requested_sponsor,\\\\n ps.receiver_account,\\\\n ps.supervisor,\\\\n ps.timeline,\\\\n ps.views\\\\nFROM\\\\n proposals p\\\\n INNER JOIN (\\\\n SELECT\\\\n proposal_id,\\\\n MAX(ts) AS max_ts\\\\n FROM\\\\n proposal_snapshots\\\\n GROUP BY\\\\n proposal_id\\\\n ) latest_snapshots ON p.id = latest_snapshots.proposal_id\\\\n INNER JOIN proposal_snapshots ps ON latest_snapshots.proposal_id = ps.proposal_id\\\\n AND latest_snapshots.max_ts = ps.ts;\\\\n\\\\nCREATE TABLE\\\\n rfps (id serial primary key, author_id VARCHAR not null);\\\\n\\\\nCREATE TABLE\\\\n rfp_snapshots (\\\\n -- due to how query api runs, an edit_rfp can be processed by the worker before corresponding add_rfp, so we can\\\\\\'t enforce rfp_id as foreign key\\\\n rfp_id int REFERENCES rfps (id),\\\\n block_height bigint,\\\\n ts decimal(20, 0),\\\\n editor_id varchar,\\\\n social_db_post_block_height bigint,\\\\n labels jsonb,\\\\n linked_proposals jsonb,\\\\n rfp_version varchar,\\\\n rfp_body_version varchar,\\\\n \\\\\\\"name\\\\\\\" text,\\\\n category varchar,\\\\n summary text,\\\\n description text,\\\\n timeline jsonb,\\\\n submission_deadline decimal(20, 0),\\\\n views int,\\\\n primary key (rfp_id, ts)\\\\n );\\\\n\\\\nCREATE INDEX\\\\n idx_rfps_author_id ON rfps (author_id);\\\\n\\\\nCREATE INDEX\\\\n idx_rfp_snapshots_rfp_id ON rfp_snapshots (rfp_id);\\\\n\\\\nCREATE INDEX\\\\n idx_rfp_snapshots_category ON rfp_snapshots (category);\\\\n\\\\nCREATE INDEX\\\\n idx_rfp_snapshots_ts ON rfp_snapshots (ts);\\\\n\\\\nCREATE INDEX\\\\n idx_rfp_snapshots_editor_id ON rfp_snapshots (editor_id);\\\\n\\\\nCREATE INDEX\\\\n idx_rfp_snapshots_labels ON rfp_snapshots USING GIN (labels);\\\\n\\\\nCREATE INDEX\\\\n idx_fulltext_rfp_snapshots_description ON rfp_snapshots USING gin (to_tsvector(\\\\\\'english\\\\\\', description));\\\\n\\\\nCREATE INDEX\\\\n idx_fulltext_rfp_snapshots_summary ON rfp_snapshots USING gin (to_tsvector(\\\\\\'english\\\\\\', summary));\\\\n\\\\nCREATE INDEX\\\\n idx_fulltext_rfp_snapshots_timeline ON rfp_snapshots USING gin (to_tsvector(\\\\\\'english\\\\\\', timeline));\\\\n\\\\nCREATE INDEX\\\\n idx_fulltext_rfp_snapshots_name ON rfp_snapshots USING gin (to_tsvector(\\\\\\'english\\\\\\', name));\\\\n\\\\nCREATE INDEX\\\\n idx_rfp_snapshots_views ON rfp_snapshots (views);\\\\n\\\\nCREATE VIEW\\\\n rfps_with_latest_snapshot AS\\\\nSELECT\\\\n ps.rfp_id,\\\\n p.author_id,\\\\n ps.block_height,\\\\n ps.ts,\\\\n ps.editor_id,\\\\n ps.social_db_post_block_height,\\\\n ps.labels,\\\\n ps.linked_proposals,\\\\n ps.rfp_version,\\\\n ps.rfp_body_version,\\\\n ps.name,\\\\n ps.category,\\\\n ps.summary,\\\\n ps.description,\\\\n ps.timeline,\\\\n ps.views,\\\\n ps.submission_deadline\\\\nFROM\\\\n rfps p\\\\n INNER JOIN (\\\\n SELECT\\\\n rfp_id,\\\\n MAX(ts) AS max_ts\\\\n FROM\\\\n rfp_snapshots\\\\n GROUP BY\\\\n rfp_id\\\\n ) latest_snapshots ON p.id = latest_snapshots.rfp_id\\\\n INNER JOIN rfp_snapshots ps ON latest_snapshots.rfp_id = ps.rfp_id\\\\n AND latest_snapshots.max_ts = ps.ts;\\\\n\\\\nCREATE TABLE\\\\n rfp_dumps (\\\\n receipt_id varchar primary key,\\\\n method_name varchar,\\\\n block_height bigint,\\\\n block_timestamp decimal(20, 0),\\\\n args varchar,\\\\n author varchar,\\\\n rfp_id bigint\\\\n );\\\\n", |