@getpara/aa-porto 2.16.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.
@@ -0,0 +1,13 @@
1
+ import type { SmartAccount7702 } from '@getpara/viem-v2-integration';
2
+ import type { CreatePortoSmartAccountParams } from './types.js';
3
+ export type { CreatePortoSmartAccountParams } from './types.js';
4
+ /**
5
+ * Creates a Porto smart account by upgrading a Para EOA to a Porto 7702 smart account.
6
+ *
7
+ * Porto uses EIP-7702 to delegate the EOA to a smart account contract, enabling
8
+ * session keys, transaction batching, and gas sponsorship while keeping the same address.
9
+ *
10
+ * @param params - Configuration parameters
11
+ * @returns Unified SmartAccount interface wrapping Porto account, or null if no EVM wallet exists
12
+ */
13
+ export declare function createPortoSmartAccount(params: CreatePortoSmartAccountParams): Promise<SmartAccount7702 | null>;
package/dist/action.js ADDED
@@ -0,0 +1,165 @@
1
+ import { Account, Key, RelayActions } from "porto/viem";
2
+ import { createClient, createPublicClient, http } from "viem";
3
+ import { createParaAccount } from "@getpara/viem-v2-integration";
4
+ import { wrapProviderError, resolveWalletIdentifier } from "@getpara/viem-v2-integration";
5
+ const PORTO_RELAY_RPC = "https://rpc.porto.sh";
6
+ const PORTO_POLL_MAX_ATTEMPTS = 30;
7
+ const PORTO_POLL_INTERVAL_MS = 2e3;
8
+ const PORTO_UPGRADE_POLL_MAX = 6;
9
+ const PORTO_UPGRADE_POLL_MS = 500;
10
+ async function sendCallsViaRelay(portoClient, portoAccount, adminKey, calls, merchantUrl, feeToken) {
11
+ const { capabilities, context, digest } = await RelayActions.prepareCalls(
12
+ portoClient,
13
+ {
14
+ account: portoAccount,
15
+ key: adminKey,
16
+ calls,
17
+ ...merchantUrl ? { merchantUrl } : {},
18
+ ...feeToken ? { feeToken } : {}
19
+ }
20
+ );
21
+ const signature = await portoAccount.sign({ hash: digest });
22
+ const caps = capabilities;
23
+ const result = await RelayActions.sendPreparedCalls(portoClient, {
24
+ capabilities: caps.feeSignature ? { feeSignature: caps.feeSignature } : void 0,
25
+ context,
26
+ key: adminKey,
27
+ signature
28
+ });
29
+ return result.id;
30
+ }
31
+ async function createPortoSmartAccount(params) {
32
+ const { para, chain, merchantUrl, feeToken } = params;
33
+ const walletAddress = resolveWalletIdentifier(para, params);
34
+ if (!walletAddress) return null;
35
+ const viemAccount = createParaAccount(para, walletAddress);
36
+ const portoClient = createClient({
37
+ chain,
38
+ transport: http(PORTO_RELAY_RPC)
39
+ });
40
+ const publicClient = createPublicClient({
41
+ chain,
42
+ transport: http()
43
+ });
44
+ const adminKey = Key.fromSecp256k1({ address: viemAccount.address, role: "admin" });
45
+ const portoAccount = Account.from({
46
+ address: viemAccount.address,
47
+ async sign({ hash }) {
48
+ return viemAccount.sign({ hash });
49
+ }
50
+ });
51
+ const buildAccount = () => Account.from({
52
+ address: viemAccount.address,
53
+ keys: [adminKey],
54
+ async sign({ hash }) {
55
+ return viemAccount.sign({ hash });
56
+ }
57
+ });
58
+ const upgradeAndWait = async (expectedCode) => {
59
+ const ephemeralP256Key = Key.createP256({ role: "admin" });
60
+ const prepared = await RelayActions.prepareUpgradeAccount(portoClient, {
61
+ address: portoAccount.address,
62
+ authorizeKeys: [ephemeralP256Key, adminKey],
63
+ chain
64
+ });
65
+ const signatures = {
66
+ auth: await viemAccount.sign({ hash: prepared.digests.auth }),
67
+ exec: await viemAccount.sign({ hash: prepared.digests.exec })
68
+ };
69
+ await RelayActions.upgradeAccount(portoClient, {
70
+ ...prepared,
71
+ signatures
72
+ });
73
+ for (let i = 0; i < PORTO_UPGRADE_POLL_MAX; i++) {
74
+ const code = await publicClient.getCode({ address: viemAccount.address });
75
+ const isReady = expectedCode ? code?.toLowerCase() === expectedCode : code && code.length > 4;
76
+ if (isReady) break;
77
+ await new Promise((r) => setTimeout(r, PORTO_UPGRADE_POLL_MS));
78
+ }
79
+ return buildAccount();
80
+ };
81
+ const withTimeout = (promise, ms) => Promise.race([
82
+ promise.then((v) => v).catch(() => void 0),
83
+ new Promise((resolve) => setTimeout(() => resolve(void 0), ms))
84
+ ]);
85
+ const [capsResult, currentCode] = await Promise.all([
86
+ (async () => {
87
+ try {
88
+ const caps = await withTimeout(RelayActions.getCapabilities(portoClient, { chainId: chain.id }), 3e3);
89
+ const chainKey = `0x${chain.id.toString(16)}`;
90
+ const capsAny = caps;
91
+ return capsAny?.[chainKey]?.contracts?.accountProxy?.address ?? capsAny?.contracts?.accountProxy?.address;
92
+ } catch {
93
+ return void 0;
94
+ }
95
+ })(),
96
+ publicClient.getCode({ address: viemAccount.address })
97
+ ]);
98
+ const portoExpectedCode = capsResult ? `0xef0100${capsResult.slice(2).toLowerCase()}` : void 0;
99
+ const isEip7702 = currentCode && currentCode.length === 48 && currentCode.startsWith("0xef0100");
100
+ let account;
101
+ try {
102
+ if (isEip7702 && portoExpectedCode && currentCode.toLowerCase() === portoExpectedCode) {
103
+ account = buildAccount();
104
+ } else if (isEip7702 && portoExpectedCode) {
105
+ const otherTarget = `0x${currentCode.slice(8)}`;
106
+ throw new Error(
107
+ `This account is already delegated to another EIP-7702 provider (${otherTarget}). Porto cannot be used alongside another 7702 provider.`
108
+ );
109
+ } else {
110
+ account = await upgradeAndWait(portoExpectedCode);
111
+ }
112
+ } catch (error) {
113
+ throw wrapProviderError(error, "PORTO");
114
+ }
115
+ const sendBatchTransaction = async (calls, isRetry = false) => {
116
+ try {
117
+ const id = await sendCallsViaRelay(
118
+ portoClient,
119
+ account,
120
+ adminKey,
121
+ calls.map((c) => ({
122
+ to: c.to,
123
+ value: c.value || BigInt(0),
124
+ data: c.data || "0x"
125
+ })),
126
+ merchantUrl,
127
+ feeToken
128
+ );
129
+ let status;
130
+ for (let i = 0; i < PORTO_POLL_MAX_ATTEMPTS; i++) {
131
+ status = await RelayActions.getCallsStatus(portoClient, { id });
132
+ if (status.status !== 100 && status.receipts?.length) break;
133
+ await new Promise((r) => setTimeout(r, PORTO_POLL_INTERVAL_MS));
134
+ }
135
+ const receipt = status?.receipts?.[0];
136
+ const txHash = receipt?.transactionHash ?? id;
137
+ return publicClient.getTransactionReceipt({ hash: txHash });
138
+ } catch (error) {
139
+ const msg = error instanceof Error ? error.message : String(error);
140
+ if (!isRetry && (msg.includes("ORCHESTRATOR") || msg.includes("returned no data"))) {
141
+ try {
142
+ account = await upgradeAndWait(portoExpectedCode);
143
+ return await sendBatchTransaction(calls, true);
144
+ } catch (retryError) {
145
+ throw wrapProviderError(retryError, "PORTO");
146
+ }
147
+ }
148
+ throw wrapProviderError(error, "PORTO");
149
+ }
150
+ };
151
+ const sendTransaction = async (tx) => sendBatchTransaction([tx], false);
152
+ return {
153
+ smartAccountAddress: account.address,
154
+ signer: viemAccount,
155
+ chain,
156
+ mode: "7702",
157
+ provider: "PORTO",
158
+ sendBatchTransaction,
159
+ sendTransaction,
160
+ client: { portoAccount: account, portoClient }
161
+ };
162
+ }
163
+ export {
164
+ createPortoSmartAccount
165
+ };
@@ -0,0 +1,2 @@
1
+ export { createPortoSmartAccount } from './action.js';
2
+ export type { PortoAccountConfig, CreatePortoSmartAccountParams, UsePortoSmartAccountParams } from './types.js';
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ import { createPortoSmartAccount } from "./action.js";
2
+ export {
3
+ createPortoSmartAccount
4
+ };
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -0,0 +1,51 @@
1
+ import type { ChainBasedSmartAccountConfig, CreateSmartAccountParams } from '@getpara/viem-v2-integration';
2
+ /**
3
+ * Configuration for Porto smart account.
4
+ *
5
+ * EIP-7702 only. Uses Porto's relay (`https://rpc.porto.sh`). No API key required.
6
+ *
7
+ * Porto supports multiple chains including Base, Optimism, Arbitrum, Ethereum,
8
+ * and several testnets. See https://porto.sh/relay for the full list.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const { smartAccount } = usePortoSmartAccount({ chain: baseSepolia });
13
+ * ```
14
+ */
15
+ export interface PortoAccountConfig extends Omit<ChainBasedSmartAccountConfig, 'rpcUrl'> {
16
+ /**
17
+ * URL of your Porto merchant endpoint for gas sponsorship.
18
+ * The merchant account covers transaction fees for your users.
19
+ *
20
+ * Required for mainnet — without it, transactions will fail unless the user's
21
+ * account has native tokens to pay gas. On testnets, Porto's relay sponsors
22
+ * fees by default so this can be omitted during development.
23
+ *
24
+ * See https://porto.sh/sdk/guides/sponsoring for setup instructions.
25
+ *
26
+ * @example '/porto/merchant'
27
+ * @example 'https://myapi.com/porto/merchant'
28
+ */
29
+ merchantUrl?: string;
30
+ /**
31
+ * ERC-20 token address used to pay gas fees.
32
+ *
33
+ * On Base Sepolia testnet, use Porto's EXP token:
34
+ * `0xfca413a634c4df6b98ebb970a44d9a32f8f5c64e`
35
+ *
36
+ * When omitted, Porto's relay uses native gas sponsorship via the ORCHESTRATOR
37
+ * contract. If the ORCHESTRATOR is not deployed on the target chain (e.g. Base
38
+ * Sepolia), transactions will fail. Providing a feeToken bypasses this path.
39
+ */
40
+ feeToken?: `0x${string}`;
41
+ }
42
+ /**
43
+ * Parameters for `createPortoSmartAccount`.
44
+ * Includes all config fields plus `para` (the Para SDK instance).
45
+ */
46
+ export type CreatePortoSmartAccountParams = CreateSmartAccountParams<PortoAccountConfig>;
47
+ /**
48
+ * Parameters for the `usePortoSmartAccount` hook.
49
+ * Same as {@link PortoAccountConfig} — `para` is provided automatically via context.
50
+ */
51
+ export type UsePortoSmartAccountParams = PortoAccountConfig;
package/dist/types.js ADDED
File without changes
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@getpara/aa-porto",
3
+ "version": "2.16.0",
4
+ "description": "Porto smart account integration for Para",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "sideEffects": false,
9
+ "files": [
10
+ "dist",
11
+ "package.json"
12
+ ],
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js"
17
+ }
18
+ },
19
+ "scripts": {
20
+ "build": "rm -rf dist && yarn typegen && node ./scripts/build.mjs",
21
+ "typegen": "tsc --emitDeclarationOnly --outDir dist",
22
+ "test": "vitest run --coverage"
23
+ },
24
+ "dependencies": {
25
+ "@getpara/viem-v2-integration": "2.16.0",
26
+ "porto": "^0.2.37"
27
+ },
28
+ "peerDependencies": {
29
+ "viem": ">=2.0.0"
30
+ },
31
+ "devDependencies": {
32
+ "typescript": "^5.8.3"
33
+ },
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "gitHead": "fbe96a062b308d04105213378c12c38ee973c798"
38
+ }