UNPKG

@dfinity/oisy-wallet-signer

Version:

A library designed to facilitate communication between a dApp and the OISY Wallet on the Internet Computer.

3 lines (2 loc) 13.4 kB
import{assertNonNullish as Ze,nonNullish as g,notEmptyString as Xe}from"@dfinity/utils";var se="icrc21_call_consent_message",U="icrc25_request_permissions",L="icrc25_permissions",v="icrc25_supported_standards",_="icrc27_accounts",T="icrc29_status",P="icrc49_call_canister",D="granted",oe="denied",ne="ask_on_use",ie="ICRC-21",ce="ICRC-25",ae="ICRC-27",pe="ICRC-29",de="ICRC-49";import*as W from"zod/v4";var tt=W.enum([se,U,L,v,_,T,P]),G=tt.extract([_,P]),ue=W.enum([D,oe,ne]),B=W.enum([ie,ce,ae,pe,de]);var le=5e3,Y=60*2*1e3,Q=5e3,me=Y,Re=Q,Ie=Y,ye=Q,Se=Y,he=Y,fe=Q,Ce={scopes:Object.values(G.enum).map(t=>({method:t}))};var A="toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=yes, resizable=no, copyhistory=no",ge={width:576,height:625},Ee={...ge,position:"top-right",features:A},Dt={...ge,position:"center",features:A};import*as c from"zod/v4";var I="2.0",rt=c.literal(I),O=c.union([c.string(),c.number(),c.null()]),_e=c.object({jsonrpc:rt,id:c.optional(O)}),st=_e.extend({id:O}).merge(c.object({method:c.string(),params:c.optional(c.any())})).strict();var Gt=st.omit({id:!0}).strict(),Te=(i=>(i[i.PARSE_ERROR=-32700]="PARSE_ERROR",i[i.INVALID_REQUEST=-32600]="INVALID_REQUEST",i[i.METHOD_NOT_FOUND=-32601]="METHOD_NOT_FOUND",i[i.INVALID_PARAMS=-32602]="INVALID_PARAMS",i[i.INTERNAL_ERROR=-32603]="INTERNAL_ERROR",i[i.SERVER_ERROR=-32e3]="SERVER_ERROR",i))(Te||{}),ot=c.union([c.number(),c.nativeEnum(Te)]),Pe=c.object({code:ot,message:c.string(),data:c.optional(c.never())}),nt=_e.extend({id:O}),V=nt.extend({error:Pe}).strict(),f=t=>V.omit({error:!0}).merge(c.object({result:t,error:Pe}).partial()).strict().refine(({result:e,error:r})=>e!==void 0||r!==void 0,"Either result or error should be provided."),K=f(c.any());var ct=async t=>{await new Promise(e=>{setTimeout(e,t)})},J=async({retries:t,isReady:e,fn:r,intervalInMilliseconds:s=500})=>{let n=e();if(n!=="pending")return n;let o=t-1;return o===0?"timeout":(r(),await ct(s),await J({retries:o,intervalInMilliseconds:s,isReady:e,fn:r}))};var Ae=async({popup:t,id:e,isReady:r,timeoutInMilliseconds:s,intervalInMilliseconds:n})=>{let o=()=>{x({popup:t,msg:{jsonrpc:I,id:e,method:T},origin:"*"})};return await J({retries:s/(n??500),intervalInMilliseconds:n,isReady:r,fn:o})},Oe=({id:t,...e})=>{x({msg:{jsonrpc:I,id:t,method:T},...e})},xe=({id:t,...e})=>{x({msg:{jsonrpc:I,id:t,method:v},...e})},qe=({id:t,...e})=>{x({msg:{jsonrpc:I,id:t,method:L},...e})},we=({id:t,params:e,...r})=>{Z({msg:{jsonrpc:I,id:t,method:U,params:e},...r})},Ne=({id:t,...e})=>{Z({msg:{jsonrpc:I,id:t,method:_},...e})},Me=({id:t,params:e,...r})=>{Z({msg:{jsonrpc:I,id:t,method:P,params:e},...r})},Z=({popup:t,...e})=>{t.focus(),x({popup:t,...e})},x=({popup:t,msg:e,origin:r})=>{t.postMessage(e,r)};import{UrlSchema as lt}from"@dfinity/zod-schemas";import*as u from"zod/v4";import*as ze from"zod/v4";var q=ze.string().refine(t=>{try{return btoa(atob(t))===t}catch{return!1}},{message:"Invalid base64 string"});import{base64ToUint8Array as at}from"@dfinity/utils";import{PrincipalTextSchema as pt}from"@dfinity/zod-schemas";import*as k from"zod/v4";var dt=q.refine(t=>{try{return at(t).length===32}catch{return!1}},{message:"Subaccount must be exactly 32 bytes long."}),ut=k.object({owner:pt,subaccount:dt.optional()}).strict(),be=k.array(ut).min(1);var mt=u.object({method:G}),Rt=u.object({scope:mt,state:ue}).strict(),It=u.array(Rt),yt=u.object({scopes:It}).strict(),Ue=f(yt),St=/^https:\/\/github\.com\/dfinity\/ICRC\/blob\/main\/ICRCs\/ICRC-\d+\/ICRC-\d+\.md$/,ht=u.url().regex(St).refine(t=>{try{lt.parse(t)}catch{return!1}let e=/(ICRC-\d+)\.md/g.exec(t);if(e===null)return!1;let[r,s]=e;return Object.keys(B.enum).includes(s)},{message:"The URL does not match any of the IcrcStandard values."}),ft=u.array(u.object({name:B,url:ht}).strict()).min(1),Le=f(u.object({supportedStandards:ft})),X=f(u.literal("ready")),ve=f(u.object({accounts:be})),Ct=u.object({contentMap:q,certificate:q}).strict(),De=f(Ct.strict());var j=class extends Error{code;constructor({message:e,code:r}){super(e),this.code=r}},w=class extends Error{};import{UrlSchema as Ye}from"@dfinity/zod-schemas";import*as d from"zod/v4";import*as We from"zod/v4";var Ge=t=>We.custom(e=>t.implement(e));var gt=d.object({pollingIntervalInMilliseconds:d.number().positive().optional(),timeoutInMilliseconds:d.number().positive().optional()}),Et=d.object({position:d.enum(["top-right","center"]),width:d.number().positive(),height:d.number().positive(),features:d.string().optional()}),_t=Ge(d.function({output:d.void()})).optional(),Tt=Ye.optional(),ke=d.object({url:Ye,windowOptions:d.union([Et,d.string()]).optional(),connectionOptions:gt.optional(),onDisconnect:_t,host:Tt});import*as N from"zod/v4";var je=N.object({timeoutInMilliseconds:N.number().positive()}),ee=N.object({requestId:O.optional()}).merge(je.partial()),dr=ee.omit({timeoutInMilliseconds:!0}).merge(je);import{AnonymousIdentity as Cr,Certificate as gr,HttpAgent as Er,lookupResultToBuffer as _r,requestIdOf as Tr,uint8ToBuf as Pr}from"@dfinity/agent";import{Principal as wt}from"@dfinity/principal";import{assertNonNullish as xr,base64ToUint8Array as qr}from"@dfinity/utils";import{decode as Pt}from"@dfinity/cbor";import{Principal as At}from"@dfinity/principal";import{base64ToUint8Array as Ot}from"@dfinity/utils";var Fe=t=>{let{ingress_expiry:e,canister_id:r,...s}=Pt(Ot(t));return{...s,canister_id:At.fromUint8Array(r),ingress_expiry:e}};import{Principal as $e}from"@dfinity/principal";import{arrayBufferToUint8Array as xt,base64ToUint8Array as qt}from"@dfinity/utils";var He=({requestMethod:t,responseMethod:e})=>{if(e!==t)throw new Error("The response method does not match the request method.")},Be=({responseArg:t,requestArg:e})=>{let r=qt(e),s=xt(t);if(!(({first:o,second:i})=>o.length===i.length&&o.every((a,p)=>a===i[p]))({first:r,second:s}))throw new Error("The response does not contain the request arguments.")},Qe=({requestCanisterId:t,responseCanisterId:e})=>{if(t.toText()!==e.toText())throw new Error("The response canister ID does not match the requested canister ID.")},Ve=({requestSender:t,responseSender:e})=>{if((e instanceof Uint8Array?$e.fromUint8Array(e):e).toText()!==$e.fromText(t).toText())throw new Error("The response sender does not match the request sender.")};var Ke=({params:{method:t,arg:e,canisterId:r,sender:s},result:{contentMap:n}})=>{let o=Fe(n);Qe({requestCanisterId:wt.fromText(r),responseCanisterId:o.canister_id}),He({requestMethod:t,responseMethod:o.method_name}),Be({requestArg:e,responseArg:o.arg}),Ve({requestSender:s,responseSender:o.sender})};import{isNullish as F}from"@dfinity/utils";var te=()=>typeof window<"u";var Je=({position:t,...e})=>(t==="center"?Nt:Mt)(e),Nt=({width:t,height:e,features:r=A})=>{if(!te()||F(window)||F(window.top))return;let{top:{innerWidth:s,innerHeight:n}}=window,o=n/2+screenY-e/2,i=s/2+screenX-t/2;return`${r}, width=${t}, height=${e}, top=${o}, left=${i}`},Mt=({width:t,height:e,features:r=A})=>{if(!te()||F(window)||F(window.top))return;let{top:{innerWidth:s,innerHeight:n}}=window,o=outerHeight-n,i=s-t;return`${r}, width=${t}, height=${e}, top=${o}, left=${i}`};var et=class t{#t;#e;#s;host;#r="connected";#o;constructor({origin:e,popup:r,onDisconnect:s,host:n}){this.#t=e,this.#e=r,this.#s=s,this.host=n,this.#r="connected",this.#o=setInterval(this.checkWalletStatusCallback,le)}static async connect({onDisconnect:e,...r}){return await this.connectSigner({options:r,init:s=>new t({...s,onDisconnect:e})})}static async connectSigner({options:e,init:r}){let{success:s,error:n}=ke.safeParse(e);if(!s)throw new Error(`Options cannot be parsed: ${n?.message??""}`);let{url:o,windowOptions:i,connectionOptions:a}=e,p=typeof i=="string"?i:Je(i??Ee),R=window.open(o,"relyingPartyWindow",p);Ze(R,"Unable to open the signer window.");let S=()=>{R.close()};class y extends Error{}let l,M=({origin:m,data:C})=>{let{success:$}=K.safeParse(C);if(!$)return;let b;try{let{origin:E}=new URL(o);b=E}catch{l=new y(`The origin ${m} of the signer URL ${o} cannot be parsed.`);return}if(Xe(m)&&m!==b){l=new y(`The response origin ${m} does not match the requested signer URL ${o}.`);return}let{success:H}=X.safeParse(C);H&&(l=r({origin:m,popup:R}))};window.addEventListener("message",M);let z=()=>{window.removeEventListener("message",M)},h=async()=>{if(await Ae({popup:R,isReady:()=>g(l)?l instanceof t?"ready":"error":"pending",id:crypto.randomUUID(),timeoutInMilliseconds:a?.timeoutInMilliseconds??me,intervalInMilliseconds:a?.pollingIntervalInMilliseconds})==="timeout")throw new Error("Connection timeout. Unable to connect to the signer.");if(Ze(l,"Unexpected error. The request status succeeded, but the signer response is not defined."),l instanceof y)throw l;return l};try{return await h()}catch(m){throw S(),m}finally{z()}}disconnect=async()=>{clearInterval(this.#o),this.#e.close(),this.#s?.()};checkWalletStatusCallback=()=>{this.checkWalletStatus()};async checkWalletStatus(){let e=({data:n,id:o})=>{let{success:i,data:a}=X.safeParse(n);return i&&o===a?.id?{handled:!0,result:"connected"}:{handled:!0,result:"disconnected"}},r=n=>{Oe({popup:this.#e,origin:this.#t,id:n})},s=async()=>{try{return await this.request({options:{timeoutInMilliseconds:fe},postRequest:r,handleMessage:e})}catch{return"disconnected"}};this.#r=await s(),this.#r!=="connected"&&await this.disconnect()}request=async({options:e,postRequest:r,handleMessage:s})=>await new Promise((n,o)=>{let{connected:i,err:a}=this.assertWalletConnected();if(!i){o(a??new Error("Unexpected reason for disconnection."));return}let{success:p,error:R}=ee.safeParse(e);if(!p)throw new Error(`Wallet request options cannot be parsed: ${R?.message??""}`);let{requestId:S,timeoutInMilliseconds:y}=e,l=S??crypto.randomUUID(),M=setTimeout(()=>{o(new Error(`Request to signer timed out after ${y} milliseconds.`)),h()},y),z=({origin:m,data:C,source:$})=>{let{success:b}=K.safeParse(C);if(!b)return;if($!==this.#e){o(new Error("The response is not originating from the window that was opened.")),h();return}if(Xe(m)&&m!==this.#t){o(new Error(`The response origin ${m} does not match the signer origin ${this.#t}.`)),h();return}let{handled:H,result:E}=s({data:C,id:l});if(H&&g(E)){n(E),h();return}let re=this.handleErrorMessage({data:C,id:l});re.valid||(o(re.error),h())};window.addEventListener("message",z);let h=()=>{clearTimeout(M),window.removeEventListener("message",z)};r(l)});assertWalletConnected(){return this.#r==="disconnected"?{connected:!1,err:new w("The signer has been disconnected. Your request cannot be processed.")}:this.#e.closed?{connected:!1,err:new w("The signer has been closed. Your request cannot be processed.")}:{connected:!0}}supportedStandards=async({options:{timeoutInMilliseconds:e,...r}={}}={})=>{let s=({data:o,id:i})=>{let{success:a,data:p}=Le.safeParse(o);if(a&&i===p?.id&&g(p?.result)){let{result:{supportedStandards:R}}=p;return{handled:!0,result:R}}return{handled:!1}},n=o=>{xe({popup:this.#e,origin:this.#t,id:o})};return await this.request({options:{timeoutInMilliseconds:e??Re,...r},postRequest:n,handleMessage:s})};permissions=async({options:{timeoutInMilliseconds:e,...r}={}}={})=>{let s=n=>{qe({popup:this.#e,origin:this.#t,id:n})};return await this.requestPermissionsScopes({options:{timeoutInMilliseconds:e??ye,...r},postRequest:s})};requestPermissions=async({options:{timeoutInMilliseconds:e,...r}={},params:s}={})=>{let n=o=>{we({popup:this.#e,origin:this.#t,id:o,params:s??Ce})};return await this.requestPermissionsScopes({options:{timeoutInMilliseconds:e??Ie,...r},postRequest:n})};requestPermissionsScopes=async({options:e,postRequest:r})=>{let s=({data:n,id:o})=>{let{success:i,data:a}=Ue.safeParse(n);if(i&&o===a?.id&&g(a?.result)){let{result:{scopes:p}}=a;return{handled:!0,result:p}}return{handled:!1}};return await this.request({options:e,postRequest:r,handleMessage:s})};accounts=async({options:{timeoutInMilliseconds:e,...r}={}}={})=>{let s=({data:o,id:i})=>{let{success:a,data:p}=ve.safeParse(o);if(a&&i===p?.id&&g(p?.result)){let{result:{accounts:R}}=p;return{handled:!0,result:R}}return{handled:!1}},n=o=>{Ne({popup:this.#e,origin:this.#t,id:o})};return await this.request({options:{timeoutInMilliseconds:e??Se,...r},postRequest:n,handleMessage:s})};call=async({options:{timeoutInMilliseconds:e,...r}={},params:s})=>{let n=({data:a,id:p})=>{let{success:R,data:S}=De.safeParse(a);if(R&&p===S?.id&&g(S?.result)){let{result:y}=S;return{handled:!0,result:y}}return{handled:!1}},o=a=>{Me({popup:this.#e,origin:this.#t,id:a,params:s})},i=await this.request({options:{timeoutInMilliseconds:e??he,...r},postRequest:o,handleMessage:n});return Ke({params:s,result:i}),i};handleErrorMessage=({data:e,id:r})=>{let{success:s,data:n}=V.safeParse(e);return!s||r!==n?.id?{valid:!0}:{valid:!1,error:new j(n.error)}};requestPermissionsNotGranted=async({options:e}={})=>{let r=a=>a.filter(({state:p})=>p!==D).map(({scope:p})=>p),s=await this.permissions({options:e});if(s.length===0)throw new Error("The signer did not provide any data about the current set of permissions.");let n=r(s);if(n.length===0)return{allPermissionsGranted:!0};let o=await this.requestPermissions({options:e,params:{scopes:n}});if(o.length===0)throw new Error("The signer did not provide any data about the current set of permissions following the request.");return{allPermissionsGranted:r(o).length===0}}};export{et as RelyingParty}; //# sourceMappingURL=relying-party.js.map