@xyyz1207/uptick-aawallet-mpc-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # AA Wallet SDK
2
+
3
+ An ERC-4337 Account Abstraction wallet SDK with a minimal public API.
4
+ This SDK is **only designed for the Uptick Network chain**.
5
+
6
+ Common methods:
7
+
8
+ - `init`: Initialize chain and sponsor configuration (bundler / paymaster / chainId / rpc / apiKey, etc.)
9
+ - `createWallet`: Create and return the AA wallet address (auto-sponsored deployment when needed)
10
+ - `sendTransaction`: Send a transaction with the AA wallet (single call or batched calls)
11
+
12
+ The internal design follows a layered architecture:
13
+
14
+ - `core`: Parameter normalization and input validation
15
+ - `services`: Wallet and transaction flow orchestration
16
+ - `sdk`: External SDK instance API (while keeping default singleton compatibility)
17
+
18
+ ## Installation & Import
19
+
20
+ ```bash
21
+ npm install @uptickjs/aa-wallet-sdk
22
+ ```
23
+
24
+ ```js
25
+ import { init, createWallet, sendTransaction } from '@uptickjs/aa-wallet-sdk';
26
+ import { encodeFunctionData } from 'viem';
27
+ ```
28
+
29
+ ## API
30
+
31
+ ### 1) `init(config)`
32
+
33
+ ```js
34
+ init({
35
+ chainId: 9000,
36
+ bundlerUrl: 'https://...',
37
+ paymasterServiceUrl: 'https://...',
38
+ bundlerApiKey: '',
39
+ paymasterApiKey: '',
40
+ });
41
+ ```
42
+
43
+ ### 2) `createWallet(params)`
44
+
45
+ ```js
46
+ const wallet = await createWallet({
47
+ ownerPrivateKey: '0x...',
48
+ });
49
+ ```
50
+
51
+ Return example:
52
+
53
+ ```js
54
+ {
55
+ ownerAddress: "0x...",
56
+ aaWalletAddress: "0x...",
57
+ deployedOnChain: true,
58
+ deployTxHash: "0x..." // null if deployment is not triggered
59
+ }
60
+ ```
61
+
62
+ ### 3) `sendTransaction(params)`
63
+
64
+ ```js
65
+ const tx = await sendTransaction({
66
+ ownerPrivateKey: '0x...',
67
+ to: '0x...',
68
+ value: 0n,
69
+ data: '0x',
70
+ });
71
+
72
+ const data = encodeFunctionData({
73
+ abi: loadAbi,
74
+ functionName: 'transfer',
75
+ args: [params1,params2...],
76
+ });
77
+
78
+ const tx = await sendTransaction({
79
+ ownerPrivateKey: '0x...',
80
+ to: contractAddress,
81
+ value: 0n,
82
+ data: data,
83
+ });
84
+ ```
package/package.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "@xyyz1207/uptick-aawallet-mpc-sdk",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "main": "src/index.js",
6
+ "exports": {
7
+ ".": "./src/index.js"
8
+ },
9
+ "keywords": [],
10
+ "author": "",
11
+ "license": "ISC",
12
+ "description": "",
13
+ "dependencies": {
14
+ "dotenv": "^17.4.2",
15
+ "permissionless": "^0.3.5",
16
+ "viem": "^2.48.1"
17
+ }
18
+ }
@@ -0,0 +1,70 @@
1
+ const COMMON_AA_PRESET = {
2
+ accountType: "safe",
3
+ safeVersion: "1.5.0",
4
+ entryPointVersion: "0.7",
5
+ };
6
+
7
+ /**
8
+ * 按 chainId 维护默认参数,调用方可在 init(config) 里覆盖。
9
+ */
10
+ export const CHAIN_PRESETS = {
11
+ 9000: {
12
+ chain: {
13
+ id: 9000,
14
+ name: "Uptick test",
15
+ rpcUrl: "http://54.254.15.166:10545",
16
+ },
17
+ aa: {
18
+ ...COMMON_AA_PRESET,
19
+ entryPointAddress: "0x0000000071727De22E5E9d8BAf0edAc6f37da032",
20
+ factoryAddress: "0x91E60e0613810449d098b0b5Ec8b51A0FE8c8985",
21
+ safeProxyFactoryAddress: "0x34aeE8688B23516f9cD8F145D52D5a13080028D2",
22
+ safeSingletonAddress: "0x59CAB03C911eF5Ab4590Bb6c4F00B768C10F09D8",
23
+ safe4337ModuleAddress: "0x75cf11467937ce3F2f357CE24ffc3DBF8fD5c226",
24
+ safeModuleSetupAddress: "0x2dd68b007B46fBe91B9A7c3EDa5A7a1063cB5b47",
25
+ multiSendAddress: "0x4faF5C1F98B09F1494bDaf93c85E2A05FbC1e1Bd",
26
+ multiSendCallOnlyAddress: "0x756E377D1dcDC33bD973216E64D32bec6aB4b569",
27
+ },
28
+ deployment: {
29
+ enabled: true,
30
+ paymasterAddress: "0x80b00Dce53C4F717aECEb84881F4f8beA099189C",
31
+ paymasterVerificationGasLimit: "100000",
32
+ paymasterPostOpGasLimit: "50000",
33
+ gasLimitMultiplier: 120,
34
+ feeMultiplier: 130,
35
+ debug: false,
36
+ },
37
+ },
38
+ 1170: {
39
+ chain: {
40
+ id: 1170,
41
+ name: "Uptick test",
42
+ rpcUrl: "https://json-rpc.origin.uptick.network",
43
+ },
44
+ aa: {
45
+ ...COMMON_AA_PRESET,
46
+ entryPointAddress: "0x0000000071727De22E5E9d8BAf0edAc6f37da032",
47
+ factoryAddress: "0x91E60e0613810449d098b0b5Ec8b51A0FE8c8985",
48
+ safeProxyFactoryAddress: "0x34aeE8688B23516f9cD8F145D52D5a13080028D2",
49
+ safeSingletonAddress: "0x59CAB03C911eF5Ab4590Bb6c4F00B768C10F09D8",
50
+ safe4337ModuleAddress: "0x75cf11467937ce3F2f357CE24ffc3DBF8fD5c226",
51
+ safeModuleSetupAddress: "0x2dd68b007B46fBe91B9A7c3EDa5A7a1063cB5b47",
52
+ multiSendAddress: "0x4faF5C1F98B09F1494bDaf93c85E2A05FbC1e1Bd",
53
+ multiSendCallOnlyAddress: "0x756E377D1dcDC33bD973216E64D32bec6aB4b569",
54
+ },
55
+ deployment: {
56
+ enabled: true,
57
+ paymasterAddress: "0x450AeF871cc771D4F0B7813f2BBef9a8c77611F7",
58
+ paymasterVerificationGasLimit: "100000",
59
+ paymasterPostOpGasLimit: "50000",
60
+ gasLimitMultiplier: 120,
61
+ feeMultiplier: 130,
62
+ debug: false,
63
+ },
64
+ },
65
+ };
66
+
67
+ export function getChainPreset(chainId) {
68
+ if (chainId == null) return null;
69
+ return CHAIN_PRESETS[Number(chainId)] ?? null;
70
+ }
@@ -0,0 +1,159 @@
1
+ import { getChainPreset } from "./chain-presets.js";
2
+
3
+ function pickDefined(value, fallback) {
4
+ return value === undefined ? fallback : value;
5
+ }
6
+
7
+ export function normalizeSdkConfig(config) {
8
+ if (!config) throw new Error("init(config) 参数不能为空");
9
+ if (!config.chainId) throw new Error("缺少必填参数 chainId");
10
+
11
+ const preset = getChainPreset(config.chainId);
12
+ const chainId = Number(config.chainId);
13
+
14
+ const chain = {
15
+ id: chainId,
16
+ name: pickDefined(config.chainName, preset?.chain?.name ?? "AA Chain"),
17
+ rpcUrl: pickDefined(config.rpcUrl, preset?.chain?.rpcUrl),
18
+ };
19
+ if (!chain.rpcUrl) throw new Error(`chainId=${chainId} 缺少 rpcUrl 配置`);
20
+
21
+ const aa = {
22
+ ...(preset?.aa ?? {}),
23
+ ...(config.aa ?? {}),
24
+ accountType: pickDefined(
25
+ config.accountType,
26
+ pickDefined(config.aa?.accountType, preset?.aa?.accountType)
27
+ ),
28
+ safeVersion: pickDefined(
29
+ config.safeVersion,
30
+ pickDefined(config.aa?.safeVersion, preset?.aa?.safeVersion)
31
+ ),
32
+ entryPointVersion: pickDefined(
33
+ config.entryPointVersion,
34
+ pickDefined(config.aa?.entryPointVersion, preset?.aa?.entryPointVersion)
35
+ ),
36
+ entryPointAddress: pickDefined(
37
+ config.entryPointAddress,
38
+ pickDefined(config.aa?.entryPointAddress, preset?.aa?.entryPointAddress)
39
+ ),
40
+ factoryAddress: pickDefined(
41
+ config.factoryAddress,
42
+ pickDefined(config.aa?.factoryAddress, preset?.aa?.factoryAddress)
43
+ ),
44
+ safeProxyFactoryAddress: pickDefined(
45
+ config.safeProxyFactoryAddress,
46
+ pickDefined(
47
+ config.aa?.safeProxyFactoryAddress,
48
+ preset?.aa?.safeProxyFactoryAddress
49
+ )
50
+ ),
51
+ safeSingletonAddress: pickDefined(
52
+ config.safeSingletonAddress,
53
+ pickDefined(config.aa?.safeSingletonAddress, preset?.aa?.safeSingletonAddress)
54
+ ),
55
+ safe4337ModuleAddress: pickDefined(
56
+ config.safe4337ModuleAddress,
57
+ pickDefined(
58
+ config.aa?.safe4337ModuleAddress,
59
+ preset?.aa?.safe4337ModuleAddress
60
+ )
61
+ ),
62
+ safeModuleSetupAddress: pickDefined(
63
+ config.safeModuleSetupAddress,
64
+ pickDefined(config.aa?.safeModuleSetupAddress, preset?.aa?.safeModuleSetupAddress)
65
+ ),
66
+ multiSendAddress: pickDefined(
67
+ config.multiSendAddress,
68
+ pickDefined(config.aa?.multiSendAddress, preset?.aa?.multiSendAddress)
69
+ ),
70
+ multiSendCallOnlyAddress: pickDefined(
71
+ config.multiSendCallOnlyAddress,
72
+ pickDefined(
73
+ config.aa?.multiSendCallOnlyAddress,
74
+ preset?.aa?.multiSendCallOnlyAddress
75
+ )
76
+ ),
77
+ };
78
+
79
+ const deployment = {
80
+ ...(preset?.deployment ?? {}),
81
+ ...(config.deployment ?? {}),
82
+ enabled: pickDefined(
83
+ config.deploymentEnabled,
84
+ pickDefined(config.deployment?.enabled, preset?.deployment?.enabled ?? true)
85
+ ),
86
+ bundlerUrl: pickDefined(
87
+ config.bundlerUrl,
88
+ config.deployment?.bundlerUrl
89
+ ),
90
+ bundlerApiKey: pickDefined(
91
+ config.bundlerApiKey,
92
+ config.deployment?.bundlerApiKey
93
+ ),
94
+ paymasterServiceUrl: pickDefined(
95
+ config.paymasterServiceUrl,
96
+ config.deployment?.paymasterServiceUrl
97
+ ),
98
+ paymasterAddress: pickDefined(
99
+ config.paymasterAddress,
100
+ pickDefined(config.deployment?.paymasterAddress, preset?.deployment?.paymasterAddress)
101
+ ),
102
+ paymasterApiKey: pickDefined(
103
+ config.paymasterApiKey,
104
+ config.deployment?.paymasterApiKey
105
+ ),
106
+ paymasterVerificationGasLimit: pickDefined(
107
+ config.paymasterVerificationGasLimit,
108
+ pickDefined(
109
+ config.deployment?.paymasterVerificationGasLimit,
110
+ preset?.deployment?.paymasterVerificationGasLimit
111
+ )
112
+ ),
113
+ paymasterPostOpGasLimit: pickDefined(
114
+ config.paymasterPostOpGasLimit,
115
+ pickDefined(
116
+ config.deployment?.paymasterPostOpGasLimit,
117
+ preset?.deployment?.paymasterPostOpGasLimit
118
+ )
119
+ ),
120
+ gasLimitMultiplier: pickDefined(
121
+ config.gasLimitMultiplier,
122
+ pickDefined(
123
+ config.deployment?.gasLimitMultiplier,
124
+ preset?.deployment?.gasLimitMultiplier
125
+ )
126
+ ),
127
+ feeMultiplier: pickDefined(
128
+ config.feeMultiplier,
129
+ pickDefined(config.deployment?.feeMultiplier, preset?.deployment?.feeMultiplier)
130
+ ),
131
+ debug: pickDefined(
132
+ config.deployDebug,
133
+ pickDefined(config.deployment?.debug, preset?.deployment?.debug ?? false)
134
+ ),
135
+ };
136
+
137
+ if (!deployment.bundlerUrl) {
138
+ throw new Error("缺少必填参数 bundlerUrl");
139
+ }
140
+ if (!deployment.paymasterServiceUrl) {
141
+ throw new Error("缺少必填参数 paymasterServiceUrl");
142
+ }
143
+
144
+ return { chain, aa, deployment };
145
+ }
146
+
147
+ export function normalizeCalls({ to, value = 0n, data = '0x', calls }) {
148
+ const normalizedCalls =
149
+ calls && calls.length
150
+ ? calls
151
+ : to
152
+ ? [{ to, value: BigInt(value), data }]
153
+ : null;
154
+
155
+ if (!normalizedCalls) {
156
+ throw new Error('sendTransaction 需要传入 calls 或 to');
157
+ }
158
+ return normalizedCalls;
159
+ }
package/src/index.js ADDED
@@ -0,0 +1,32 @@
1
+ import { createAaWalletSdk } from "./sdk.js";
2
+
3
+ export { AaWalletSdk, createAaWalletSdk } from "./sdk.js";
4
+ export {
5
+ createMpcViemLocalAccount,
6
+ createMpcOwnerFromRest,
7
+ } from "./utils/aa-mpc.js";
8
+ export { mpcGetWallet, mpcSignHash } from "./utils/mpc-client.js";
9
+ export { createAaSmartAccountFromOwner } from "./utils/aa-wallet.js";
10
+
11
+ const defaultSdk = createAaWalletSdk();
12
+
13
+ /**
14
+ * 兼容模式:默认单例 init
15
+ */
16
+ export function init(config) {
17
+ defaultSdk.init(config);
18
+ }
19
+
20
+ /**
21
+ * 兼容模式:默认单例 createWallet
22
+ */
23
+ export async function createWallet(params) {
24
+ return defaultSdk.createWallet(params);
25
+ }
26
+
27
+ /**
28
+ * 兼容模式:默认单例 sendTransaction
29
+ */
30
+ export async function sendTransaction(params) {
31
+ return defaultSdk.sendTransaction(params);
32
+ }
package/src/sdk.js ADDED
@@ -0,0 +1,89 @@
1
+ import { normalizeCalls, normalizeSdkConfig } from './core/config.js';
2
+ import {
3
+ buildWalletContext,
4
+ ensureWalletDeployed,
5
+ } from './services/wallet-service.js';
6
+ import { sendCallsBySmartAccount } from './services/transaction-service.js';
7
+ import { shouldAttemptSponsoredDeploy } from './utils/aa-deploy.js';
8
+
9
+ export class AaWalletSdk {
10
+ constructor() {
11
+ this.config = null;
12
+ }
13
+
14
+ init(config) {
15
+ console.log('init 15', config);
16
+ this.config = normalizeSdkConfig(config);
17
+ }
18
+
19
+ getConfig() {
20
+ if (!this.config) {
21
+ throw new Error('SDK 未初始化,请先调用 init(config)');
22
+ }
23
+ return this.config;
24
+ }
25
+
26
+ async createWallet({ ownerPrivateKey, owner, index = 0 } = {}) {
27
+ const config = this.getConfig();
28
+ const {
29
+ publicClient,
30
+ owner: resolvedOwner,
31
+ smartAccount,
32
+ } = await buildWalletContext(config, { ownerPrivateKey, owner, index });
33
+ const { deployedOnChain, deployTxHash } = await ensureWalletDeployed(
34
+ config,
35
+ publicClient,
36
+ smartAccount,
37
+ );
38
+
39
+ return {
40
+ ownerAddress: resolvedOwner.address,
41
+ aaWalletAddress: smartAccount.address,
42
+ deployedOnChain,
43
+ deployTxHash,
44
+ };
45
+ }
46
+
47
+ async sendTransaction({
48
+ ownerPrivateKey,
49
+ owner,
50
+ index = 0,
51
+ to,
52
+ value = 0n,
53
+ data = '0x',
54
+ calls,
55
+ } = {}) {
56
+ const config = this.getConfig();
57
+ if (!shouldAttemptSponsoredDeploy(config.deployment)) {
58
+ throw new Error('缺少 bundler/paymaster 配置,无法发送赞助交易');
59
+ }
60
+ const normalizedCalls = normalizeCalls({ to, value, data, calls });
61
+ const {
62
+ publicClient,
63
+ owner: resolvedOwner,
64
+ smartAccount,
65
+ } = await buildWalletContext(config, { ownerPrivateKey, owner, index });
66
+ const { deployedOnChain } = await ensureWalletDeployed(
67
+ config,
68
+ publicClient,
69
+ smartAccount,
70
+ );
71
+ const txHash = await sendCallsBySmartAccount({
72
+ config,
73
+ publicClient,
74
+ smartAccount,
75
+ calls: normalizedCalls,
76
+ });
77
+
78
+ return {
79
+ ownerAddress: resolvedOwner.address,
80
+ aaWalletAddress: smartAccount.address,
81
+ deployedOnChain,
82
+ txHash,
83
+ };
84
+ }
85
+ }
86
+
87
+ export function createAaWalletSdk() {
88
+ return new AaWalletSdk();
89
+ }
@@ -0,0 +1,17 @@
1
+ import { sendSponsoredAaCallViaPaymaster } from "../utils/aa-deploy.js";
2
+
3
+ export async function sendCallsBySmartAccount({
4
+ config,
5
+ publicClient,
6
+ smartAccount,
7
+ calls,
8
+ }) {
9
+ const callData = await smartAccount.encodeCalls(calls);
10
+ const txHash = await sendSponsoredAaCallViaPaymaster({
11
+ publicClient,
12
+ smartAccount,
13
+ deployment: config.deployment,
14
+ callData,
15
+ });
16
+ return txHash;
17
+ }
@@ -0,0 +1,65 @@
1
+ import {
2
+ createAaPublicClient,
3
+ createAaSmartAccountFromOwner,
4
+ createAaWallet,
5
+ createChainFromConfig,
6
+ } from "../utils/aa-wallet.js";
7
+ import {
8
+ deployAaWalletViaPaymaster,
9
+ shouldAttemptSponsoredDeploy,
10
+ } from "../utils/aa-deploy.js";
11
+
12
+ /**
13
+ * @param {object} config
14
+ * @param {{ ownerPrivateKey?: `0x${string}`; owner?: import("viem/accounts").LocalAccount; index?: number }} options
15
+ */
16
+ export async function buildWalletContext(config, options) {
17
+ const index = options.index ?? 0;
18
+ const { ownerPrivateKey, owner } = options;
19
+
20
+ if (!ownerPrivateKey && !owner) {
21
+ throw new Error("缺少 ownerPrivateKey 或 owner(例如 MPC LocalAccount)");
22
+ }
23
+ if (ownerPrivateKey && owner) {
24
+ throw new Error("请勿同时传入 ownerPrivateKey 与 owner");
25
+ }
26
+
27
+ const publicClient = createAaPublicClient(config.chain);
28
+ const { owner: resolvedOwner, smartAccount } = owner
29
+ ? await createAaSmartAccountFromOwner({
30
+ publicClient,
31
+ owner,
32
+ index,
33
+ aa: config.aa,
34
+ })
35
+ : await createAaWallet({
36
+ publicClient,
37
+ privateKey: /** @type {`0x${string}`} */ (ownerPrivateKey),
38
+ index,
39
+ aa: config.aa,
40
+ });
41
+
42
+ return { publicClient, owner: resolvedOwner, smartAccount };
43
+ }
44
+
45
+ export async function ensureWalletDeployed(
46
+ config,
47
+ publicClient,
48
+ smartAccount
49
+ ) {
50
+ let deployedOnChain = await smartAccount.isDeployed();
51
+ let deployTxHash = null;
52
+
53
+ if (!deployedOnChain && shouldAttemptSponsoredDeploy(config.deployment)) {
54
+ const chain = createChainFromConfig(config.chain);
55
+ deployTxHash = await deployAaWalletViaPaymaster({
56
+ publicClient,
57
+ chain,
58
+ smartAccount,
59
+ deployment: config.deployment,
60
+ });
61
+ deployedOnChain = await smartAccount.isDeployed();
62
+ }
63
+
64
+ return { deployedOnChain, deployTxHash };
65
+ }
@@ -0,0 +1,159 @@
1
+ import { encodeFunctionData } from "viem";
2
+ import { createAaWallet, createAaPublicClient } from "./aa-wallet.js";
3
+ import {
4
+ deployAaWalletViaPaymaster,
5
+ sendSponsoredAaCallViaPaymaster,
6
+ shouldAttemptSponsoredDeploy,
7
+ } from "./aa-deploy.js";
8
+
9
+ const erc20Abi = [
10
+ {
11
+ type: "function",
12
+ name: "approve",
13
+ stateMutability: "nonpayable",
14
+ inputs: [
15
+ { name: "spender", type: "address" },
16
+ { name: "value", type: "uint256" },
17
+ ],
18
+ outputs: [{ name: "", type: "bool" }],
19
+ },
20
+ {
21
+ type: "function",
22
+ name: "transfer",
23
+ stateMutability: "nonpayable",
24
+ inputs: [
25
+ { name: "to", type: "address" },
26
+ { name: "value", type: "uint256" },
27
+ ],
28
+ outputs: [{ name: "", type: "bool" }],
29
+ },
30
+ ];
31
+
32
+ /**
33
+ * 通过 AA 钱包调用目标合约(默认通过 paymaster 代付)。
34
+ *
35
+ * @param {object} params
36
+ * @param {object} params.config 与运行时配置结构一致
37
+ * @param {number} [params.accountIndex=0] 使用第几个 owner 账户
38
+ * @param {{ to: `0x${string}`; value?: bigint; data?: `0x${string}` }[]} params.calls
39
+ * @returns {Promise<{ ownerAddress: `0x${string}`; aaWalletAddress: `0x${string}`; txHash: `0x${string}`; deployedOnChain: boolean }>}
40
+ */
41
+ export async function callContractByAaWallet({
42
+ config,
43
+ accountIndex = 0,
44
+ calls,
45
+ }) {
46
+ const account = config.accounts?.[accountIndex];
47
+ if (!account) {
48
+ throw new Error(`accounts[${accountIndex}] 不存在`);
49
+ }
50
+
51
+ if (!shouldAttemptSponsoredDeploy(config.deployment)) {
52
+ throw new Error("deployment 未启用或缺少 bundler/paymaster 配置");
53
+ }
54
+
55
+ const publicClient = createAaPublicClient(config.chain);
56
+ const { owner, smartAccount } = await createAaWallet({
57
+ publicClient,
58
+ privateKey: account.privateKey,
59
+ index: account.index,
60
+ aa: config.aa,
61
+ });
62
+
63
+ let deployedOnChain = await smartAccount.isDeployed();
64
+ if (!deployedOnChain) {
65
+ await deployAaWalletViaPaymaster({
66
+ publicClient,
67
+ smartAccount,
68
+ deployment: config.deployment,
69
+ });
70
+ deployedOnChain = await smartAccount.isDeployed();
71
+ }
72
+
73
+ const callData = await smartAccount.encodeCalls(calls);
74
+ const txHash = await sendSponsoredAaCallViaPaymaster({
75
+ publicClient,
76
+ smartAccount,
77
+ deployment: config.deployment,
78
+ callData,
79
+ });
80
+
81
+ return {
82
+ ownerAddress: owner.address,
83
+ aaWalletAddress: smartAccount.address,
84
+ txHash,
85
+ deployedOnChain,
86
+ };
87
+ }
88
+
89
+ /**
90
+ * AA 钱包执行 ERC20 transfer。
91
+ *
92
+ * @param {object} params
93
+ * @param {object} params.config 与运行时配置结构一致
94
+ * @param {`0x${string}`} params.tokenAddress
95
+ * @param {`0x${string}`} params.to
96
+ * @param {bigint} params.amount
97
+ * @param {number} [params.accountIndex=0]
98
+ */
99
+ export async function transferErc20ByAaWallet({
100
+ config,
101
+ tokenAddress,
102
+ to,
103
+ amount,
104
+ accountIndex = 0,
105
+ }) {
106
+ const data = encodeFunctionData({
107
+ abi: erc20Abi,
108
+ functionName: "transfer",
109
+ args: [to, amount],
110
+ });
111
+
112
+ return callContractByAaWallet({
113
+ config,
114
+ accountIndex,
115
+ calls: [
116
+ {
117
+ to: tokenAddress,
118
+ value: 0n,
119
+ data,
120
+ },
121
+ ],
122
+ });
123
+ }
124
+
125
+ /**
126
+ * AA 钱包执行 ERC20 approve。
127
+ *
128
+ * @param {object} params
129
+ * @param {object} params.config 与运行时配置结构一致
130
+ * @param {`0x${string}`} params.tokenAddress
131
+ * @param {`0x${string}`} params.spender
132
+ * @param {bigint} params.amount
133
+ * @param {number} [params.accountIndex=0]
134
+ */
135
+ export async function approveErc20ByAaWallet({
136
+ config,
137
+ tokenAddress,
138
+ spender,
139
+ amount,
140
+ accountIndex = 0,
141
+ }) {
142
+ const data = encodeFunctionData({
143
+ abi: erc20Abi,
144
+ functionName: "approve",
145
+ args: [spender, amount],
146
+ });
147
+
148
+ return callContractByAaWallet({
149
+ config,
150
+ accountIndex,
151
+ calls: [
152
+ {
153
+ to: tokenAddress,
154
+ value: 0n,
155
+ data,
156
+ },
157
+ ],
158
+ });
159
+ }