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) 14.6 kB
import{assertNonNullish as nt,nonNullish as E,notEmptyString as it}from"@dfinity/utils";var pe="icrc21_call_consent_message",L="icrc25_request_permissions",v="icrc25_permissions",D="icrc25_supported_standards",T="icrc27_accounts",P="icrc29_status",A="icrc49_call_canister",W="granted",de="denied",ue="ask_on_use",le="ICRC-21",me="ICRC-25",Re="ICRC-27",ye="ICRC-29",Ie="ICRC-49";import*as G from"zod";var at=G.enum([pe,L,v,D,T,P,A]),Y=at.extract([T,A]),Se=G.enum([W,de,ue]),V=G.enum([le,me,Re,ye,Ie]);var he=5e3,B=60*2*1e3,Q=5e3,fe=B,ge=Q,Ce=B,Ee=Q,_e=B,Te=B,Pe=Q,Ae={scopes:Object.values(Y.Values).map(t=>({method:t}))};var x="toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=yes, resizable=no, copyhistory=no",xe={width:576,height:625},Oe={...xe,position:"top-right",features:x},Vt={...xe,position:"center",features:x};import*as c from"zod";var y="2.0",pt=c.literal(y),O=c.union([c.string(),c.number(),c.null()]),we=c.object({jsonrpc:pt,id:c.optional(O)}),dt=we.extend({id:O}).merge(c.object({method:c.string(),params:c.optional(c.any())})).strict();var Kt=dt.omit({id:!0}).strict(),qe=(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))(qe||{}),ut=c.union([c.number(),c.nativeEnum(qe)]),be=c.object({code:ut,message:c.string(),data:c.optional(c.never())}),lt=we.extend({id:O}),K=lt.extend({error:be}).strict(),f=t=>K.omit({error:!0}).merge(c.object({result:t,error:be}).partial()).strict().refine(({result:e,error:r})=>e!==void 0||r!==void 0,"Either result or error should be provided."),J=f(c.any());var Rt=async t=>{await new Promise(e=>{setTimeout(e,t)})},Z=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 Rt(s),await Z({retries:o,intervalInMilliseconds:s,isReady:e,fn:r}))};var Ne=async({popup:t,id:e,isReady:r,timeoutInMilliseconds:s,intervalInMilliseconds:n})=>{let o=()=>{w({popup:t,msg:{jsonrpc:y,id:e,method:P},origin:"*"})};return await Z({retries:s/(n??500),intervalInMilliseconds:n,isReady:r,fn:o})},Me=({id:t,...e})=>{w({msg:{jsonrpc:y,id:t,method:P},...e})},ze=({id:t,...e})=>{w({msg:{jsonrpc:y,id:t,method:D},...e})},Ue=({id:t,...e})=>{w({msg:{jsonrpc:y,id:t,method:v},...e})},Le=({id:t,params:e,...r})=>{X({msg:{jsonrpc:y,id:t,method:L,params:e},...r})},ve=({id:t,...e})=>{X({msg:{jsonrpc:y,id:t,method:T},...e})},De=({id:t,params:e,...r})=>{X({msg:{jsonrpc:y,id:t,method:A,params:e},...r})},X=({popup:t,...e})=>{t.focus(),w({popup:t,...e})},w=({popup:t,msg:e,origin:r})=>{t.postMessage(e,r)};import{UrlSchema as ft}from"@dfinity/zod-schemas";import*as u from"zod";import*as We from"zod";var q=We.string().refine(t=>{try{return btoa(atob(t))===t}catch{return!1}},{message:"Invalid base64 string"});import{base64ToUint8Array as yt}from"@dfinity/utils";import{PrincipalTextSchema as It}from"@dfinity/zod-schemas";import*as k from"zod";var St=q.refine(t=>{try{return yt(t).length===32}catch{return!1}},{message:"Subaccount must be exactly 32 bytes long."}),ht=k.object({owner:It,subaccount:St.optional()}).strict(),Ge=k.array(ht).min(1);var gt=u.object({method:Y}),Ct=u.object({scope:gt,state:Se}).strict(),Et=u.array(Ct),_t=u.object({scopes:Et}).strict(),Ye=f(_t),Tt=/^https:\/\/github\.com\/dfinity\/ICRC\/blob\/main\/ICRCs\/ICRC-\d+\/ICRC-\d+\.md$/,Pt=u.string().url().regex(Tt).refine(t=>{try{ft.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(V.Values).includes(s)},{message:"The URL does not match any of the IcrcStandard values."}),At=u.array(u.object({name:V,url:Pt}).strict()).min(1),Be=f(u.object({supportedStandards:At})),ee=f(u.literal("ready")),ke=f(u.object({accounts:Ge})),xt=u.object({contentMap:q,certificate:q}).strict(),je=f(xt.strict());var j=class extends Error{code;constructor({message:e,code:r}){super(e),this.code=r}},b=class extends Error{};import{UrlSchema as Fe}from"@dfinity/zod-schemas";import*as d from"zod";var Ot=d.object({pollingIntervalInMilliseconds:d.number().positive().optional(),timeoutInMilliseconds:d.number().positive().optional()}),wt=d.object({position:d.enum(["top-right","center"]),width:d.number().positive(),height:d.number().positive(),features:d.string().optional()}),qt=d.function().args().returns(d.void()).optional(),bt=Fe.optional(),$e=d.object({url:Fe,windowOptions:d.union([wt,d.string()]).optional(),connectionOptions:Ot.optional(),onDisconnect:qt,host:bt});import*as N from"zod";var He=N.object({timeoutInMilliseconds:N.number().positive()}),te=N.object({requestId:O.optional()}).merge(He.partial()),hr=te.omit({timeoutInMilliseconds:!0}).merge(He);import{AnonymousIdentity as Mr,Certificate as zr,HttpAgent as Ur,lookupResultToBuffer as Lr,requestIdOf as vr}from"@dfinity/agent";import{Principal as Gt}from"@dfinity/principal";import{assertNonNullish as Gr,base64ToUint8Array as Yr}from"@dfinity/utils";import{Principal as Lt}from"@dfinity/principal";import{base64ToUint8Array as vt}from"@dfinity/utils";import{concat as Nt,fromHex as Ve,toHex as Mt}from"@dfinity/agent";import zt from"borc";import*as g from"simple-cbor";import{SelfDescribeCborSerializer as Ut}from"simple-cbor";var re=class{get name(){return"Principal"}get priority(){return 0}match(e){return e&&e._isPrincipal===!0}encode(e){return g.value.bytes(e.toUint8Array())}},se=class{get name(){return"Buffer"}get priority(){return 1}match(e){return e instanceof ArrayBuffer||ArrayBuffer.isView(e)}encode(e){return g.value.bytes(new Uint8Array(e))}},oe=class{get name(){return"BigInt"}get priority(){return 1}match(e){return typeof e=="bigint"}encode(e){return e>BigInt(0)?g.value.tagged(2,g.value.bytes(Ve(e.toString(16)))):g.value.tagged(3,g.value.bytes(Ve((BigInt("-1")*e).toString(16))))}},ie=Ut.withDefaultEncoders(!0);ie.addEncoder(new re);ie.addEncoder(new se);ie.addEncoder(new oe);function Qe(t){let e=t.byteLength,r=BigInt(0);for(let s=0;s<e;s++)r=r*BigInt(256)+BigInt(t[s]);return r}var ne=class extends zt.Decoder{createByteString(e){return Nt(...e)}createByteStringFromHeap(e,r){return e===r?new ArrayBuffer(0):new Uint8Array(this._heap.slice(e,r))}};function Ke(t){let e=new Uint8Array(t),r=new ne({size:e.byteLength,tags:{2:s=>Qe(s),3:s=>-Qe(s),55799:s=>s}});try{return r.decodeFirst(e)}catch(s){throw new Error(`Failed to decode CBOR: ${s}, input: ${Mt(e)}`)}}var Je=t=>{let{ingress_expiry:e,canister_id:r,...s}=Ke(vt(t));return{...s,canister_id:Lt.fromUint8Array(r),ingress_expiry:BigInt(e.toFixed())}};import{Principal as Ze}from"@dfinity/principal";import{arrayBufferToUint8Array as Dt,base64ToUint8Array as Wt}from"@dfinity/utils";var Xe=({requestMethod:t,responseMethod:e})=>{if(e!==t)throw new Error("The response method does not match the request method.")},et=({responseArg:t,requestArg:e})=>{let r=Wt(e),s=Dt(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.")},tt=({requestCanisterId:t,responseCanisterId:e})=>{if(t.toText()!==e.toText())throw new Error("The response canister ID does not match the requested canister ID.")},rt=({requestSender:t,responseSender:e})=>{if((e instanceof Uint8Array?Ze.fromUint8Array(e):e).toText()!==Ze.fromText(t).toText())throw new Error("The response sender does not match the request sender.")};var st=({params:{method:t,arg:e,canisterId:r,sender:s},result:{contentMap:n}})=>{let o=Je(n);tt({requestCanisterId:Gt.fromText(r),responseCanisterId:o.canister_id}),Xe({requestMethod:t,responseMethod:o.method_name}),et({requestArg:e,responseArg:o.arg}),rt({requestSender:s,responseSender:o.sender})};import{isNullish as F}from"@dfinity/utils";var ce=()=>typeof window<"u";var ot=({position:t,...e})=>(t==="center"?Yt:Bt)(e),Yt=({width:t,height:e,features:r=x})=>{if(!ce()||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}`},Bt=({width:t,height:e,features:r=x})=>{if(!ce()||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 ct=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,he)}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}=$e.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:ot(i??Oe),R=window.open(o,"relyingPartyWindow",p);nt(R,"Unable to open the signer window.");let S=()=>{R.close()};class I extends Error{}let l,M=({origin:m,data:C})=>{let{success:$}=J.safeParse(C);if(!$)return;let U;try{let{origin:_}=new URL(o);U=_}catch{l=new I(`The origin ${m} of the signer URL ${o} cannot be parsed.`);return}if(it(m)&&m!==U){l=new I(`The response origin ${m} does not match the requested signer URL ${o}.`);return}let{success:H}=ee.safeParse(C);H&&(l=r({origin:m,popup:R}))};window.addEventListener("message",M);let z=()=>{window.removeEventListener("message",M)},h=async()=>{if(await Ne({popup:R,isReady:()=>E(l)?l instanceof t?"ready":"error":"pending",id:crypto.randomUUID(),timeoutInMilliseconds:a?.timeoutInMilliseconds??fe,intervalInMilliseconds:a?.pollingIntervalInMilliseconds})==="timeout")throw new Error("Connection timeout. Unable to connect to the signer.");if(nt(l,"Unexpected error. The request status succeeded, but the signer response is not defined."),l instanceof I)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}=ee.safeParse(n);return i&&o===a?.id?{handled:!0,result:"connected"}:{handled:!0,result:"disconnected"}},r=n=>{Me({popup:this.#e,origin:this.#t,id:n})},s=async()=>{try{return await this.request({options:{timeoutInMilliseconds:Pe},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}=te.safeParse(e);if(!p)throw new Error(`Wallet request options cannot be parsed: ${R?.message??""}`);let{requestId:S,timeoutInMilliseconds:I}=e,l=S??crypto.randomUUID(),M=setTimeout(()=>{o(new Error(`Request to signer timed out after ${I} milliseconds.`)),h()},I),z=({origin:m,data:C,source:$})=>{let{success:U}=J.safeParse(C);if(!U)return;if($!==this.#e){o(new Error("The response is not originating from the window that was opened.")),h();return}if(it(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:_}=s({data:C,id:l});if(H&&E(_)){n(_),h();return}let ae=this.handleErrorMessage({data:C,id:l});ae.valid||(o(ae.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 b("The signer has been disconnected. Your request cannot be processed.")}:this.#e.closed?{connected:!1,err:new b("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}=Be.safeParse(o);if(a&&i===p?.id&&E(p?.result)){let{result:{supportedStandards:R}}=p;return{handled:!0,result:R}}return{handled:!1}},n=o=>{ze({popup:this.#e,origin:this.#t,id:o})};return await this.request({options:{timeoutInMilliseconds:e??ge,...r},postRequest:n,handleMessage:s})};permissions=async({options:{timeoutInMilliseconds:e,...r}={}}={})=>{let s=n=>{Ue({popup:this.#e,origin:this.#t,id:n})};return await this.requestPermissionsScopes({options:{timeoutInMilliseconds:e??Ee,...r},postRequest:s})};requestPermissions=async({options:{timeoutInMilliseconds:e,...r}={},params:s}={})=>{let n=o=>{Le({popup:this.#e,origin:this.#t,id:o,params:s??Ae})};return await this.requestPermissionsScopes({options:{timeoutInMilliseconds:e??Ce,...r},postRequest:n})};requestPermissionsScopes=async({options:e,postRequest:r})=>{let s=({data:n,id:o})=>{let{success:i,data:a}=Ye.safeParse(n);if(i&&o===a?.id&&E(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}=ke.safeParse(o);if(a&&i===p?.id&&E(p?.result)){let{result:{accounts:R}}=p;return{handled:!0,result:R}}return{handled:!1}},n=o=>{ve({popup:this.#e,origin:this.#t,id:o})};return await this.request({options:{timeoutInMilliseconds:e??_e,...r},postRequest:n,handleMessage:s})};call=async({options:{timeoutInMilliseconds:e,...r}={},params:s})=>{let n=({data:a,id:p})=>{let{success:R,data:S}=je.safeParse(a);if(R&&p===S?.id&&E(S?.result)){let{result:I}=S;return{handled:!0,result:I}}return{handled:!1}},o=a=>{De({popup:this.#e,origin:this.#t,id:a,params:s})},i=await this.request({options:{timeoutInMilliseconds:e??Te,...r},postRequest:o,handleMessage:n});return st({params:s,result:i}),i};handleErrorMessage=({data:e,id:r})=>{let{success:s,data:n}=K.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!==W).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{ct as RelyingParty}; //# sourceMappingURL=relying-party.js.map