@argent/x-sessions
Version:
Manage sessions for Argent X wallets
720 lines (719 loc) • 18.8 kB
JavaScript
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
};