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) 17.4 kB
import{mapIcrc1TransferError as pr,mapIcrc2ApproveError as dr,toIcrc1TransferRawRequest as ur,toIcrc2ApproveRawRequest as lr}from"@dfinity/ledger-icp";import{IDL as a}from"@dfinity/candid";var de=a.Vec(a.Nat8),Ct=a.Record({owner:a.Principal,subaccount:a.Opt(de)}),ue=a.Nat64,le=a.Record({to:Ct,fee:a.Opt(a.Nat),memo:a.Opt(a.Vec(a.Nat8)),from_subaccount:a.Opt(de),created_at_time:a.Opt(ue),amount:a.Nat}),_t=a.Variant({GenericError:a.Record({message:a.Text,error_code:a.Nat}),TemporarilyUnavailable:a.Null,BadBurn:a.Record({min_burn_amount:a.Nat}),Duplicate:a.Record({duplicate_of:a.Nat}),BadFee:a.Record({expected_fee:a.Nat}),CreatedInFuture:a.Record({ledger_time:ue}),TooOld:a.Null,InsufficientFunds:a.Record({balance:a.Nat})}),me=a.Variant({Ok:a.Nat,Err:_t});import{IDL as t}from"@dfinity/candid";var K=t.Record({owner:t.Principal,subaccount:t.Opt(t.Vec(t.Nat8))}),Re=t.Record({fee:t.Opt(t.Nat),memo:t.Opt(t.Vec(t.Nat8)),from_subaccount:t.Opt(t.Vec(t.Nat8)),created_at_time:t.Opt(t.Nat64),amount:t.Nat,expected_allowance:t.Opt(t.Nat),expires_at:t.Opt(t.Nat64),spender:K}),yr=t.Record({to:K,fee:t.Opt(t.Nat),spender_subaccount:t.Opt(t.Vec(t.Nat8)),from:K,memo:t.Opt(t.Vec(t.Nat8)),created_at_time:t.Opt(t.Nat64),amount:t.Nat}),Tt=t.Variant({GenericError:t.Record({message:t.Text,error_code:t.Nat}),TemporarilyUnavailable:t.Null,Duplicate:t.Record({duplicate_of:t.Nat}),BadFee:t.Record({expected_fee:t.Nat}),AllowanceChanged:t.Record({current_allowance:t.Nat}),CreatedInFuture:t.Record({ledger_time:t.Nat64}),TooOld:t.Null,Expired:t.Record({ledger_time:t.Nat64}),InsufficientFunds:t.Record({balance:t.Nat})}),Et=t.Variant({GenericError:t.Record({message:t.Text,error_code:t.Nat}),TemporarilyUnavailable:t.Null,InsufficientAllowance:t.Record({allowance:t.Nat}),BadBurn:t.Record({min_burn_amount:t.Nat}),Duplicate:t.Record({duplicate_of:t.Nat}),BadFee:t.Record({expected_fee:t.Nat}),CreatedInFuture:t.Record({ledger_time:t.Nat64}),TooOld:t.Null,InsufficientFunds:t.Record({balance:t.Nat})}),Ie=t.Variant({Ok:t.Nat,Err:Tt}),hr=t.Variant({Ok:t.Nat,Err:Et});import{assertNonNullish as ht,nonNullish as T,notEmptyString as St}from"@dfinity/utils";var ye="icrc21_call_consent_message",v="icrc25_request_permissions",D="icrc25_permissions",W="icrc25_supported_standards",P="icrc27_accounts",A="icrc29_status",O="icrc49_call_canister",G="granted",he="denied",Se="ask_on_use",fe="ICRC-21",ge="ICRC-25",Ce="ICRC-27",_e="ICRC-29",Te="ICRC-49";import*as Y from"zod/v4";var Pt=Y.enum([ye,v,D,W,P,A,O]),k=Pt.extract([P,O]),Ee=Y.enum([G,he,Se]),J=Y.enum([fe,ge,Ce,_e,Te]);var Pe=5e3,F=60*2*1e3,Z=5e3,Ae=F,Oe=Z,xe=F,Ne=Z,qe=F,we=F,Me=Z,be={scopes:Object.values(k.enum).map(r=>({method:r}))};var x="toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=yes, resizable=no, copyhistory=no",Le={width:576,height:625},ze={...Le,position:"top-right",features:x},Er={...Le,position:"center",features:x};var Ue="http://localhost:4943",ve="https://icp-api.io";import*as p from"zod/v4";var f="2.0",At=p.literal(f),N=p.union([p.string(),p.number(),p.null()]),De=p.object({jsonrpc:At,id:p.optional(N)}),Ot=De.extend({id:N}).merge(p.object({method:p.string(),params:p.optional(p.any())})).strict();var Or=Ot.omit({id:!0}).strict(),We=(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))(We||{}),xt=p.union([p.number(),p.nativeEnum(We)]),Ge=p.object({code:xt,message:p.string(),data:p.optional(p.never())}),Nt=De.extend({id:N}),X=Nt.extend({error:Ge}).strict(),C=r=>X.omit({error:!0}).merge(p.object({result:r,error:Ge}).partial()).strict().refine(({result:e,error:o})=>e!==void 0||o!==void 0,"Either result or error should be provided."),ee=C(p.any());var wt=async r=>{await new Promise(e=>{setTimeout(e,r)})},te=async({retries:r,isReady:e,fn:o,intervalInMilliseconds:s=500})=>{let c=e();if(c!=="pending")return c;let n=r-1;return n===0?"timeout":(o(),await wt(s),await te({retries:n,intervalInMilliseconds:s,isReady:e,fn:o}))};var Ye=async({popup:r,id:e,isReady:o,timeoutInMilliseconds:s,intervalInMilliseconds:c})=>{let n=()=>{q({popup:r,msg:{jsonrpc:f,id:e,method:A},origin:"*"})};return await te({retries:s/(c??500),intervalInMilliseconds:c,isReady:o,fn:n})},ke=({id:r,...e})=>{q({msg:{jsonrpc:f,id:r,method:A},...e})},Fe=({id:r,...e})=>{q({msg:{jsonrpc:f,id:r,method:W},...e})},Be=({id:r,...e})=>{q({msg:{jsonrpc:f,id:r,method:D},...e})},je=({id:r,params:e,...o})=>{re({msg:{jsonrpc:f,id:r,method:v,params:e},...o})},Ve=({id:r,...e})=>{re({msg:{jsonrpc:f,id:r,method:P},...e})},He=({id:r,params:e,...o})=>{re({msg:{jsonrpc:f,id:r,method:O,params:e},...o})},re=({popup:r,...e})=>{r.focus(),q({popup:r,...e})},q=({popup:r,msg:e,origin:o})=>{r.postMessage(e,o)};import{UrlSchema as Ut}from"@dfinity/zod-schemas";import*as y from"zod/v4";import*as $e from"zod/v4";var w=$e.string().refine(r=>{try{return btoa(atob(r))===r}catch{return!1}},{message:"Invalid base64 string"});import{base64ToUint8Array as Mt}from"@dfinity/utils";import{PrincipalTextSchema as bt}from"@dfinity/zod-schemas";import*as B from"zod/v4";var Lt=w.refine(r=>{try{return Mt(r).length===32}catch{return!1}},{message:"Subaccount must be exactly 32 bytes long."}),zt=B.object({owner:bt,subaccount:Lt.optional()}).strict(),Qe=B.array(zt).min(1);var vt=y.object({method:k}),Dt=y.object({scope:vt,state:Ee}).strict(),Wt=y.array(Dt),Gt=y.object({scopes:Wt}).strict(),Ke=C(Gt),Yt=/^https:\/\/github\.com\/dfinity\/ICRC\/blob\/main\/ICRCs\/ICRC-\d+\/ICRC-\d+\.md$/,kt=y.url().regex(Yt).refine(r=>{try{Ut.parse(r)}catch{return!1}let e=/(ICRC-\d+)\.md/g.exec(r);if(e===null)return!1;let[o,s]=e;return Object.keys(J.enum).includes(s)},{message:"The URL does not match any of the IcrcStandard values."}),Ft=y.array(y.object({name:J,url:kt}).strict()).min(1),Je=C(y.object({supportedStandards:Ft})),oe=C(y.literal("ready")),Ze=C(y.object({accounts:Qe})),Bt=y.object({contentMap:w,certificate:w}).strict(),Xe=C(Bt.strict());var j=class extends Error{code;constructor({message:e,code:o}){super(e),this.code=o}},M=class extends Error{};import{UrlSchema as rt}from"@dfinity/zod-schemas";import*as m from"zod/v4";import*as et from"zod/v4";var tt=r=>et.custom(e=>r.implement(e));var jt=m.object({pollingIntervalInMilliseconds:m.number().positive().optional(),timeoutInMilliseconds:m.number().positive().optional()}),Vt=m.object({position:m.enum(["top-right","center"]),width:m.number().positive(),height:m.number().positive(),features:m.string().optional()}),Ht=tt(m.function({output:m.void()})).optional(),$t=rt.optional(),ot=m.object({url:rt,windowOptions:m.union([Vt,m.string()]).optional(),connectionOptions:jt.optional(),onDisconnect:Ht,host:$t});import*as b from"zod/v4";var st=b.object({timeoutInMilliseconds:b.number().positive()}),se=b.object({requestId:N.optional()}).merge(st.partial()),Jr=se.omit({timeoutInMilliseconds:!0}).merge(st);import{AnonymousIdentity as tr,Certificate as rr,HttpAgent as or,lookupResultToBuffer as sr,requestIdOf as nr,uint8ToBuf as lt}from"@dfinity/agent";import{Principal as Rt}from"@dfinity/principal";import{assertNonNullish as mt,base64ToUint8Array as cr}from"@dfinity/utils";import{decode as Qt}from"@dfinity/cbor";import{Principal as Kt}from"@dfinity/principal";import{base64ToUint8Array as Jt}from"@dfinity/utils";var ne=r=>{let{ingress_expiry:e,canister_id:o,...s}=Qt(Jt(r));return{...s,canister_id:Kt.fromUint8Array(o),ingress_expiry:e}};import{Principal as nt}from"@dfinity/principal";import{arrayBufferToUint8Array as Zt,base64ToUint8Array as Xt}from"@dfinity/utils";var ct=({requestMethod:r,responseMethod:e})=>{if(e!==r)throw new Error("The response method does not match the request method.")},it=({responseArg:r,requestArg:e})=>{let o=Xt(e),s=Zt(r);if(!(({first:n,second:i})=>n.length===i.length&&n.every((d,u)=>d===i[u]))({first:o,second:s}))throw new Error("The response does not contain the request arguments.")},at=({requestCanisterId:r,responseCanisterId:e})=>{if(r.toText()!==e.toText())throw new Error("The response canister ID does not match the requested canister ID.")},pt=({requestSender:r,responseSender:e})=>{if((e instanceof Uint8Array?nt.fromUint8Array(e):e).toText()!==nt.fromText(r).toText())throw new Error("The response sender does not match the request sender.")};import{IDL as dt}from"@dfinity/candid";import{uint8ArrayToBase64 as er}from"@dfinity/utils";var ce=({recordClass:r,rawArgs:e})=>er(new Uint8Array(dt.encode([r],[e]))),ut=({recordClass:r,bytes:e})=>{let o=dt.decode([r],e);if(o.length!==1)throw new Error("More than one object returned. This is unexpected.");let[s]=o;return s};var It=({params:{method:r,arg:e,canisterId:o,sender:s},result:{contentMap:c}})=>{let n=ne(c);at({requestCanisterId:Rt.fromText(o),responseCanisterId:n.canister_id}),ct({requestMethod:r,responseMethod:n.method_name}),it({requestArg:e,responseArg:n.arg}),pt({requestSender:s,responseSender:n.sender})},ie=async({params:{canisterId:r},result:{certificate:e,contentMap:o},resultRecordClass:s,host:c})=>{let n=ne(o),{location:{hostname:i}}=window,d=["localhost","127.0.0.1"].includes(i),u=await or.create({identity:new tr,host:d?c??Ue:ve,...d&&{shouldFetchRootKey:!0}});mt(u.rootKey,"Missing agent root key, which is required to certify the response.");let l=await rr.create({certificate:lt(cr(e)),rootKey:u.rootKey,canisterId:Rt.fromText(r)}),h=nr(n),R=[lt(new TextEncoder().encode("request_status")),h],I=sr(l.lookup([...R,"reply"]));return mt(I,"A reply cannot be resolved within the provided certificate. This is unexpected; it should have been known at this point."),ut({recordClass:s,bytes:I})};import{isNullish as V}from"@dfinity/utils";var ae=()=>typeof window<"u";var yt=({position:r,...e})=>(r==="center"?ir:ar)(e),ir=({width:r,height:e,features:o=x})=>{if(!ae()||V(window)||V(window.top))return;let{top:{innerWidth:s,innerHeight:c}}=window,n=c/2+screenY-e/2,i=s/2+screenX-r/2;return`${o}, width=${r}, height=${e}, top=${n}, left=${i}`},ar=({width:r,height:e,features:o=x})=>{if(!ae()||V(window)||V(window.top))return;let{top:{innerWidth:s,innerHeight:c}}=window,n=outerHeight-c,i=s-r;return`${o}, width=${r}, height=${e}, top=${n}, left=${i}`};var H=class r{#t;#e;#o;host;#r="connected";#s;constructor({origin:e,popup:o,onDisconnect:s,host:c}){this.#t=e,this.#e=o,this.#o=s,this.host=c,this.#r="connected",this.#s=setInterval(this.checkWalletStatusCallback,Pe)}static async connect({onDisconnect:e,...o}){return await this.connectSigner({options:o,init:s=>new r({...s,onDisconnect:e})})}static async connectSigner({options:e,init:o}){let{success:s,error:c}=ot.safeParse(e);if(!s)throw new Error(`Options cannot be parsed: ${c?.message??""}`);let{url:n,windowOptions:i,connectionOptions:d}=e,u=typeof i=="string"?i:yt(i??ze),l=window.open(n,"relyingPartyWindow",u);ht(l,"Unable to open the signer window.");let h=()=>{l.close()};class R extends Error{}let I,L=({origin:S,data:_})=>{let{success:$}=ee.safeParse(_);if(!$)return;let U;try{let{origin:E}=new URL(n);U=E}catch{I=new R(`The origin ${S} of the signer URL ${n} cannot be parsed.`);return}if(St(S)&&S!==U){I=new R(`The response origin ${S} does not match the requested signer URL ${n}.`);return}let{success:Q}=oe.safeParse(_);Q&&(I=o({origin:S,popup:l}))};window.addEventListener("message",L);let z=()=>{window.removeEventListener("message",L)},g=async()=>{if(await Ye({popup:l,isReady:()=>T(I)?I instanceof r?"ready":"error":"pending",id:crypto.randomUUID(),timeoutInMilliseconds:d?.timeoutInMilliseconds??Ae,intervalInMilliseconds:d?.pollingIntervalInMilliseconds})==="timeout")throw new Error("Connection timeout. Unable to connect to the signer.");if(ht(I,"Unexpected error. The request status succeeded, but the signer response is not defined."),I instanceof R)throw I;return I};try{return await g()}catch(S){throw h(),S}finally{z()}}disconnect=async()=>{clearInterval(this.#s),this.#e.close(),this.#o?.()};checkWalletStatusCallback=()=>{this.checkWalletStatus()};async checkWalletStatus(){let e=({data:c,id:n})=>{let{success:i,data:d}=oe.safeParse(c);return i&&n===d?.id?{handled:!0,result:"connected"}:{handled:!0,result:"disconnected"}},o=c=>{ke({popup:this.#e,origin:this.#t,id:c})},s=async()=>{try{return await this.request({options:{timeoutInMilliseconds:Me},postRequest:o,handleMessage:e})}catch{return"disconnected"}};this.#r=await s(),this.#r!=="connected"&&await this.disconnect()}request=async({options:e,postRequest:o,handleMessage:s})=>await new Promise((c,n)=>{let{connected:i,err:d}=this.assertWalletConnected();if(!i){n(d??new Error("Unexpected reason for disconnection."));return}let{success:u,error:l}=se.safeParse(e);if(!u)throw new Error(`Wallet request options cannot be parsed: ${l?.message??""}`);let{requestId:h,timeoutInMilliseconds:R}=e,I=h??crypto.randomUUID(),L=setTimeout(()=>{n(new Error(`Request to signer timed out after ${R} milliseconds.`)),g()},R),z=({origin:S,data:_,source:$})=>{let{success:U}=ee.safeParse(_);if(!U)return;if($!==this.#e){n(new Error("The response is not originating from the window that was opened.")),g();return}if(St(S)&&S!==this.#t){n(new Error(`The response origin ${S} does not match the signer origin ${this.#t}.`)),g();return}let{handled:Q,result:E}=s({data:_,id:I});if(Q&&T(E)){c(E),g();return}let pe=this.handleErrorMessage({data:_,id:I});pe.valid||(n(pe.error),g())};window.addEventListener("message",z);let g=()=>{clearTimeout(L),window.removeEventListener("message",z)};o(I)});assertWalletConnected(){return this.#r==="disconnected"?{connected:!1,err:new M("The signer has been disconnected. Your request cannot be processed.")}:this.#e.closed?{connected:!1,err:new M("The signer has been closed. Your request cannot be processed.")}:{connected:!0}}supportedStandards=async({options:{timeoutInMilliseconds:e,...o}={}}={})=>{let s=({data:n,id:i})=>{let{success:d,data:u}=Je.safeParse(n);if(d&&i===u?.id&&T(u?.result)){let{result:{supportedStandards:l}}=u;return{handled:!0,result:l}}return{handled:!1}},c=n=>{Fe({popup:this.#e,origin:this.#t,id:n})};return await this.request({options:{timeoutInMilliseconds:e??Oe,...o},postRequest:c,handleMessage:s})};permissions=async({options:{timeoutInMilliseconds:e,...o}={}}={})=>{let s=c=>{Be({popup:this.#e,origin:this.#t,id:c})};return await this.requestPermissionsScopes({options:{timeoutInMilliseconds:e??Ne,...o},postRequest:s})};requestPermissions=async({options:{timeoutInMilliseconds:e,...o}={},params:s}={})=>{let c=n=>{je({popup:this.#e,origin:this.#t,id:n,params:s??be})};return await this.requestPermissionsScopes({options:{timeoutInMilliseconds:e??xe,...o},postRequest:c})};requestPermissionsScopes=async({options:e,postRequest:o})=>{let s=({data:c,id:n})=>{let{success:i,data:d}=Ke.safeParse(c);if(i&&n===d?.id&&T(d?.result)){let{result:{scopes:u}}=d;return{handled:!0,result:u}}return{handled:!1}};return await this.request({options:e,postRequest:o,handleMessage:s})};accounts=async({options:{timeoutInMilliseconds:e,...o}={}}={})=>{let s=({data:n,id:i})=>{let{success:d,data:u}=Ze.safeParse(n);if(d&&i===u?.id&&T(u?.result)){let{result:{accounts:l}}=u;return{handled:!0,result:l}}return{handled:!1}},c=n=>{Ve({popup:this.#e,origin:this.#t,id:n})};return await this.request({options:{timeoutInMilliseconds:e??qe,...o},postRequest:c,handleMessage:s})};call=async({options:{timeoutInMilliseconds:e,...o}={},params:s})=>{let c=({data:d,id:u})=>{let{success:l,data:h}=Xe.safeParse(d);if(l&&u===h?.id&&T(h?.result)){let{result:R}=h;return{handled:!0,result:R}}return{handled:!1}},n=d=>{He({popup:this.#e,origin:this.#t,id:d,params:s})},i=await this.request({options:{timeoutInMilliseconds:e??we,...o},postRequest:n,handleMessage:c});return It({params:s,result:i}),i};handleErrorMessage=({data:e,id:o})=>{let{success:s,data:c}=X.safeParse(e);return!s||o!==c?.id?{valid:!0}:{valid:!1,error:new j(c.error)}};requestPermissionsNotGranted=async({options:e}={})=>{let o=d=>d.filter(({state:u})=>u!==G).map(({scope:u})=>u),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 c=o(s);if(c.length===0)return{allPermissionsGranted:!0};let n=await this.requestPermissions({options:e,params:{scopes:c}});if(n.length===0)throw new Error("The signer did not provide any data about the current set of permissions following the request.");return{allPermissionsGranted:o(n).length===0}}};var ft="ryjl3-tyaaa-aaaaa-aaaba-cai",gt=class r extends H{static async connect({onDisconnect:e,host:o,...s}){return await this.connectSigner({options:s,init:c=>new r({...c,onDisconnect:e,host:o})})}icrc1Transfer=async({request:e,owner:o,ledgerCanisterId:s,options:c})=>{let n=ur(e),i=ce({recordClass:le,rawArgs:n}),l={sender:o,method:"icrc1_transfer",canisterId:s??ft,arg:i},h=await this.call({params:l,options:c}),R=await ie({params:l,result:h,resultRecordClass:me,host:this.host});if("Err"in R)throw pr(R.Err);return R.Ok};icrc2Approve=async({request:e,owner:o,ledgerCanisterId:s,options:c})=>{let n=lr(e),i=ce({recordClass:Re,rawArgs:n}),l={sender:o,method:"icrc2_approve",canisterId:s??ft,arg:i},h=await this.call({params:l,options:c}),R=await ie({params:l,result:h,resultRecordClass:Ie,host:this.host});if("Err"in R)throw dr(R.Err);return R.Ok}};export{gt as IcpWallet}; //# sourceMappingURL=icp-wallet.js.map