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) 16.3 kB
import{assertNonNullish as mt,nonNullish as O,notEmptyString as Rt}from"@dfinity/utils";var ye="icrc21_call_consent_message",j="icrc25_request_permissions",F="icrc25_permissions",$="icrc25_supported_standards",q="icrc27_accounts",N="icrc29_status",U="icrc49_call_canister",H="granted",Ie="denied",Se="ask_on_use",fe="ICRC-21",he="ICRC-25",ge="ICRC-27",Ee="ICRC-29",Ce="ICRC-49";import*as V from"zod";var It=V.enum([ye,j,F,$,q,N,U]),K=It.extract([q,U]),Te=V.enum([H,Ie,Se]),se=V.enum([fe,he,ge,Ee,Ce]);var _e=5e3,Q=120*1e3,ne=5e3,Pe=Q,we=ne,Ae=Q,Oe=ne,xe=Q,qe=Q,Ne=ne,Ue={scopes:Object.values(K.enum).map(e=>({method:e}))};var M="toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=yes, resizable=no, copyhistory=no",Me={width:576,height:625},be={...Me,position:"top-right",features:M},ur={...Me,position:"center",features:M};import*as c from"zod";var S="2.0",St=c.literal(S),b=c.union([c.string(),c.number(),c.null()]),ze=c.object({jsonrpc:St,id:c.optional(b)}),ft=ze.extend({id:b}).merge(c.object({method:c.string(),params:c.optional(c.any())})).strict();var lr=ft.omit({id:!0}).strict(),ve=(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))(ve||{}),ht=c.union([c.number(),c.nativeEnum(ve)]),Le=c.object({code:ht,message:c.string(),data:c.optional(c.never())}),gt=ze.extend({id:b}),oe=gt.extend({error:Le}).strict(),C=e=>oe.omit({error:!0}).merge(c.object({result:e,error:Le}).partial()).strict().refine(({result:t,error:r})=>t!==void 0||r!==void 0,"Either result or error should be provided."),ie=C(c.any());var Ct=async e=>{await new Promise(t=>{setTimeout(t,e)})},ce=async({retries:e,isReady:t,fn:r,intervalInMilliseconds:o=500})=>{let n=t();if(n!=="pending")return n;let s=e-1;return s===0?"timeout":(r(),await Ct(o),await ce({retries:s,intervalInMilliseconds:o,isReady:t,fn:r}))};var De=async({popup:e,id:t,isReady:r,timeoutInMilliseconds:o,intervalInMilliseconds:n})=>{let s=()=>{z({popup:e,msg:{jsonrpc:S,id:t,method:N},origin:"*"})};return await ce({retries:o/(n??500),intervalInMilliseconds:n,isReady:r,fn:s})},We=({id:e,...t})=>{z({msg:{jsonrpc:S,id:e,method:N},...t})},Ye=({id:e,...t})=>{z({msg:{jsonrpc:S,id:e,method:$},...t})},Be=({id:e,...t})=>{z({msg:{jsonrpc:S,id:e,method:F},...t})},Ge=({id:e,params:t,...r})=>{ae({msg:{jsonrpc:S,id:e,method:j,params:t},...r})},ke=({id:e,...t})=>{ae({msg:{jsonrpc:S,id:e,method:q},...t})},je=({id:e,params:t,...r})=>{ae({msg:{jsonrpc:S,id:e,method:U,params:t},...r})},ae=({popup:e,...t})=>{e.focus(),z({popup:e,...t})},z=({popup:e,msg:t,origin:r})=>{e.postMessage(t,r)};import{UrlSchema as At}from"@dfinity/zod-schemas";import*as d from"zod";import*as Fe from"zod";var v=Fe.string().refine(e=>{try{return btoa(atob(e))===e}catch{return!1}},{message:"Invalid base64 string"});import{base64ToUint8Array as Tt}from"@dfinity/utils";import{PrincipalTextSchema as _t}from"@dfinity/zod-schemas";import*as J from"zod";var Pt=v.refine(e=>{try{return Tt(e).length===32}catch{return!1}},{message:"Subaccount must be exactly 32 bytes long."}),wt=J.object({owner:_t,subaccount:Pt.optional()}).strict(),$e=J.array(wt).min(1);var Ot=d.object({method:K}),xt=d.object({scope:Ot,state:Te}).strict(),qt=d.array(xt),Nt=d.object({scopes:qt}).strict(),He=C(Nt),Ut=/^https:\/\/github\.com\/dfinity\/ICRC\/blob\/main\/ICRCs\/ICRC-\d+\/ICRC-\d+\.md$/,Mt=d.url().regex(Ut).refine(e=>{try{At.parse(e)}catch{return!1}let t=/(ICRC-\d+)\.md/g.exec(e);if(t===null)return!1;let[r,o]=t;return Object.keys(se.enum).includes(o)},{message:"The URL does not match any of the IcrcStandard values."}),bt=d.array(d.object({name:se,url:Mt}).strict()).min(1),Ve=C(d.object({supportedStandards:bt})),pe=C(d.literal("ready")),Ke=C(d.object({accounts:$e})),zt=d.object({contentMap:v,certificate:v}).strict(),Qe=C(zt.strict());var X=class extends Error{code;constructor({message:t,code:r}){super(t),this.code=r}},L=class extends Error{};import{UrlSchema as Je}from"@dfinity/zod-schemas";import*as u from"zod";var vt=u.object({pollingIntervalInMilliseconds:u.number().positive().optional(),timeoutInMilliseconds:u.number().positive().optional()}),Lt=u.object({position:u.enum(["top-right","center"]),width:u.number().positive(),height:u.number().positive(),features:u.string().optional()}),Dt=u.function({output:u.void()}).optional(),Wt=Je.optional(),Xe=u.object({url:Je,windowOptions:u.union([Lt,u.string()]).optional(),connectionOptions:vt.optional(),onDisconnect:Dt,host:Wt});import*as D from"zod";var Ze=D.object({timeoutInMilliseconds:D.number().positive()}),ue=D.object({requestId:b.optional()}).merge(Ze.partial()),br=ue.omit({timeoutInMilliseconds:!0}).merge(Ze);import{assertNonNullish as Jr,base64ToUint8Array as Xr}from"@dfinity/utils";import{AnonymousIdentity as es,Certificate as ts,HttpAgent as rs,lookupResultToBuffer as ss,requestIdOf as ns}from"@icp-sdk/core/agent";import{Principal as rr}from"@icp-sdk/core/principal";var f=class extends Error{constructor(t){super(t),this.name="DecodingError"}},Yt=55799,et=Symbol("CBOR_STOP_CODE"),_=(e=>(e[e.False=20]="False",e[e.True=21]="True",e[e.Null=22]="Null",e[e.Undefined=23]="Undefined",e[e.Break=31]="Break",e))(_||{}),y=(e=>(e[e.UnsignedInteger=0]="UnsignedInteger",e[e.NegativeInteger=1]="NegativeInteger",e[e.ByteString=2]="ByteString",e[e.TextString=3]="TextString",e[e.Array=4]="Array",e[e.Map=5]="Map",e[e.Tag=6]="Tag",e[e.Simple=7]="Simple",e))(y||{});var vr=BigInt("0xffffffffffffffff"),T=(e=>(e[e.Value=23]="Value",e[e.OneByte=24]="OneByte",e[e.TwoBytes=25]="TwoBytes",e[e.FourBytes=26]="FourBytes",e[e.EightBytes=27]="EightBytes",e[e.Indefinite=31]="Indefinite",e))(T||{}),de=!1;function Bt(e){return e==null}var Gt=new TextDecoder;function kt(e){return(e&224)>>5}function jt(e){return e&31}var Y=new Uint8Array,W,I=0;function tt(e,t){Y=e,I=0;let r=P(t);return t?.(r)??r}function P(e){let[t,r]=Z();switch(t){case y.UnsignedInteger:return A(r);case y.NegativeInteger:return Vt(r);case y.ByteString:return rt(r);case y.TextString:return le(r);case y.Array:return Ft(r,e);case y.Map:return Ht(r,e);case y.Tag:return Kt(r,e);case y.Simple:return $t(r)}throw new f(`Unsupported major type: ${t}`)}function Z(){let e=Y.at(I);if(Bt(e))throw new f("Provided CBOR data is empty");let t=kt(e),r=jt(e);return I++,[t,r]}function Ft(e,t){let r=A(e);if(r===1/0){let n=[],s=P(t);for(;s!==et;)n.push(t?.(s)??s),s=P(t);return n}let o=new Array(r);for(let n=0;n<r;n++){let s=P(t);o[n]=t?.(s)??s}return o}function $t(e){switch(e){case _.False:return!1;case _.True:return!0;case _.Null:return null;case _.Undefined:return;case _.Break:return et}throw new f(`Unrecognized simple type: ${e.toString(2)}`)}function Ht(e,t){let r=A(e),o={};if(r===1/0){let[n,s]=Z();for(;n!==y.Simple&&s!==_.Break;){let i=le(s),a=P(t);o[i]=t?.(a,i)??a,[n,s]=Z()}return o}for(let n=0;n<r;n++){let[s,i]=Z();if(s!==y.TextString)throw new f("Map keys must be text strings");let a=le(i),p=P(t);o[a]=t?.(p,a)??p}return o}function A(e){if(e<=T.Value)return e;switch(W=new DataView(Y.buffer,Y.byteOffset+I),e){case T.OneByte:return I++,W.getUint8(0);case T.TwoBytes:return I+=2,W.getUint16(0,de);case T.FourBytes:return I+=4,W.getUint32(0,de);case T.EightBytes:return I+=8,W.getBigUint64(0,de);case T.Indefinite:return 1/0;default:throw new f(`Unsupported integer info: ${e.toString(2)}`)}}function Vt(e){let t=A(e);return typeof t=="number"?-1-t:-1n-t}function rt(e){let t=A(e);if(t>Number.MAX_SAFE_INTEGER)throw new f("Byte length is too large");let r=Number(t);return I+=r,Y.slice(I-r,I)}function le(e){let t=rt(e);return Gt.decode(t)}function Kt(e,t){let r=A(e);if(r===Yt)return P(t);throw new f(`Unsupported tag: ${r}.`)}var Qt=2*1024;var Lr=new TextEncoder;var Jt=new Uint8Array(Qt),Dr=new DataView(Jt.buffer);import{base64ToUint8Array as er}from"@dfinity/utils";import{Principal as nt}from"@icp-sdk/core/principal";import{Expiry as Xt,JSON_KEY_EXPIRY as Zt}from"@icp-sdk/core/agent";var st=e=>{let t=JSON.stringify({[Zt]:e.toString()});return Xt.fromJSON(t)};var ot=e=>{let{ingress_expiry:t,canister_id:r,sender:o,...n}=tt(er(e));return{...n,canister_id:nt.fromUint8Array(r),sender:nt.fromUint8Array(o),ingress_expiry:st(t)}};import{base64ToUint8Array as tr}from"@dfinity/utils";import{Principal as it}from"@icp-sdk/core/principal";var ct=({requestMethod:e,responseMethod:t})=>{if(t!==e)throw new Error("The response method does not match the request method.")},at=({responseArg:e,requestArg:t})=>{let r=tr(t);if(!(({first:s,second:i})=>s.length===i.length&&s.every((a,p)=>a===i[p]))({first:r,second:e}))throw new Error("The response does not contain the request arguments.")},pt=({requestCanisterId:e,responseCanisterId:t})=>{if(e.toText()!==t.toText())throw new Error("The response canister ID does not match the requested canister ID.")},ut=({requestSender:e,responseSender:t})=>{if((t instanceof Uint8Array?it.fromUint8Array(t):t).toText()!==it.fromText(e).toText())throw new Error("The response sender does not match the request sender.")};var dt=({params:{method:e,arg:t,canisterId:r,sender:o},result:{contentMap:n}})=>{let s=ot(n);pt({requestCanisterId:rr.fromText(r),responseCanisterId:s.canister_id}),ct({requestMethod:e,responseMethod:s.method_name}),at({requestArg:t,responseArg:s.arg}),ut({requestSender:o,responseSender:s.sender})};import{isNullish as ee}from"@dfinity/utils";var me=()=>typeof window<"u";var lt=({position:e,...t})=>(e==="center"?sr:nr)(t),sr=({width:e,height:t,features:r=M})=>{if(!me()||ee(window)||ee(window.top))return;let{top:{innerWidth:o,innerHeight:n}}=window,s=n/2+screenY-t/2,i=o/2+screenX-e/2;return`${r}, width=${e}, height=${t}, top=${s}, left=${i}`},nr=({width:e,height:t,features:r=M})=>{if(!me()||ee(window)||ee(window.top))return;let{top:{innerWidth:o,innerHeight:n}}=window,s=outerHeight-n,i=o-e;return`${r}, width=${e}, height=${t}, top=${s}, left=${i}`};var yt=class e{#t;#e;#s;host;#r="connected";#n;constructor({origin:t,popup:r,onDisconnect:o,host:n}){this.#t=t,this.#e=r,this.#s=o,this.host=n,this.#r="connected",this.#n=setInterval(this.checkWalletStatusCallback,_e)}static async connect({onDisconnect:t,...r}){return await this.connectSigner({options:r,init:o=>new e({...o,onDisconnect:t})})}static async connectSigner({options:t,init:r}){let{success:o,error:n}=Xe.safeParse(t);if(!o)throw new Error(`Options cannot be parsed: ${n?.message??""}`);let{url:s,windowOptions:i,connectionOptions:a}=t,p=typeof i=="string"?i:lt(i??be),R=window.open(s,"relyingPartyWindow",p);mt(R,"Unable to open the signer window.");let g=()=>{R.close()};class h extends Error{}let l,B=({origin:m,data:w})=>{let{success:te}=ie.safeParse(w);if(!te)return;let k;try{let{origin:x}=new URL(s);k=x}catch{l=new h(`The origin ${m} of the signer URL ${s} cannot be parsed.`);return}if(Rt(m)&&m!==k){l=new h(`The response origin ${m} does not match the requested signer URL ${s}.`);return}let{success:re}=pe.safeParse(w);re&&(l=r({origin:m,popup:R}))};window.addEventListener("message",B);let G=()=>{window.removeEventListener("message",B)},E=async()=>{if(await De({popup:R,isReady:()=>O(l)?l instanceof e?"ready":"error":"pending",id:crypto.randomUUID(),timeoutInMilliseconds:a?.timeoutInMilliseconds??Pe,intervalInMilliseconds:a?.pollingIntervalInMilliseconds})==="timeout")throw new Error("Connection timeout. Unable to connect to the signer.");if(mt(l,"Unexpected error. The request status succeeded, but the signer response is not defined."),l instanceof h)throw l;return l};try{return await E()}catch(m){throw g(),m}finally{G()}}disconnect=async()=>{clearInterval(this.#n),this.#e.close(),this.#s?.()};checkWalletStatusCallback=()=>{this.checkWalletStatus()};async checkWalletStatus(){let t=({data:n,id:s})=>{let{success:i,data:a}=pe.safeParse(n);return i&&s===a?.id?{handled:!0,result:"connected"}:{handled:!0,result:"disconnected"}},r=n=>{We({popup:this.#e,origin:this.#t,id:n})},o=async()=>{try{return await this.request({options:{timeoutInMilliseconds:Ne},postRequest:r,handleMessage:t})}catch{return"disconnected"}};this.#r=await o(),this.#r!=="connected"&&await this.disconnect()}request=async({options:t,postRequest:r,handleMessage:o})=>await new Promise((n,s)=>{let{connected:i,err:a}=this.assertWalletConnected();if(!i){s(a??new Error("Unexpected reason for disconnection."));return}let{success:p,error:R}=ue.safeParse(t);if(!p)throw new Error(`Wallet request options cannot be parsed: ${R?.message??""}`);let{requestId:g,timeoutInMilliseconds:h}=t,l=g??crypto.randomUUID(),B=setTimeout(()=>{s(new Error(`Request to signer timed out after ${h} milliseconds.`)),E()},h),G=({origin:m,data:w,source:te})=>{let{success:k}=ie.safeParse(w);if(!k)return;if(te!==this.#e){s(new Error("The response is not originating from the window that was opened.")),E();return}if(Rt(m)&&m!==this.#t){s(new Error(`The response origin ${m} does not match the signer origin ${this.#t}.`)),E();return}let{handled:re,result:x}=o({data:w,id:l});if(re&&O(x)){n(x),E();return}let Re=this.handleErrorMessage({data:w,id:l});Re.valid||(s(Re.error),E())};window.addEventListener("message",G);let E=()=>{clearTimeout(B),window.removeEventListener("message",G)};r(l)});assertWalletConnected(){return this.#r==="disconnected"?{connected:!1,err:new L("The signer has been disconnected. Your request cannot be processed.")}:this.#e.closed?{connected:!1,err:new L("The signer has been closed. Your request cannot be processed.")}:{connected:!0}}supportedStandards=async({options:{timeoutInMilliseconds:t,...r}={}}={})=>{let o=({data:s,id:i})=>{let{success:a,data:p}=Ve.safeParse(s);if(a&&i===p?.id&&O(p?.result)){let{result:{supportedStandards:R}}=p;return{handled:!0,result:R}}return{handled:!1}},n=s=>{Ye({popup:this.#e,origin:this.#t,id:s})};return await this.request({options:{timeoutInMilliseconds:t??we,...r},postRequest:n,handleMessage:o})};permissions=async({options:{timeoutInMilliseconds:t,...r}={}}={})=>{let o=n=>{Be({popup:this.#e,origin:this.#t,id:n})};return await this.requestPermissionsScopes({options:{timeoutInMilliseconds:t??Oe,...r},postRequest:o})};requestPermissions=async({options:{timeoutInMilliseconds:t,...r}={},params:o}={})=>{let n=s=>{Ge({popup:this.#e,origin:this.#t,id:s,params:o??Ue})};return await this.requestPermissionsScopes({options:{timeoutInMilliseconds:t??Ae,...r},postRequest:n})};requestPermissionsScopes=async({options:t,postRequest:r})=>{let o=({data:n,id:s})=>{let{success:i,data:a}=He.safeParse(n);if(i&&s===a?.id&&O(a?.result)){let{result:{scopes:p}}=a;return{handled:!0,result:p}}return{handled:!1}};return await this.request({options:t,postRequest:r,handleMessage:o})};accounts=async({options:{timeoutInMilliseconds:t,...r}={}}={})=>{let o=({data:s,id:i})=>{let{success:a,data:p}=Ke.safeParse(s);if(a&&i===p?.id&&O(p?.result)){let{result:{accounts:R}}=p;return{handled:!0,result:R}}return{handled:!1}},n=s=>{ke({popup:this.#e,origin:this.#t,id:s})};return await this.request({options:{timeoutInMilliseconds:t??xe,...r},postRequest:n,handleMessage:o})};call=async({options:{timeoutInMilliseconds:t,...r}={},params:o})=>{let n=({data:a,id:p})=>{let{success:R,data:g}=Qe.safeParse(a);if(R&&p===g?.id&&O(g?.result)){let{result:h}=g;return{handled:!0,result:h}}return{handled:!1}},s=a=>{je({popup:this.#e,origin:this.#t,id:a,params:o})},i=await this.request({options:{timeoutInMilliseconds:t??qe,...r},postRequest:s,handleMessage:n});return dt({params:o,result:i}),i};handleErrorMessage=({data:t,id:r})=>{let{success:o,data:n}=oe.safeParse(t);return!o||r!==n?.id?{valid:!0}:{valid:!1,error:new X(n.error)}};requestPermissionsNotGranted=async({options:t}={})=>{let r=a=>a.filter(({state:p})=>p!==H).map(({scope:p})=>p),o=await this.permissions({options:t});if(o.length===0)throw new Error("The signer did not provide any data about the current set of permissions.");let n=r(o);if(n.length===0)return{allPermissionsGranted:!0};let s=await this.requestPermissions({options:t,params:{scopes:n}});if(s.length===0)throw new Error("The signer did not provide any data about the current set of permissions following the request.");return{allPermissionsGranted:r(s).length===0}}};export{yt as RelyingParty}; //# sourceMappingURL=relying-party.js.map