1"use strict";(self.webpackChunkrarimo_docs=self.webpackChunkrarimo_docs||[]).push([[833],{1986:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>d,contentTitle:()=>u,default:()=>g,frontMatter:()=>c,metadata:()=>n,toc:()=>p});const n=JSON.parse('{"id":"zk-passport/guide-setting-up-cross-chain-replication","title":"Guide: Setting up cross-chain replication for ZK Passport","description":"Set up ZK Passport Registry replication to verify ZK Passport proofs on any EVM-compatible chain.","source":"@site/docs/zk-passport/guide-setting-up-cross-chain-replication.mdx","sourceDirName":"zk-passport","slug":"/zk-passport/guide-setting-up-cross-chain-replication","permalink":"/zk-passport/guide-setting-up-cross-chain-replication","draft":false,"unlisted":false,"editUrl":"https://github.com/rarimo/docs/blob/main/docs/zk-passport/guide-setting-up-cross-chain-replication.mdx","tags":[],"version":"current","lastUpdatedAt":1750947153000,"frontMatter":{"description":"Set up ZK Passport Registry replication to verify ZK Passport proofs on any EVM-compatible chain.","sidebar_label":"Going cross-chain"},"sidebar":"sidebar","previous":{"title":"Hosting your own verificator","permalink":"/zk-passport/guide-setting-up-verificator-svc"},"next":{"title":"Smart contracts","permalink":"/zk-passport/contracts"}}');var s=r(4848),a=r(8453),i=r(9489),o=r(7227),l=r(8210);r(4612);const c={description:"Set up ZK Passport Registry replication to verify ZK Passport proofs on any EVM-compatible chain.",sidebar_label:"Going cross-chain"},u="Guide: Setting up cross-chain replication for ZK Passport",d={},p=[{value:"Step 1: Deploy the RegistrationSMTReplicator",id:"step-1-deploy-the-registrationsmtreplicator",level:2},{value:"Step 2: Set up the relayer with front-end submission to replicate the states",id:"step-2-set-up-the-relayer-with-front-end-submission-to-replicate-the-states",level:2}];function h(e){const t={admonition:"admonition",code:"code",h1:"h1",h2:"h2",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(t.header,{children:(0,s.jsx)(t.h1,{id:"guide-setting-up-cross-chain-replication-for-zk-passport",children:"Guide: Setting up cross-chain replication for ZK Passport"})}),"\n",(0,s.jsx)(t.admonition,{type:"info",children:(0,s.jsx)(t.p,{children:"Native state transfer to Ethereum L1 is currently under construction."})}),"\n",(0,s.jsx)(t.p,{children:"This guide walks you through the process of setting up cross-chain replication for ZK Passport Registry state. By the end of this tutorial, you will have:"}),"\n",(0,s.jsxs)(t.ol,{children:["\n",(0,s.jsxs)(t.li,{children:["Deployed the ",(0,s.jsx)(t.strong,{children:"RegistrationSMTReplicator"})," contract on the target chain."]}),"\n",(0,s.jsx)(t.li,{children:"Set up your relayer or front-end submission flow."}),"\n"]}),"\n",(0,s.jsx)(t.h2,{id:"step-1-deploy-the-registrationsmtreplicator",children:"Step 1: Deploy the RegistrationSMTReplicator"}),"\n",(0,s.jsxs)(t.p,{children:["The ",(0,s.jsx)(t.strong,{children:"RegistrationSMTReplicator"})," handles the replication of state from the ZK Registry onto your chain. This ensures your chain is kept up to date with changes to passport registrations."]}),"\n",(0,s.jsxs)(t.ol,{children:["\n",(0,s.jsxs)(t.li,{children:["\n",(0,s.jsxs)(t.p,{children:[(0,s.jsx)(t.strong,{children:"Install the Rarimo Passport Contracts"})," package:"]}),"\n",(0,s.jsxs)(i.A,{groupId:"npm2yarn",children:[(0,s.jsx)(o.A,{value:"npm",children:(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-bash",children:"npm install @rarimo/passport-contracts\n"})})}),(0,s.jsx)(o.A,{value:"yarn",label:"Yarn",children:(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-bash",children:"yarn add @rarimo/passport-contracts\n"})})}),(0,s.jsx)(o.A,{value:"pnpm",label:"pnpm",children:(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-bash",children:"pnpm add @rarimo/passport-contracts\n"})})}),(0,s.jsx)(o.A,{value:"bun",label:"Bun",children:(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-bash",children:"bun add @rarimo/passport-contracts\n"})})})]}),"\n"]}),"\n",(0,s.jsxs)(t.li,{children:["\n",(0,s.jsxs)(t.p,{children:[(0,s.jsx)(t.strong,{children:"Generate a key pair"})," for the relayer that will submit state updates to the ",(0,s.jsx)(t.code,{children:"RegistrationSMTReplicator"})," contract."]}),"\n",(0,s.jsxs)(t.p,{children:["You can use the ",(0,s.jsx)(t.code,{children:"ethers"})," library to generate a new key pair:"]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-typescript",children:"import { ethers } from 'ethers';\n\nconst wallet = ethers.Wallet.createRandom();\nconsole.log('Address:', wallet.address);\nconsole.log('Private Key:', wallet.privateKey);\n"})}),"\n",(0,s.jsx)(t.p,{children:"Make sure to securely store the private key, as it will be used by the relayer to sign state updates."}),"\n"]}),"\n",(0,s.jsxs)(t.li,{children:["\n",(0,s.jsxs)(t.p,{children:[(0,s.jsx)(t.strong,{children:"Deploy"})," the ",(0,s.jsx)(t.code,{children:"RegistrationSMTReplicator"})," to your chain and initialize it:"]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-typescript",children:"import { RegistrationSMTReplicator } from '@rarimo/passport-contracts';\n\nconst replicator = await RegistrationSMTReplicator.deploy({\n // Specify the chain and network parameters here\n});\n\nconst oracle = '0x...'; // Replace with your relayer address from the previous step\nconst sourceSMT = '0x479F84502Db545FA8d2275372E0582425204A879'; // This is the address of the original ZK Passport Registry on Rarimo L2.\n\nawait replicator.__RegistrationSMTReplicator_init(\n [oracle], // list of oracle public keys that can submit roots directly\n sourceSMT // contract address of the original registry on Rarimo L2\n);\n"})}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(t.h2,{id:"step-2-set-up-the-relayer-with-front-end-submission-to-replicate-the-states",children:"Step 2: Set up the relayer with front-end submission to replicate the states"}),"\n",(0,s.jsxs)(t.p,{children:["To keep the state of the ZK Registry in sync with your chain, you need to set up a relayer with front-end submission.\nThis will ensure that any changes in the ZK Registry are reflected in the ",(0,s.jsx)(t.code,{children:"RegistrationSMTReplicator"})," contract."]}),"\n",(0,s.jsx)(t.p,{children:"You have to set up backend service and make your front-end app update the root:"}),"\n",(0,s.jsxs)(t.ol,{children:["\n",(0,s.jsxs)(t.li,{children:["\n",(0,s.jsxs)(t.p,{children:[(0,s.jsx)(t.strong,{children:"Relayer"}),": Run a relayer service that listens for events from the ZK Registry."]}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsxs)(t.li,{children:["Follow the instructions in ",(0,s.jsx)(l.A,{href:"https://github.com/rarimo/proof-verification-relayer",children:"proof-verification-relayer README"}),"."]}),"\n",(0,s.jsxs)(t.li,{children:["Make sure you set the right replicator contract address and private key of corresponding ",(0,s.jsx)(t.code,{children:"allowedUpdaterAddress"})," in the config."]}),"\n",(0,s.jsxs)(t.li,{children:["Make sure ",(0,s.jsx)(l.A,{href:"https://rarimo.github.io/proof-verification-relayer/#tag/State/operation/GetState",children:"endpoint"})," is available for your frontend app."]}),"\n",(0,s.jsxs)(t.li,{children:["Make sure that the relayer's corresponding key is set as an oracle in the replicator contract. You can check this using the ",(0,s.jsx)(l.A,{href:"https://github.com/rarimo/passport-contracts/blob/6812b70ca12209ccf1bb3179e1358dbcbe7e9f3e/contracts/sdk/RegistrationSMTReplicator.sol#L152",children:(0,s.jsx)(t.code,{children:"isOracle(address)"})})," method on the ",(0,s.jsx)(t.code,{children:"RegistrationSMTReplicator"})," contract."]}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(t.li,{children:["\n",(0,s.jsxs)(t.p,{children:[(0,s.jsx)(t.strong,{children:"Front-end"}),": Send the state update transactions on-demand from your front-end:"]}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsxs)(t.li,{children:["After obtaining the proof from verificator-svc you know the required ",(0,s.jsx)(t.code,{children:"root"})," (it is a ",(0,s.jsx)(l.A,{href:"https://github.com/rarimo/passport-zk-circuits/?tab=readme-ov-file#query-circuit-public-signals",children:"11th pub signal"}),")."]}),"\n",(0,s.jsx)(t.li,{children:"Now you can get signed state by root"}),"\n"]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-typescript",children:"async function getSignedRootState(root: string) {\n const API_BASE_URL = 'https://api.example.com'\n\n const requestUrl = new URL(`${API_BASE_URL}/integrations/proof-verification-relayer/v2/state`)\n requestUrl.searchParams.set('filter[root]', root)\n\n const res = await fetch(requestUrl.toString())\n const { data } = await res.json()\n\n return {\n // Signature of root state signed by relayer private key.\n signature: data.attributes.signature,\n // Time when the event was caught, a.k.a state transition timestamp\n timestamp: data.attributes.timestamp,\n }\n}\n"})}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsxs)(t.li,{children:["You can use the ",(0,s.jsx)(l.A,{href:"https://github.com/rarimo/passport-contracts/blob/6812b70ca12209ccf1bb3179e1358dbcbe7e9f3e/contracts/sdk/RegistrationSMTReplicator.sol#L102",children:(0,s.jsx)(t.code,{children:"transitionRootWithSignature(root, timestamp, signature)"})})," method on the ",(0,s.jsx)(t.code,{children:"RegistrationSMTReplicator"})," contract to submit state updates."]}),"\n"]}),"\n"]}),"\n"]})]})}function g(e={}){const{wrapper:t}={...(0,a.R)(),...e.components};return t?(0,s.jsx)(t,{...e,children:(0,s.jsx)(h,{...e})}):h(e)}},7227:(e,t,r)=>{r.d(t,{A:()=>i});r(6540);var n=r(4164);const s={tabItem:"tabItem_Ymn6"};var a=r(4848);function i({children:e,hidden:t,className:r}){return(0,a.jsx)("div",{role:"tabpanel",className:(0,n.A)(s.tabItem,r),hidden:t,children:e})}},9489:(e,t,r)=>{r.d(t,{A:()=>R});
1var n=r(6540),s=r(4164),a=r(4245),i=r(6347),o=r(6494),l=r(2814),c=r(5167),u=r(9900);function d(e){return n.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,n.isValidElement)(e)&&function(e){const{props:t}=e;return!!t&&"object"==typeof t&&"value"in t}(e))return e;throw new Error(`Docusaurus error: Bad <Tabs> child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function p(e){const{values:t,children:r}=e;return(0,n.useMemo)((()=>{const e=t??function(e){return d(e).map((({props:{value:e,label:t,attributes:r,default:n}})=>({value:e,label:t,attributes:r,default:n})))}(r);return function(e){const t=(0,c.XI)(e,((e,t)=>e.value===t.value));if(t.length>0)throw new Error(`Docusaurus error: Duplicate values "${t.map((e=>e.value)).join(", ")}" found in <Tabs>. Every value needs to be unique.`)}(e),e}),[t,r])}function h({value:e,tabValues:t}){return t.some((t=>t.value===e))}function g({queryString:e=!1,groupId:t}){const r=(0,i.W6)(),s=function({queryString:e=!1,groupId:t}){if("string"==typeof e)return e;if(!1===e)return null;if(!0===e&&!t)throw new Error('Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return t??null}({queryString:e,groupId:t});return[(0,l.aZ)(s),(0,n.useCallback)((e=>{if(!s)return;const t=new URLSearchParams(r.location.search);t.set(s,e),r.replace({...r.location,search:t.toString()})}),[s,r])]}function m(e){const{defaultValue:t,queryString:r=!1,groupId:s}
1=e,a=p(e),[i,l]=(0,n.useState)((()=>function({defaultValue:e,tabValues:t}){if(0===t.length)throw new Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(e){if(!h({value:e,tabValues:t}))throw new Error(`Docusaurus error: The <Tabs> has a defaultValue "${e}" but none of its children has the corresponding value. Available values are: ${t.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return e}const r=t.find((e=>e.default))??t[0];if(!r)throw new Error("Unexpected error: 0 tabValues");return r.value}({defaultValue:t,tabValues:a}))),[c,d]=g({queryString:r,groupId:s}),[m,f]=function({groupId:e}){const t=function(e){return e?`docusaurus.tab.${e}`:null}(e),[r,s]=(0,u.Dv)(t);return[r,(0,n.useCallback)((e=>{t&&s.set(e)}),[t,s])]}({groupId:s}),b=(()=>{const e=c??m;return h({value:e,tabValues:a})?e:null})();(0,o.A)((()=>{b&&l(b)}),[b]);return{selectedValue:i,selectValue:(0,n.useCallback)((e=>{if(!h({value:e,tabValues:a}))throw new Error(`Can't select invalid tab value=${e}`);l(e),d(e),f(e)}),[d,f,a]),tabValues:a}}var f=r(1062);const b={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var y=r(4848);function x({className:e,block:t,selectedValue:r,selectValue:n,tabValues:i}){const o=[],{blockElementScrollPositionUntilNextRender:l}=(0,a.a_)(),c=e=>{const t=e.currentTarget,s=o.indexOf(t),a=i[s].value;a!==r&&(l(t),n(a))},u=e=>{let t=null;switch(e.key){case"Enter":c(e);break;case"ArrowRight":{const r=o.indexOf(e.currentTarget)+1;t=o[r]??o[0];break}case"ArrowLeft":{const r=o.indexOf(e.currentTarget)-1;t=o[r]??o[o.length-1];break}}t?.focus()};return(0,y.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,s.A)("tabs",{"tabs--block":t},e),children:i.map((({value:e,label:t,attributes:n})=>(0,y.jsx)("li",{role:"tab",tabIndex:r===e?0:-1,"aria-selected":r===e,ref:e=>{o.push(e)},onKeyDown:u,onClick:c,...n,className:(0,s.A)("tabs__item",b.tabItem,n?.className,{"tabs__item--active":r===e}),children:t??e},e)))})}function j({lazy:e,children:t,selectedValue:r}){const a=(Array.isArray(t)?t:[t]).filter(Boolean);if(e){const e=a.find((e=>e.props.value===r));return e?(0,n.cloneElement)(e,{className:(0,s.A)("margin-top--md",e.props.className)}):null}return(0,y.jsx)("div",{className:"margin-top--md",children:a.map(((e,t)=>(0,n.cloneElement)(e,{key:t,hidden:e.props.value!==r})))})}function v(e){const t=m(e);return(0,y.jsxs)("div",{className:(0,s.A)("tabs-container",b.tabList),children:[(0,y.jsx)(x,{...t,...e}),(0,y.jsx)(j,{...t,...e})]})}function R(e){const t=(0,f.A)();return(0,y.jsx)(v,{...e,children:d(e.children)},String(t))}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.