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