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