Skip to content

Commit 305edf2

Browse files
working version
1 parent 339687d commit 305edf2

File tree

8 files changed

+228
-138
lines changed

8 files changed

+228
-138
lines changed

packages/thirdweb/src/wallets/smart/index.ts

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,7 @@ import type {
3232
SendTransactionOption,
3333
Wallet,
3434
} from "../interfaces/wallet.js";
35-
import type {
36-
CreateWalletArgs,
37-
WalletConnectionOption,
38-
WalletId,
39-
} from "../wallet-types.js";
35+
import type { WalletId } from "../wallet-types.js";
4036
import {
4137
broadcastZkTransaction,
4238
bundleUserOp,
@@ -91,8 +87,8 @@ const smartWalletToPersonalAccountMap = new WeakMap<Wallet<"smart">, Account>();
9187
*/
9288
export async function connectSmartWallet(
9389
wallet: Wallet<"smart">,
94-
connectionOptions: WalletConnectionOption<"smart">,
95-
creationOptions: CreateWalletArgs<"smart">[1],
90+
connectionOptions: SmartWalletConnectionOptions,
91+
creationOptions: SmartWalletOptions,
9692
): Promise<[Account, Chain]> {
9793
const { personalAccount, client, chain: connectChain } = connectionOptions;
9894

@@ -116,7 +112,6 @@ export async function connectSmartWallet(
116112
entrypointAddress,
117113
};
118114
}
119-
console.log("entrypointAddress", entrypointAddress);
120115
}
121116

122117
const factoryAddress =
@@ -159,8 +154,6 @@ export async function connectSmartWallet(
159154
);
160155
});
161156

162-
console.log("accountAddress", accountAddress);
163-
164157
const accountContract = getContract({
165158
client,
166159
address: accountAddress,
@@ -331,7 +324,7 @@ async function createSmartAccount(
331324
message: { message: wrappedMessageHash },
332325
});
333326
if (isModularFactory) {
334-
// add validator address
327+
// TODO (msa) - override for signatures to add validator address
335328
sig = encodePacked(
336329
["address", "bytes"],
337330
["0x6DF8ea6FF6Ca55f367CDA45510CA40dC78993DEC", sig],
@@ -341,8 +334,6 @@ async function createSmartAccount(
341334
sig = await options.personalAccount.signMessage({ message });
342335
}
343336

344-
console.log("sig", sig);
345-
346337
const isValid = await verifyContractWalletSignature({
347338
address: accountContract.address,
348339
chain: accountContract.chain,
@@ -667,7 +658,7 @@ async function _sendUserOp(args: {
667658
};
668659
}
669660

670-
async function confirmContractDeployment(args: {
661+
export async function confirmContractDeployment(args: {
671662
accountContract: ThirdwebContract;
672663
}) {
673664
const { accountContract } = args;

packages/thirdweb/src/wallets/smart/lib/calls.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export async function predictAddress(args: {
6666
factoryContract: ThirdwebContract;
6767
predictAddressOverride?: (
6868
factoryContract: ThirdwebContract,
69+
admin: string,
6970
) => Promise<string>;
7071
adminAddress: string;
7172
accountSalt?: string;
@@ -79,7 +80,7 @@ export async function predictAddress(args: {
7980
accountAddress,
8081
} = args;
8182
if (predictAddress) {
82-
return predictAddress(factoryContract);
83+
return predictAddress(factoryContract, adminAddress);
8384
}
8485
if (accountAddress) {
8586
return accountAddress;
@@ -109,6 +110,7 @@ export function prepareCreateAccount(args: {
109110
accountSalt?: string;
110111
createAccountOverride?: (
111112
factoryContract: ThirdwebContract,
113+
admin: string,
112114
) => PreparedTransaction;
113115
}): PreparedTransaction {
114116
const {
@@ -118,7 +120,7 @@ export function prepareCreateAccount(args: {
118120
accountSalt,
119121
} = args;
120122
if (createAccount) {
121-
return createAccount(factoryContract);
123+
return createAccount(factoryContract, adminAddress);
122124
}
123125
const saltHex =
124126
accountSalt && isHex(accountSalt)

packages/thirdweb/src/wallets/smart/lib/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { getAddress } from "../../../utils/address.js";
33
import { getThirdwebDomains } from "../../../utils/domains.js";
44

55
// dev only
6-
export const DEBUG = true;
6+
export const DEBUG = false;
77

88
export const DUMMY_SIGNATURE =
99
"0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c";

packages/thirdweb/src/wallets/smart/lib/userop.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ async function populateUserOp_v0_6(args: {
412412
sponsorGas: boolean;
413413
overrides?: SmartWalletOptions["overrides"];
414414
isDeployed: boolean;
415-
nonce: bigint | Hex;
415+
nonce: bigint;
416416
callData: Hex;
417417
callGasLimit?: bigint;
418418
maxFeePerGas: bigint;
@@ -609,6 +609,7 @@ async function getAccountInitCode(options: {
609609
accountSalt?: string;
610610
createAccountOverride?: (
611611
factoryContract: ThirdwebContract,
612+
adminAddress: string,
612613
) => PreparedTransaction;
613614
}): Promise<Hex> {
614615
const { factoryContract, adminAddress, accountSalt, createAccountOverride } =
@@ -657,7 +658,6 @@ async function getAccountNonce(options: {
657658
* @example
658659
* ```ts
659660
* import { createAndSignUserOp } from "thirdweb/wallets/smart";
660-
import { keccak256 } from "../../../utils/hashing/keccak256.js";
661661
*
662662
* const userOp = await createAndSignUserOp({
663663
* client,
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import { AbiParameters, Hash, Hex } from "ox";
2+
import { ZERO_ADDRESS } from "../../../constants/addresses.js";
3+
import { getContract } from "../../../contract/contract.js";
4+
import { getNonce } from "../../../extensions/erc4337/__generated__/IEntryPoint/read/getNonce.js";
5+
import { encodeSingleInitMSA } from "../../../extensions/erc7579/__generated__/Bootstrap/write/singleInitMSA.js";
6+
import { execute } from "../../../extensions/erc7579/__generated__/IERC7579Account/write/execute.js";
7+
import { prepareContractCall } from "../../../transaction/prepare-contract-call.js";
8+
import { readContract } from "../../../transaction/read-contract.js";
9+
import { getAddress } from "../../../utils/address.js";
10+
import { toHex } from "../../../utils/encoding/hex.js";
11+
import { ENTRYPOINT_ADDRESS_v0_7 } from "../lib/constants.js";
12+
import { generateRandomUint192 } from "../lib/utils.js";
13+
import type { SmartWalletOptions } from "../types.js";
14+
15+
export type ModuleType = "validator" | "executor" | "fallback" | "hook";
16+
export type Module = {
17+
address: string;
18+
installData?: Hex.Hex;
19+
uninstallData?: Hex.Hex;
20+
};
21+
22+
export type Erc7579Options = SmartWalletOptions & {
23+
defaultModules?: {
24+
validator: (admin: string) => Promise<Module>;
25+
};
26+
};
27+
28+
// TODO (msa) - actual addresses here instead of test ones
29+
const REFERENCE_BOOTSTRAP_ADDRESS =
30+
"0xedd4503de72bac321dfeb65f1373d2def17403fc";
31+
const REFERENCE_FACTORY_ADDRESS = "0xC7c2a0aC7334f84bAe0EB1c4e42526FB6ea2e661"; //"0xa951A1179bA8bd08b8140aB9dc7910AF08AE7181";
32+
const REFERENCE_VALIDATOR_ADDRESS =
33+
"0x6DF8ea6FF6Ca55f367CDA45510CA40dC78993DEC";
34+
35+
export function erc7579Config(options: Erc7579Options): SmartWalletOptions {
36+
return {
37+
...options,
38+
factoryAddress: options.factoryAddress || REFERENCE_FACTORY_ADDRESS,
39+
overrides: {
40+
entrypointAddress: ENTRYPOINT_ADDRESS_v0_7,
41+
createAccount(factoryContract, admin) {
42+
return prepareContractCall({
43+
contract: factoryContract,
44+
// TODO (msa) - adapt to our own 7579 factory
45+
method: "function createAccount(bytes32 salt, bytes initCode)",
46+
async params() {
47+
return createAccountParams({
48+
defaultModules: options.defaultModules,
49+
admin,
50+
});
51+
},
52+
});
53+
},
54+
async predictAddress(factoryContract, admin) {
55+
return readContract({
56+
contract: factoryContract,
57+
method:
58+
"function getAddress(bytes32 salt, bytes initCode) returns (address)",
59+
// TODO (msa) - adapt to our own 7579 factory
60+
async params() {
61+
return createAccountParams({
62+
defaultModules: options.defaultModules,
63+
admin,
64+
});
65+
},
66+
});
67+
},
68+
execute(accountContract, transaction) {
69+
return execute({
70+
contract: accountContract,
71+
async asyncParams() {
72+
return {
73+
mode: Hex.padRight("0x00", 32), // single execution
74+
executionCalldata: AbiParameters.encodePacked(
75+
["address", "uint256", "bytes"],
76+
[
77+
transaction.to || ZERO_ADDRESS,
78+
transaction.value || 0n,
79+
transaction.data || "0x",
80+
],
81+
),
82+
};
83+
},
84+
});
85+
},
86+
executeBatch(accountContract, transactions) {
87+
return execute({
88+
contract: accountContract,
89+
async asyncParams() {
90+
return {
91+
mode: Hex.padRight("0x01", 32), // batch execution
92+
executionCalldata: AbiParameters.encode(
93+
[
94+
{
95+
type: "tuple[]",
96+
components: [
97+
{ type: "address", name: "to" },
98+
{ type: "uint256", name: "value" },
99+
{ type: "bytes", name: "data" },
100+
],
101+
},
102+
],
103+
[
104+
transactions.map((t) => ({
105+
to: t.to || ZERO_ADDRESS,
106+
value: t.value || 0n,
107+
data: t.data || "0x",
108+
})),
109+
],
110+
),
111+
};
112+
},
113+
});
114+
},
115+
async getAccountNonce(accountContract) {
116+
const nonce = await getNonce({
117+
contract: getContract({
118+
address: ENTRYPOINT_ADDRESS_v0_7,
119+
chain: accountContract.chain,
120+
client: accountContract.client,
121+
}),
122+
key: generateRandomUint192(),
123+
sender: accountContract.address,
124+
});
125+
const withValidator = `${REFERENCE_VALIDATOR_ADDRESS}${toHex(nonce).slice(42)}`;
126+
return Hex.toBigInt(withValidator as Hex.Hex);
127+
},
128+
...options.overrides,
129+
},
130+
};
131+
}
132+
133+
async function createAccountParams(args: {
134+
defaultModules: Erc7579Options["defaultModules"];
135+
admin: string;
136+
}): Promise<[Hex.Hex, Hex.Hex]> {
137+
const { defaultModules, admin } = args;
138+
const customValidator = defaultModules
139+
? await defaultModules.validator(admin)
140+
: undefined;
141+
return [
142+
Hash.keccak256(getAddress(admin)),
143+
AbiParameters.encode(
144+
[{ type: "address" }, { type: "bytes" }],
145+
[
146+
REFERENCE_BOOTSTRAP_ADDRESS, // bootstrap
147+
encodeSingleInitMSA(
148+
customValidator
149+
? {
150+
validator: getAddress(customValidator.address),
151+
data: customValidator.installData || "0x",
152+
}
153+
: {
154+
validator: REFERENCE_VALIDATOR_ADDRESS,
155+
data: "0x",
156+
},
157+
),
158+
],
159+
),
160+
];
161+
}

0 commit comments

Comments
 (0)