UNPKG

@argent/x-sessions

Version:

Manage sessions for Argent X wallets

720 lines (719 loc) 18.8 kB
var L = Object.defineProperty; var J = (e, t, n) => t in e ? L(e, t, { enumerable: !0, configurable: !0, writable: !0, value: n }) : e[t] = n; var D = (e, t, n) => J(e, typeof t != "symbol" ? t + "" : t, n); import { typedData as f, stark as w, transaction as K, RPC as T, num as _, Signer as R, CairoCustomEnum as $, byteArray as Y, hash as S, ec as H, selector as z, merkle as Q, shortString as v, Account as X, CallData as I, encode as C } from "starknet"; const Z = { ACTIVE: "1" }, E = "https://cloud.argent-api.com/v1"; class B extends Error { constructor(t, n) { super(t), this.cause = n, Error.captureStackTrace(this, this.constructor); } } const q = async ({ sessionKey: e, authorisationSignature: t, argentSessionServiceBaseUrl: n = E, calls: o, transactionsDetail: s, sessionTypedData: r, sessionSignature: a, cacheAuthorisation: i }) => { var x, A; const c = K.getExecuteCalldata( o, s.cairoVersion ), g = f.getMessageHash( r, s.walletAddress ), l = w.formatSignature(t), u = { session: { sessionHash: g, sessionAuthorisation: l, cacheAuthorisation: i, sessionSignature: { type: "StarknetKey", signer: { publicKey: e.publicKey, r: a[0].toString(), s: a[1].toString() } } } }; if (Object.values(T.ETransactionVersion2).includes( s.version )) { const d = s; u.transaction = { contractAddress: d.walletAddress, calldata: c, maxFee: d.maxFee.toString(), nonce: d.nonce.toString(), version: _.toBigInt(d.version).toString(10), chainId: _.toBigInt(d.chainId).toString(10) }; } else if (Object.values(T.ETransactionVersion3).includes( s.version )) { const d = s; u.transaction = { sender_address: d.walletAddress, calldata: c, nonce: d.nonce.toString(), version: _.toBigInt(d.version).toString(10), chain_id: _.toBigInt(d.chainId).toString(10), resource_bounds: { l1_gas: { max_amount: d.resourceBounds.l1_gas.max_amount.toString(), max_price_per_unit: d.resourceBounds.l1_gas.max_price_per_unit.toString() }, l1_data_gas: { max_amount: ((x = d.resourceBounds.l1_data_gas) == null ? void 0 : x.max_amount.toString()) || "0", max_price_per_unit: ((A = d.resourceBounds.l1_data_gas) == null ? void 0 : A.max_price_per_unit.toString()) || "0" }, l2_gas: { max_amount: d.resourceBounds.l2_gas.max_amount.toString(), max_price_per_unit: d.resourceBounds.l2_gas.max_price_per_unit.toString() } }, tip: d.tip.toString(), paymaster_data: d.paymasterData.map((k) => k.toString()), account_deployment_data: d.accountDeploymentData, nonce_data_availability_mode: d.nonceDataAvailabilityMode, fee_data_availability_mode: d.feeDataAvailabilityMode }; } else throw Error("unsupported signTransaction version"); const p = await fetch( `${n}/cosigner/signSession`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(u) } ); if (!p.ok) { const d = await p.json(); throw new B("Sign session error", d.status); } return (await p.json()).signature; }, ee = async ({ sessionKey: e, authorisationSignature: t, argentSessionServiceBaseUrl: n = E, sessionTokenToSign: o, accountAddress: s, currentTypedData: r, sessionSignature: a, cacheAuthorisation: i, chainId: c, network: g = "mainnet" }) => { const l = f.getMessageHash( b(o, c), s ), m = w.formatSignature(t), h = { session: { sessionHash: l, sessionAuthorisation: m, cacheAuthorisation: i, sessionSignature: { type: "StarknetKey", signer: { publicKey: e.publicKey, r: a[0].toString(), s: a[1].toString() } } }, message: { type: "eip712", accountAddress: s, chain: "starknet", message: r, network: g } }, x = JSON.stringify( h, (k, M) => typeof M == "bigint" ? M.toString() : M ), A = await fetch( `${n}/cosigner/signSessionEFO`, { method: "POST", headers: { "Content-Type": "application/json" }, body: x } ); if (!A.ok) { const k = await A.json(); throw new B("Sign session error", k.status); } return (await A.json()).signature; }; class te extends R { constructor(t) { super(), this.signTransactionCallback = t; } async signRaw(t) { throw new Error("Method not implemented."); } async signTransaction(t, n) { return this.signTransactionCallback(t, n); } } var y = /* @__PURE__ */ ((e) => (e[e.Starknet = 0] = "Starknet", e[e.Secp256k1 = 1] = "Secp256k1", e[e.Secp256r1 = 2] = "Secp256r1", e[e.Eip191 = 3] = "Eip191", e[e.Webauthn = 4] = "Webauthn", e))(y || {}); const ne = (e, t) => { const n = { Starknet: void 0, Secp256k1: void 0, Secp256r1: void 0, Eip191: void 0, Webauthn: void 0 }; if (e === y.Starknet) n.Starknet = t; else if (e === y.Secp256k1) n.Secp256k1 = t; else if (e === y.Secp256r1) n.Secp256r1 = t; else if (e === y.Eip191) n.Eip191 = t; else if (e === y.Webauthn) n.Webauthn = t; else throw new Error("Unknown SignerType"); return new $(n); }, P = (e) => { const t = e.allowed_methods.map( (n) => S.computePoseidonHashOnElements([ re, n["Contract Address"], z.getSelectorFromName(n.selector) ]) ); return new Q.MerkleTree(t, S.computePoseidonHash); }, j = (e) => { const t = Y.byteArrayFromString( e.metadata ), n = [ t.data.length, ...t.data, t.pending_word, t.pending_word_len ], o = S.computePoseidonHashOnElements(n); return { expires_at: e.expires_at, allowed_methods_root: P(e).root.toString(), metadata_hash: o, session_key_guid: e.session_key_guid }; }, se = (e, t) => { const n = P(e); return t.map((o) => { const s = e.allowed_methods.findIndex( (r) => _.hexToDecimalString(r["Contract Address"]) === _.hexToDecimalString(o.contractAddress) && r.selector == o.entrypoint ); return n.getProof(n.leaves[s], n.leaves); }); }, O = (e, t) => ne(y.Starknet, { pubkey: e, r: t[0], s: t[1] }), N = async (e, t, n, o, s, r, a, i) => ({ session: e, cache_authorization: i, session_authorization: r, sessionSignature: O( n.publicKey, s ), guardianSignature: O(a.publicKey, [ a.r, a.s ]), proofs: se(t, o) }), V = (e, t, n, o, s) => { const r = f.getMessageHash( n, t ), a = S.computePoseidonHashOnElements([ e, r, +o ]), i = H.starkCurve.sign(a, s.privateKey); return [i.r, i.s]; }, oe = v.encodeShortString("session-token"); class ae { /** * Creates an instance of SessionAccount. * @param session - The session object containing session details. * @param sessionKey - The session key used for signing transactions. * @param argentSessionServiceUrl - The base URL for the Argent session service. */ constructor(t, n, o = E) { D(this, "argentSessionServiceUrl"); this.session = t, this.sessionKey = n, this.argentSessionServiceUrl = o; } /** * Retrieves an account with a session signer. * * @param {Object} params - The parameters for the function. * @param {Provider} params.provider - The provider to use for the account. * @param {Session} params.session - The session information. * @param {boolean} [params.cacheAuthorisation=false] - Whether to cache the authorisation signature. * @returns {Account} The account with the session signer. */ getAccountWithSessionSigner({ provider: t, session: n, cacheAuthorisation: o = !1 }) { const s = new te( (r, a) => this.signTransaction( w.formatSignature(n.authorisationSignature), n, r, a, o ) ); return new X(t, n.address, s); } async signTransaction(t, n, o, s, r) { const a = K.getExecuteCalldata( o, s.cairoVersion ); let i; if (Object.values(T.ETransactionVersion2).includes( s.version )) { const c = s; i = S.calculateInvokeTransactionHash({ ...c, senderAddress: c.walletAddress, compiledCalldata: a, version: c.version }); } else if (Object.values(T.ETransactionVersion3).includes( s.version )) { const c = s; i = S.calculateInvokeTransactionHash({ ...c, senderAddress: c.walletAddress, compiledCalldata: a, version: c.version, nonceDataAvailabilityMode: w.intDAM( c.nonceDataAvailabilityMode ), feeDataAvailabilityMode: w.intDAM( c.feeDataAvailabilityMode ) }); } else throw Error("unsupported signTransaction version"); return this.getSessionSignatureForTransaction({ sessionAuthorizationSignature: t, session: n, transactionHash: i, calls: o, accountAddress: s.walletAddress, invocationSignerDetails: s, cacheAuthorisation: r }); } /** * Generates a session signature for a transaction. * * @param sessionAuthorizationSignature - The authorization signature for the session. * @param session - The session object containing session details. * @param transactionHash - The hash of the transaction. * @param calls - An array of calls to be made. * @param accountAddress - The address of the account. * @param invocationSignerDetails - Details of the invocation signer. * @param cacheAuthorisation - A boolean indicating whether to cache the authorization. * @returns A promise that resolves to an array containing the session signature. */ async getSessionSignatureForTransaction({ sessionAuthorizationSignature: t, session: n, transactionHash: o, calls: s, accountAddress: r, invocationSignerDetails: a, cacheAuthorisation: i }) { const c = { allowed_methods: n.allowedMethods, expires_at: n.expiresAt, metadata: n.metadata, session_key_guid: n.sessionKeyGuid }, g = j(c), l = b( c, this.session.chainId ), m = V( o, r, l, i, this.sessionKey ), u = await q({ sessionKey: this.sessionKey, authorisationSignature: this.session.authorisationSignature, argentSessionServiceBaseUrl: this.argentSessionServiceUrl, calls: s, transactionsDetail: a, sessionTypedData: l, sessionSignature: m, cacheAuthorisation: i }), p = await N( g, c, this.sessionKey, s, m, t, u, i ); return [oe, ...I.compile(p)]; } } const U = { StarknetDomain: [ { name: "name", type: "shortstring" }, { name: "version", type: "shortstring" }, { name: "chainId", type: "shortstring" }, { name: "revision", type: "shortstring" } ], "Allowed Method": [ { name: "Contract Address", type: "ContractAddress" }, { name: "selector", type: "selector" } ], Session: [ { name: "Expires At", type: "timestamp" }, { name: "Allowed Methods", type: "merkletree", contains: "Allowed Method" }, { name: "Metadata", type: "string" }, { name: "Session Key", type: "felt" } ] }, re = f.getTypeHash( U, "Allowed Method", Z.ACTIVE ), ie = (e) => ({ name: "SessionAccount.session", version: v.encodeShortString("1"), chainId: e, revision: "1" }), b = (e, t) => ({ types: U, primaryType: "Session", domain: ie(t), message: { "Expires At": e.expires_at, "Allowed Methods": e.allowed_methods, Metadata: e.metadata, "Session Key": e.session_key_guid } }), ce = (e, t, n, o) => ({ expires_at: Number(t), allowed_methods: e, metadata: JSON.stringify(n), session_key_guid: S.computePoseidonHash( v.encodeShortString("Starknet Signer"), o ) }), _e = async ({ session: e, sessionKey: t, provider: n, argentSessionServiceBaseUrl: o, useCacheAuthorisation: s }) => new ae( e, t, o ).getAccountWithSessionSigner({ provider: n, session: e, cacheAuthorisation: s }), fe = ({ chainId: e, sessionParams: t }) => { const { allowedMethods: n, expiry: o = BigInt(Date.now()) + 10000n, sessionKey: s, metaData: r } = t; if (!s || !s.publicKey) throw new Error("sessionPublicKey is required"); const a = ce( n, o, r, s.publicKey ); return { sessionTypedData: b(a, e), offchainSession: a, sessionKey: s }; }, xe = async ({ address: e, authorisationSignature: t, sessionRequest: n, chainId: o }) => { const { sessionKey: s, sessionTypedData: r, offchainSession: a } = n; if (!s || !s.publicKey) throw new Error("sessionPublicKey is required"); return { authorisationSignature: t, address: e, chainId: o, hash: f.getMessageHash(r, e), version: v.encodeShortString("1"), expiresAt: a.expires_at, allowedMethods: a.allowed_methods, metadata: a.metadata, sessionKeyGuid: a.session_key_guid, sessionKey: s }; }, Ae = ({ session: e, sessionKey: t }) => { const n = { allowed_methods: e.allowedMethods, expires_at: e.expiresAt, metadata: e.metadata, session_key_guid: e.sessionKeyGuid }, o = b(n, e.chainId), s = f.getMessageHash(o, e.address); return H.starkCurve.sign(e.hash, t.privateKey) && e.hash === s; }, we = (e) => C.addHexPrefix(C.buf2hex(e)), ve = (e) => { const n = C.removeHexPrefix(e).match(/.{1,2}/g); if (!n) throw new Error("Invalid hex string"); return Uint8Array.from(n.map((o) => parseInt(o, 16))); }; /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ function de(e) { return e instanceof Uint8Array || ArrayBuffer.isView(e) && e.constructor.name === "Uint8Array"; } function le(e) { if (!de(e)) throw new Error("Uint8Array expected"); } const ue = /* @__PURE__ */ Array.from({ length: 256 }, (e, t) => t.toString(16).padStart(2, "0")); function ge(e) { le(e); let t = ""; for (let n = 0; n < e.length; n++) t += ue[e[n]]; return t; } const pe = { StarknetDomain: [ { name: "name", type: "shortstring" }, { name: "version", type: "shortstring" }, { name: "chainId", type: "shortstring" }, { name: "revision", type: "shortstring" } ], OutsideExecution: [ { name: "Caller", type: "ContractAddress" }, { name: "Nonce", type: "felt" }, { name: "Execute After", type: "u128" }, { name: "Execute Before", type: "u128" }, { name: "Calls", type: "Call*" } ], Call: [ { name: "To", type: "ContractAddress" }, { name: "Selector", type: "selector" }, { name: "Calldata", type: "felt*" } ] }; function me(e, t = "1") { return { name: "Account.execute_from_outside", version: t, chainId: e, revision: "1" }; } function he(e) { return { to: e.contractAddress, selector: S.getSelectorFromName(e.entrypoint), calldata: e.calldata ?? [] }; } const F = (e, t, n, o, s) => { const r = v.encodeShortString("ANY_CALLER"), a = C.addHexPrefix( ge(H.starkCurve.utils.randomPrivateKey()) ), i = Date.now(), c = Math.floor((i + 6e4 * 20) / 1e3), g = Math.floor((i - 6e4 * 10) / 1e3); return { caller: t || r, nonce: s || a, execute_after: n || g, execute_before: o || c, calls: e.map((l) => he(l)) }; }, Ee = async ({ session: e, sessionKey: t, cacheAuthorisation: n, calls: o, outsideExecutionParams: s, argentSessionServiceUrl: r = E, network: a = "mainnet" }) => { const { caller: i, execute_after: c, execute_before: g, nonce: l, version: m } = s || {}, u = F( o, i, c, g, l ), p = W({ outsideExecution: u, chainId: e.chainId, version: m || "2" // version, set as "2" because of a bug in x-sessions where default was wrongly set to "1" }), h = await G({ session: e, sessionKey: t, argentSessionServiceUrl: r, outsideExecutionTypedData: p, cacheAuthorisation: n, calls: o, network: a }); return { contractAddress: e.address, entrypoint: "execute_from_outside_v2", calldata: I.compile({ ...u, signature: h }) }; }, be = async ({ session: e, sessionKey: t, cacheAuthorisation: n, calls: o, outsideExecutionParams: s, argentSessionServiceUrl: r = E, network: a = "mainnet" }) => { const { caller: i, execute_after: c, execute_before: g, nonce: l, version: m } = s || {}, u = F( o, i, c, g, l ), p = W({ outsideExecution: u, chainId: e.chainId, version: m || "1" }), h = await G({ argentSessionServiceUrl: r, cacheAuthorisation: n, calls: o, outsideExecutionTypedData: p, session: e, sessionKey: t, network: a }); return { outsideExecutionTypedData: p, signature: h }; }, W = ({ outsideExecution: e, chainId: t, version: n = "1" }) => ({ types: pe, primaryType: "OutsideExecution", domain: me(t, n), message: { Caller: e.caller, Nonce: e.nonce, "Execute After": e.execute_after, "Execute Before": e.execute_before, Calls: e.calls.map((o) => ({ To: o.to, Selector: o.selector, Calldata: o.calldata })) } }), G = async ({ session: e, sessionKey: t, outsideExecutionTypedData: n, argentSessionServiceUrl: o = E, cacheAuthorisation: s = !1, calls: r, network: a = "mainnet" }) => { const i = { expires_at: e.expiresAt, allowed_methods: e.allowedMethods, metadata: e.metadata, session_key_guid: e.sessionKeyGuid }, c = v.encodeShortString("session-token"), g = j(i), l = b(i, e.chainId), m = f.getMessageHash( n, e.address ), u = V( m, e.address, l, s, t ), p = await ee({ sessionKey: t, authorisationSignature: e.authorisationSignature, argentSessionServiceBaseUrl: o, sessionTokenToSign: i, accountAddress: e.address, currentTypedData: n, sessionSignature: u, cacheAuthorisation: s, chainId: e.chainId, network: a }), h = await N( g, i, t, r, u, w.formatSignature(e.authorisationSignature), p, s ); return [c, ...I.compile(h)].map( (x) => _.toHex(x) ); }; export { re as ALLOWED_METHOD_HASH, F as buildOutsideExecution, _e as buildSessionAccount, we as bytesToHexString, ce as createOffchainSession, Ee as createOutsideExecutionCall, be as createOutsideExecutionTypedData, xe as createSession, fe as createSessionRequest, ie as getSessionDomain, b as getSessionTypedData, ve as hexStringToBytes, U as sessionTypes, G as signOutsideExecution, Ae as verifySession };