diff --git a/kleros-sdk/dataMappings/dataMappings.ts b/kleros-sdk/dataMappings/dataMappings.ts index 6803d596c..8881eb29a 100644 --- a/kleros-sdk/dataMappings/dataMappings.ts +++ b/kleros-sdk/dataMappings/dataMappings.ts @@ -11,66 +11,6 @@ const publicClient = createPublicClient({ transport, }); -export const mappings = [ - { - type: "fetch", - source: "someAPIEndpoint", - inputs: { - variableName: "disputeData", - link: "https://someapi.com/disputeData", - }, - seek: [], - populate: [], - }, - { - type: "graphql", - source: "someGraphqlEndpoint", - inputs: { - variableName: "submissionData", - query: "someGraphqlQuery", - }, - seek: [], - populate: [], - }, - { - type: "json", - source: "evidence", - inputs: {}, - seek: ["fileURI"], - populate: ["fileURI"], - }, - { - type: "json", - source: "fileURI", - inputs: {}, - seek: ["photo", "video"], - populate: ["photoUrl", "videoUrl"], - }, - { - type: "abi/call", - source: "contractCall", - inputs: [], - seek: [], - populate: [], - }, - { - type: "abi/event", - source: "contractEvent", - inputs: [], - seek: [], - populate: [], - }, -]; - -const initialState = { - evidence: { - fileURI: { - photo: "https://photo.url", - video: "https://video.url", - }, - }, -}; - const findNestedKey = (data, keyToFind) => { if (data.hasOwnProperty(keyToFind)) return data[keyToFind]; for (let key in data) { @@ -82,11 +22,11 @@ const findNestedKey = (data, keyToFind) => { return null; }; -export const jsonAction = (data, seek, populate) => { +export const jsonAction = (source, seek, populate) => { let jsonData = {}; seek.forEach((key, idx) => { - const foundValue = findNestedKey(data, key); + const foundValue = findNestedKey(source, key); jsonData[populate[idx]] = foundValue; }); @@ -129,17 +69,22 @@ export const graphqlAction = async (query: string, seek, populate) => { return populatedData; }; -export const callAction = async (abi, inputs, seek, populate) => { +export const callAction = async (source, inputs, seek, populate) => { + let parsedAbi; + + if (typeof source === "string") { + parsedAbi = parseAbiItem(source); + } else { + parsedAbi = source; + } + const data = await publicClient.readContract({ address: inputs[0], - abi: [abi], + abi: [parsedAbi], functionName: inputs[1], args: inputs.slice(2), }); - // seek values should be the index of the values we want from the return of the contract since - // wagmi/viem returns an array instead of an object - let populatedData = {}; seek.map((item) => { @@ -154,6 +99,14 @@ export const callAction = async (abi, inputs, seek, populate) => { }; export const eventAction = async (source, inputs, seek, populate) => { + let parsedAbi; + + if (typeof source === "string") { + parsedAbi = parseAbiItem(source); + } else { + parsedAbi = source; + } + const argsObject = seek.reduce((acc, key, index) => { acc[key] = inputs[index + 2]; return acc; @@ -161,7 +114,7 @@ export const eventAction = async (source, inputs, seek, populate) => { const filter = await publicClient.createEventFilter({ address: inputs[0], - event: source, + event: parsedAbi, args: { ...argsObject }, fromBlock: inputs[1], toBlock: "latest", @@ -171,7 +124,6 @@ export const eventAction = async (source, inputs, seek, populate) => { filter: filter as any, }); - // @ts-ignore const eventData = contractEvent[0].args; let populatedData = {}; @@ -183,41 +135,23 @@ export const eventAction = async (source, inputs, seek, populate) => { return populatedData; }; -// const accumulatedData = mappings.reduce(async (acc, { type, source, inputs, seek, populate }) => { -// const currentAcc = await acc; - -// switch (type) { -// case "fetch": -// return { -// ...currentAcc, -// ...(await fetchAction(inputs.variableName, inputs.link)), -// }; - -// case "graphql": -// return { -// ...currentAcc, -// ...(await graphqlAction(inputs.variableName, inputs.query)), -// }; - -// case "json": -// return { -// ...currentAcc, -// ...jsonAction(currentAcc, source, seek, populate), -// }; -// case "abi/call": -// return { -// ...currentAcc, -// ...(await callAction(source, inputs, seek, populate)), -// }; -// // case "abi/event": -// // return { -// // ...currentAcc, -// // ...(await eventAction(source, inputs, seek, populate)), -// // }; - -// default: -// return currentAcc; -// } -// }, Promise.resolve(initialState)); - -// console.log(accumulatedData); +export const executeAction = async (action) => { + switch (action.type) { + case "fetch": + return await fetchAction(action.source, action.seek, action.populate); + case "graphql": + return await graphqlAction(action.source, action.seek, action.populate); + case "json": + return jsonAction(action.source, action.seek, action.populate); + case "abi/call": + return await callAction(action.source, action.inputs, action.seek, action.populate); + case "abi/event": + return await eventAction(action.source, action.inputs, action.seek, action.populate); + default: + throw new Error(`Unsupported action type: ${action.type}`); + } +}; + +export const parseTemplateWithData = (template, data) => { + return template.replace(/\{\{(.*?)\}\}/g, (_, key) => data[key.trim()] || ""); +}; diff --git a/web/src/pages/DisputeTemplateView.tsx b/web/src/pages/DisputeTemplateView.tsx index c25cea9f4..98adbfc73 100644 --- a/web/src/pages/DisputeTemplateView.tsx +++ b/web/src/pages/DisputeTemplateView.tsx @@ -7,8 +7,7 @@ import ReactMarkdown from "components/ReactMarkdown"; import { isUndefined } from "utils/index"; import { IPFS_GATEWAY } from "consts/index"; import { useDisputeTemplate } from "hooks/queries/useDisputeTemplate"; -import { jsonAction, callAction } from "utils/dataMappings"; -import { parseAbiItem } from "viem"; +import { executeAction, parseTemplateWithData } from "utils/dataMappings"; const Container = styled.div` width: 50%; @@ -84,37 +83,41 @@ const StyledTextArea = styled(Textarea)` const DisputeTemplateView: React.FC = () => { const [disputeTemplate, setDisputeTemplate] = useState(""); const [errorMsg, setErrorMessage] = useState(""); - const { data: template } = useDisputeTemplate("4", "0xE4af4D800Ce12149199FA6f8870cD650cD8f3164"); - // console.log("🚀 ~ file: DisputeTemplateView.tsx:86 ~ template:", template!.templateDataMappings); + const { data: template } = useDisputeTemplate("7", "0xE4af4D800Ce12149199FA6f8870cD650cD8f3164"); const parsedDisputeTemplate = useMemo(async () => { try { - const parsedMapping = JSON.parse(template?.templateDataMappings); + console.log(template); const parsedTemplate = JSON.parse(template?.templateData); - console.log("🚀 ~ file: DisputeTemplateView.tsx:94 ~ parsedDisputeTemplate ~ parsedTemplate:", parsedTemplate); - console.log("🚀 ~ file: DisputeTemplateView.tsx:90 ~ parsedDisputeTemplate ~ parsed:", parsedMapping); - const { inputs, populate, seeks, source } = parsedMapping[0]; - const parsedAbi = parseAbiItem(source); - inputs[3] = BigInt(inputs[3]); - console.log( - "🚀 ~ file: DisputeTemplateView.tsx:94 ~ parsedDisputeTemplate ~ inputs:", - inputs.slice(2), - populate, - seeks, - parsedAbi - ); - const result = await callAction(parsedAbi, inputs, seeks, populate); - console.log("🚀 ~ file: DisputeTemplateView.tsx:96 ~ parsedDisputeTemplate ~ result:", result); + const parsedMapping = JSON.parse(template?.templateDataMappings); + + console.log("parsedTemplate", parsedTemplate); + console.log("parsedMapping", parsedMapping); + + let populatedData = {}; + + for (const action of parsedMapping) { + console.log("action of parsedMapping", action); + const result = await executeAction(action); + console.log("result of executeAction", result); + populatedData = { ...populatedData, ...result }; + console.log("populatedData inside loop", populatedData); + } + + const finalTemplate = parseTemplateWithData(parsedTemplate.template.content, populatedData); + console.log("finalTemplate with everything parsed", finalTemplate); + setDisputeTemplate(finalTemplate); setErrorMessage(""); - return parsedTemplate; + return finalTemplate; } catch (e) { console.log("error", e); - setErrorMessage((e as SyntaxError).message); return undefined; } }, [template, disputeTemplate]); + const isThereInput = useMemo(() => disputeTemplate !== "", [disputeTemplate]); + return ( { if (data.hasOwnProperty(keyToFind)) return data[keyToFind]; for (let key in data) { @@ -82,11 +22,11 @@ const findNestedKey = (data, keyToFind) => { return null; }; -export const jsonAction = (data, seek, populate) => { +export const jsonAction = (source, seek, populate) => { let jsonData = {}; seek.forEach((key, idx) => { - const foundValue = findNestedKey(data, key); + const foundValue = findNestedKey(source, key); jsonData[populate[idx]] = foundValue; }); @@ -129,17 +69,22 @@ export const graphqlAction = async (query: string, seek, populate) => { return populatedData; }; -export const callAction = async (abi, inputs, seek, populate) => { +export const callAction = async (source, inputs, seek, populate) => { + let parsedAbi; + + if (typeof source === "string") { + parsedAbi = parseAbiItem(source); + } else { + parsedAbi = source; + } + const data = await publicClient.readContract({ address: inputs[0], - abi: [abi], + abi: [parsedAbi], functionName: inputs[1], args: inputs.slice(2), }); - // seek values should be the index of the values we want from the return of the contract since - // wagmi/viem returns an array instead of an object - let populatedData = {}; seek.map((item) => { @@ -154,6 +99,14 @@ export const callAction = async (abi, inputs, seek, populate) => { }; export const eventAction = async (source, inputs, seek, populate) => { + let parsedAbi; + + if (typeof source === "string") { + parsedAbi = parseAbiItem(source); + } else { + parsedAbi = source; + } + const argsObject = seek.reduce((acc, key, index) => { acc[key] = inputs[index + 2]; return acc; @@ -161,7 +114,7 @@ export const eventAction = async (source, inputs, seek, populate) => { const filter = await publicClient.createEventFilter({ address: inputs[0], - event: source, + event: parsedAbi, args: { ...argsObject }, fromBlock: inputs[1], toBlock: "latest", @@ -171,7 +124,6 @@ export const eventAction = async (source, inputs, seek, populate) => { filter: filter as any, }); - // @ts-ignore const eventData = contractEvent[0].args; let populatedData = {}; @@ -183,41 +135,23 @@ export const eventAction = async (source, inputs, seek, populate) => { return populatedData; }; -// const accumulatedData = mappings.reduce(async (acc, { type, source, inputs, seek, populate }) => { -// const currentAcc = await acc; - -// switch (type) { -// case "fetch": -// return { -// ...currentAcc, -// ...(await fetchAction(inputs.variableName, inputs.link)), -// }; - -// case "graphql": -// return { -// ...currentAcc, -// ...(await graphqlAction(inputs.variableName, inputs.query)), -// }; - -// case "json": -// return { -// ...currentAcc, -// ...jsonAction(currentAcc, source, seek, populate), -// }; -// case "abi/call": -// return { -// ...currentAcc, -// ...(await callAction(source, inputs, seek, populate)), -// }; -// // case "abi/event": -// // return { -// // ...currentAcc, -// // ...(await eventAction(source, inputs, seek, populate)), -// // }; - -// default: -// return currentAcc; -// } -// }, Promise.resolve(initialState)); - -// console.log(accumulatedData); +export const executeAction = async (action) => { + switch (action.type) { + case "fetch": + return await fetchAction(action.source, action.seek, action.populate); + case "graphql": + return await graphqlAction(action.source, action.seek, action.populate); + case "json": + return jsonAction(action.source, action.seek, action.populate); + case "abi/call": + return await callAction(action.source, action.inputs, action.seek, action.populate); + case "abi/event": + return await eventAction(action.source, action.inputs, action.seek, action.populate); + default: + throw new Error(`Unsupported action type: ${action.type}`); + } +}; + +export const parseTemplateWithData = (template, data) => { + return template.replace(/\{\{(.*?)\}\}/g, (_, key) => data[key.trim()] || ""); +};