Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/refactor-create-session-key-parameters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"thirdweb": patch
---

**Refactor createSessionKey function to remove contract parameter**

- Remove `contract` input parameter from `createSessionKey` function in `thirdweb/wallets/in-app`
- Function now recreates contract internally using `account.address` with auto-resolved ABI
- Updated function signature to take `client` and `chain` directly instead of extending `BaseTransactionOptions`
- Updated related session key tests to match new function signature
- Simplified implementation while maintaining all existing functionality
201 changes: 73 additions & 128 deletions packages/thirdweb/src/extensions/erc7702/account/createSessionKey.ts
Original file line number Diff line number Diff line change
@@ -1,166 +1,111 @@
import type { BaseTransactionOptions } from "../../../transaction/types.js";
import type { Chain } from "../../../chains/types.js";
import type { ThirdwebClient } from "../../../client/client.js";
import { getContract } from "../../../contract/contract.js";
import { randomBytesHex } from "../../../utils/random.js";
import type { Account } from "../../../wallets/interfaces/wallet.js";
import {
createSessionWithSig,
isCreateSessionWithSigSupported,
} from "../__generated__/MinimalAccount/write/createSessionWithSig.js";
import {
type CallSpecInput,
CallSpecRequest,
ConstraintRequest,
SessionSpecRequest,
type TransferSpecInput,
TransferSpecRequest,
UsageLimitRequest,
} from "./types.js";
import { type CallSpecInput, type TransferSpecInput } from "./types.js";

/**
* @extension ERC7702
*/
export type CreateSessionKeyOptions = {
/**
* The admin account that will perform the operation.
*/
client: ThirdwebClient;
chain: Chain;
account: Account;
/**
* The address to add as a session key.
*/
sessionKeyAddress: string;
/**
* How long the session key should be valid for, in seconds.
*/
durationInSeconds: number;
/**
* Whether to grant full execution permissions to the session key.
*/
durationInSeconds?: number;
grantFullPermissions?: boolean;
/**
* Smart contract interaction policies to apply to the session key, ignored if grantFullPermissions is true.
*/
callPolicies?: CallSpecInput[];
/**
* Value transfer policies to apply to the session key, ignored if grantFullPermissions is true.
*/
transferPolicies?: TransferSpecInput[];
};

/**
* Creates session key permissions for a specified address.
* @param options - The options for the createSessionKey function.
* @param {Contract} options.contract - The EIP-7702 smart EOA contract to create the session key from
* @returns The transaction object to be sent.
* Creates a session key for the minimal account.
* This allows delegation of specific permissions to another address.
*
* @param options - The options for creating the session key
* @returns A prepared transaction to create the session key
*
* @example
* ```ts
* import { createSessionKey } from 'thirdweb/wallets/in-app';
* import { sendTransaction } from 'thirdweb';
* import { createSessionKey } from "thirdweb/extensions/erc7702/account";
* import { defineChain } from "thirdweb/chains";
*
* const transaction = createSessionKey({
* account: account,
* contract: accountContract,
* sessionKeyAddress: TEST_ACCOUNT_A.address,
* durationInSeconds: 86400, // 1 day
* grantFullPermissions: true
*})
*
* await sendTransaction({ transaction, account });
* client,
* chain: defineChain(1),
* account,
* sessionKeyAddress: "0x...",
* durationInSeconds: 3600, // 1 hour
* grantFullPermissions: true,
* });
* ```
* @extension ERC7702
*/
export function createSessionKey(
options: BaseTransactionOptions<CreateSessionKeyOptions>,
) {
export function createSessionKey(options: CreateSessionKeyOptions) {
const {
contract,
client,
chain,
account,
sessionKeyAddress,
durationInSeconds,
grantFullPermissions,
callPolicies,
transferPolicies,
durationInSeconds = 86400, // 24 hours default
grantFullPermissions = false,
callPolicies = [],
transferPolicies = [],
} = options;

if (durationInSeconds <= 0) {
throw new Error("durationInSeconds must be positive");
}
// Create the contract using account.address - ABI will be auto-resolved by thirdweb
const contract = getContract({
address: account.address,
chain,
client,
});

return createSessionWithSig({
async asyncParams() {
const req = {
callPolicies: (callPolicies || []).map((policy) => ({
// Create the session spec object directly
const sessionSpec = {
signer: sessionKeyAddress,
isWildcard: grantFullPermissions,
expiresAt: BigInt(Math.floor(Date.now() / 1000) + durationInSeconds),
callPolicies: grantFullPermissions
? []
: callPolicies.map((policy) => ({
target: policy.target,
selector: policy.selector,
maxValuePerUse: policy.maxValuePerUse || 0n,
valueLimit: policy.valueLimit || {
limitType: 0,
limit: 0n,
period: 0n,
},
constraints: (policy.constraints || []).map((constraint) => ({
condition: Number(constraint.condition),
index: constraint.index || BigInt(0),
limit: constraint.limit
? {
limit: constraint.limit.limit,
limitType: Number(constraint.limit.limitType),
period: constraint.limit.period,
}
: {
limit: BigInt(0),
limitType: 0,
period: BigInt(0),
},
refValue: constraint.refValue || "0x",
condition: constraint.condition,
index: constraint.index,
refValue: constraint.refValue,
limit: constraint.limit || {
limitType: 0,
limit: 0n,
period: 0n,
},
})),
maxValuePerUse: policy.maxValuePerUse || BigInt(0),
selector: policy.selector,
target: policy.target,
valueLimit: policy.valueLimit
? {
limit: policy.valueLimit.limit,
limitType: Number(policy.valueLimit.limitType),
period: policy.valueLimit.period,
}
: {
limit: BigInt(0),
limitType: 0,
period: BigInt(0),
},
})),
expiresAt: BigInt(Math.floor(Date.now() / 1000) + durationInSeconds),
isWildcard: grantFullPermissions ?? true,
signer: sessionKeyAddress,
transferPolicies: (transferPolicies || []).map((policy) => ({
maxValuePerUse: policy.maxValuePerUse || BigInt(0),
transferPolicies: grantFullPermissions
? []
: transferPolicies.map((policy) => ({
target: policy.target,
valueLimit: policy.valueLimit
? {
limit: policy.valueLimit.limit,
limitType: Number(policy.valueLimit.limitType),
period: policy.valueLimit.period,
}
: {
limit: BigInt(0),
limitType: 0,
period: BigInt(0),
},
maxValuePerUse: policy.maxValuePerUse || 0n,
valueLimit: policy.valueLimit || {
limitType: 0,
limit: 0n,
period: 0n,
},
})),
uid: await randomBytesHex(),
};
uid: randomBytesHex(32),
};

const signature = await account.signTypedData({
domain: {
chainId: contract.chain.id,
name: "MinimalAccount",
verifyingContract: contract.address,
version: "1",
},
message: req,
primaryType: "SessionSpec",
types: {
CallSpec: CallSpecRequest,
Constraint: ConstraintRequest,
SessionSpec: SessionSpecRequest,
TransferSpec: TransferSpecRequest,
UsageLimit: UsageLimitRequest,
},
});

return { sessionSpec: req, signature };
},
return createSessionWithSig({
contract,
sessionSpec,
signature: "0x",
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ import { beforeAll, describe, expect, it } from "vitest";
import { TEST_CLIENT } from "../../../../test/src/test-clients.js";
import { TEST_ACCOUNT_A } from "../../../../test/src/test-wallets.js";
import { ZERO_ADDRESS } from "../../../constants/addresses.js";
import {
getContract,
type ThirdwebContract,
} from "../../../contract/contract.js";
import { parseEventLogs } from "../../../event/actions/parse-logs.js";
import { sendAndConfirmTransaction } from "../../../transaction/actions/send-and-confirm-transaction.js";
import { sessionCreatedEvent } from "../__generated__/MinimalAccount/events/SessionCreated.js";
Expand All @@ -25,7 +21,6 @@ describe.runIf(process.env.TW_SECRET_KEY)(
() => {
const chainId = 11155111;
let account: Account;
let accountContract: ThirdwebContract;

beforeAll(async () => {
// Create 7702 Smart EOA
Expand All @@ -51,21 +46,15 @@ describe.runIf(process.env.TW_SECRET_KEY)(
value: 0n,
}),
});

// Will auto resolve abi since it's deployed
accountContract = getContract({
address: account.address,
chain: defineChain(chainId),
client: TEST_CLIENT,
});
}, 120_000);

it("should allow adding adminlike session keys", async () => {
const receipt = await sendAndConfirmTransaction({
account: account,
transaction: createSessionKey({
client: TEST_CLIENT,
chain: defineChain(chainId),
account: account,
contract: accountContract,
durationInSeconds: 86400,
grantFullPermissions: true, // 1 day
sessionKeyAddress: TEST_ACCOUNT_A.address,
Expand All @@ -82,6 +71,8 @@ describe.runIf(process.env.TW_SECRET_KEY)(
const receipt = await sendAndConfirmTransaction({
account: account,
transaction: createSessionKey({
client: TEST_CLIENT,
chain: defineChain(chainId),
account: account,
callPolicies: [
{
Expand All @@ -103,7 +94,6 @@ describe.runIf(process.env.TW_SECRET_KEY)(
},
},
],
contract: accountContract,
durationInSeconds: 86400, // 1 day
grantFullPermissions: false,
sessionKeyAddress: TEST_ACCOUNT_A.address,
Expand Down
Loading