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