@naculus/connect-appkit-core 0.3.0 → 0.5.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/dist/index.cjs CHANGED
@@ -37,13 +37,16 @@ __export(src_exports, {
37
37
  clearOneInAppNotification: () => clearOneInAppNotification,
38
38
  compareCostsKey: () => compareCostsKey,
39
39
  createDecimalsCache: () => createDecimalsCache,
40
+ createDelegationFrameworkAdapter: () => createDelegationFrameworkAdapter,
40
41
  createDelegationPolicyFlow: () => createDelegationPolicyFlow,
41
42
  createNotification: () => createNotification,
42
43
  createNotificationSettingsStorage: () => createNotificationSettingsStorage,
44
+ createSolanaSessionKeyFlow: () => createSolanaSessionKeyFlow,
43
45
  decodeRevertReason: () => decodeRevertReason,
44
46
  defaultSiwxDomain: () => defaultSiwxDomain,
45
47
  defaultSiwxUri: () => defaultSiwxUri,
46
48
  describeChain: () => describeChain,
49
+ describePayment: () => describePayment,
47
50
  eip155ChainIdToNumber: () => eip155ChainIdToNumber,
48
51
  encodeErc20Approve: () => encodeErc20Approve,
49
52
  encodeErc20Transfer: () => encodeErc20Transfer,
@@ -871,8 +874,104 @@ function assertHexSignature(value) {
871
874
  return value;
872
875
  }
873
876
 
874
- // src/delegation-policy.ts
877
+ // src/delegation-framework-adapter.ts
875
878
  var import_connect_core15 = require("@naculus/connect-core");
879
+ function createDelegationFrameworkAdapter(options) {
880
+ const { manager, rpc, codec } = options;
881
+ return {
882
+ route: "eip7702",
883
+ async check({ policy }) {
884
+ const ready = policy.scope.mode === "eip7702" && policy.status === "active" && Boolean(policy.authorized);
885
+ return {
886
+ ready,
887
+ authorizationInstalled: ready,
888
+ sponsored: false,
889
+ promptless: true,
890
+ executorAddress: import_connect_core15.DELEGATION_FRAMEWORK.eip7702StatelessDeleGator,
891
+ reason: ready ? "The session key redeems its delegation; the chain enforces the caveats." : "This policy has no attached EIP-7702 delegation."
892
+ };
893
+ },
894
+ async prepare({ policy, transaction }) {
895
+ if (!transaction.to) {
896
+ throw new import_connect_core15.WalletError(
897
+ "invalid_input",
898
+ "A delegated execution needs a target."
899
+ );
900
+ }
901
+ let value;
902
+ try {
903
+ value = BigInt(transaction.value ?? "0");
904
+ } catch {
905
+ throw new import_connect_core15.WalletError(
906
+ "invalid_input",
907
+ "Transaction value is not a quantity."
908
+ );
909
+ }
910
+ const execution = {
911
+ target: transaction.to,
912
+ value,
913
+ callData: transaction.data ?? "0x"
914
+ };
915
+ const call = await manager.buildDelegationRedemption(
916
+ policy.id,
917
+ execution
918
+ );
919
+ const chainId = options.chainId();
920
+ if (chainId !== call.chainId) {
921
+ throw new import_connect_core15.WalletError(
922
+ "chain_mismatch",
923
+ `The delegation is for chain ${call.chainId}, not ${String(chainId)}.`
924
+ );
925
+ }
926
+ const from = (0, import_connect_core15.sessionKeyAddress)(policy.publicKey);
927
+ const [nonce, gas, fees] = await Promise.all([
928
+ rpc.getTransactionCount(from),
929
+ rpc.estimateGas({ from, to: call.to, data: call.data }),
930
+ rpc.fees()
931
+ ]);
932
+ const unsigned = {
933
+ type: "eip1559",
934
+ chainId,
935
+ nonce,
936
+ to: call.to,
937
+ value: 0n,
938
+ data: call.data,
939
+ gas,
940
+ maxFeePerGas: fees.maxFeePerGas,
941
+ maxPriorityFeePerGas: fees.maxPriorityFeePerGas
942
+ };
943
+ const payload = {
944
+ outerTx: {
945
+ to: call.to,
946
+ value: "0x0",
947
+ data: call.data,
948
+ chainId,
949
+ gas: `0x${gas.toString(16)}`
950
+ },
951
+ execution,
952
+ unsigned
953
+ };
954
+ return {
955
+ digest: codec.signingHash(unsigned),
956
+ transaction,
957
+ payload
958
+ };
959
+ },
960
+ async broadcast({ prepared, signature }) {
961
+ const { unsigned } = prepared.payload;
962
+ const hash = await rpc.sendRawTransaction(
963
+ codec.serialize(unsigned, signature)
964
+ );
965
+ return { route: "eip7702", hash, status: "submitted" };
966
+ }
967
+ };
968
+ }
969
+
970
+ // src/delegation-policy.ts
971
+ var import_connect_core16 = require("@naculus/connect-core");
972
+ function delegatesToStatelessDeleGator(delegation) {
973
+ return delegation.delegated === true && delegation.delegate?.toLowerCase() === import_connect_core16.DELEGATION_FRAMEWORK.eip7702StatelessDeleGator.toLowerCase();
974
+ }
876
975
  function sorted(values) {
877
976
  return values ? [...values].map((value) => value.toLowerCase()).sort() : null;
878
977
  }
@@ -919,12 +1018,13 @@ function buildDelegationPolicyMessage(policy, origin) {
919
1018
  ${JSON.stringify(payload)}`;
920
1019
  }
921
1020
  function isVerifiablePolicy(policy) {
922
- return policy.status === "active" && Boolean(policy.authorized) && policy.scope.mode === "offchain";
1021
+ return policy.status === "active" && Boolean(policy.authorized) && (policy.scope.mode === "offchain" || policy.scope.mode === "eip7702");
923
1022
  }
924
1023
  function createDelegationPolicyFlow(deps) {
925
1024
  const { manager, origin } = deps;
926
1025
  const verifyStoredPolicy = async (policy) => {
927
1026
  if (!isVerifiablePolicy(policy)) return false;
1027
+ if (policy.scope.mode === "eip7702") return true;
928
1028
  return manager.verifyOffchainAuthorization(
929
1029
  policy.id,
930
1030
  buildDelegationPolicyMessage(policy, origin),
@@ -934,28 +1034,29 @@ function createDelegationPolicyFlow(deps) {
934
1034
  const createPolicy = async (scope) => {
935
1035
  const signer = deps.signer();
936
1036
  if (!signer) {
937
- throw new import_connect_core15.WalletError("wallet_unavailable", "No active EVM account");
1037
+ throw new import_connect_core16.WalletError("wallet_unavailable", "No active EVM account");
938
1038
  }
1039
+ if (scope.mode === "eip7702") return createDelegatedPolicy(scope, signer);
939
1040
  if (scope.mode && scope.mode !== "offchain") {
940
- throw new import_connect_core15.WalletError(
1041
+ throw new import_connect_core16.WalletError(
941
1042
  "method_not_allowed",
942
- "This flow creates signed off-chain policies only. EIP-7702 and AA-module policies require a configured on-chain execution adapter."
1043
+ "This flow creates off-chain and EIP-7702 policies only; AA-module policies need an on-chain module."
943
1044
  );
944
1045
  }
945
1046
  if (scope.allowedRecipients && scope.allowedRecipients.length > 0) {
946
- throw new import_connect_core15.WalletError(
1047
+ throw new import_connect_core16.WalletError(
947
1048
  "method_not_allowed",
948
1049
  "Recipient-limited policies are not supported by this flow: its executions sign raw digests, which connect-core refuses while allowedRecipients is set."
949
1050
  );
950
1051
  }
951
1052
  if (!deps.encryptionKeyConfigured) {
952
- throw new import_connect_core15.WalletError(
1053
+ throw new import_connect_core16.WalletError(
953
1054
  "method_not_allowed",
954
1055
  "Persistent delegation policies require a host-provided encryptionKey. The deterministic compatibility fallback is not a production key boundary."
955
1056
  );
956
1057
  }
957
1058
  if (!deps.storageAvailable()) {
958
- throw new import_connect_core15.WalletError(
1059
+ throw new import_connect_core16.WalletError(
959
1060
  "storage_unavailable",
960
1061
  "Persistent delegation policy storage is unavailable in this browser"
961
1062
  );
@@ -969,7 +1070,7 @@ function createDelegationPolicyFlow(deps) {
969
1070
  const message = buildDelegationPolicyMessage(draft, origin);
970
1071
  const signature = await deps.signMessage(message);
971
1072
  if (!/^0x[0-9a-fA-F]{130}$/.test(signature)) {
972
- throw new import_connect_core15.WalletError(
1073
+ throw new import_connect_core16.WalletError(
973
1074
  "invalid_input",
974
1075
  "Wallet did not return a valid 65-byte EVM policy signature"
975
1076
  );
@@ -980,7 +1081,7 @@ function createDelegationPolicyFlow(deps) {
980
1081
  signerAddress: signer
981
1082
  });
982
1083
  if (!recovers) {
983
- throw new import_connect_core15.WalletError(
1084
+ throw new import_connect_core16.WalletError(
984
1085
  "invalid_input",
985
1086
  "Policy signature does not recover to the connected EVM account"
986
1087
  );
@@ -995,13 +1096,13 @@ function createDelegationPolicyFlow(deps) {
995
1096
  (policy) => policy.id === draft?.id
996
1097
  );
997
1098
  if (!authorized?.authorized) {
998
- throw new import_connect_core15.WalletError(
1099
+ throw new import_connect_core16.WalletError(
999
1100
  "invalid_input",
1000
1101
  "Policy authorization was not persisted"
1001
1102
  );
1002
1103
  }
1003
1104
  if (!await verifyStoredPolicy(authorized)) {
1004
- throw new import_connect_core15.WalletError(
1105
+ throw new import_connect_core16.WalletError(
1005
1106
  "invalid_input",
1006
1107
  "Persisted policy signature did not verify against its stored scope and origin"
1007
1108
  );
@@ -1018,6 +1119,66 @@ function createDelegationPolicyFlow(deps) {
1018
1119
  throw cause instanceof Error ? cause : new Error("Policy creation failed");
1019
1120
  }
1020
1121
  };
1122
+ const createDelegatedPolicy = async (scope, signer) => {
1123
+ if (!deps.signTypedData || !deps.chainId) {
1124
+ throw new import_connect_core16.WalletError(
1125
+ "method_not_allowed",
1126
+ "EIP-7702 policies need the wallet's signTypedData and the current chain."
1127
+ );
1128
+ }
1129
+ if (!deps.encryptionKeyConfigured) {
1130
+ throw new import_connect_core16.WalletError(
1131
+ "method_not_allowed",
1132
+ "Persistent delegation policies require a host-provided encryptionKey. The deterministic compatibility fallback is not a production key boundary."
1133
+ );
1134
+ }
1135
+ if (!deps.storageAvailable()) {
1136
+ throw new import_connect_core16.WalletError(
1137
+ "storage_unavailable",
1138
+ "Persistent delegation policy storage is unavailable in this browser"
1139
+ );
1140
+ }
1141
+ if (!delegatesToStatelessDeleGator(deps.delegation())) {
1142
+ throw new import_connect_core16.WalletError(
1143
+ "method_not_allowed",
1144
+ `The account must first delegate to EIP7702StatelessDeleGatorImpl (${import_connect_core16.DELEGATION_FRAMEWORK.eip7702StatelessDeleGator}).`
1145
+ );
1146
+ }
1147
+ const chainId = deps.chainId();
1148
+ if (chainId === null || !Number.isSafeInteger(chainId)) {
1149
+ throw new import_connect_core16.WalletError("wallet_unavailable", "No connected EVM chain");
1150
+ }
1151
+ let draft = null;
1152
+ try {
1153
+ draft = await manager.createSessionKey(
1154
+ { ...scope, mode: "eip7702" },
1155
+ signer
1156
+ );
1157
+ const delegation = await manager.prepareDelegation(draft.id, chainId);
1158
+ const signature = await deps.signTypedData(
1159
+ (0, import_connect_core16.delegationTypedData)(delegation)
1160
+ );
1161
+ if (!/^0x[0-9a-fA-F]{130}$/.test(signature)) {
1162
+ throw new import_connect_core16.WalletError(
1163
+ "invalid_input",
1164
+ "Wallet did not return a valid 65-byte EVM signature"
1165
+ );
1166
+ }
1167
+ await manager.attachDelegation(
1168
+ draft.id,
1169
+ delegation,
1170
+ signature
1171
+ );
1172
+ await deps.refresh();
1173
+ return (await manager.listSessions()).find((s) => s.id === draft?.id) ?? draft;
1174
+ } catch (cause) {
1175
+ if (draft) await manager.revokeSession(draft.id).catch(() => {
1176
+ });
1177
+ await deps.refresh().catch(() => {
1178
+ });
1179
+ throw cause;
1180
+ }
1181
+ };
1021
1182
  const revokePolicy = async (policyId) => {
1022
1183
  await manager.revokeSession(policyId);
1023
1184
  await deps.refresh();
@@ -1050,16 +1211,21 @@ function createDelegationPolicyFlow(deps) {
1050
1211
  const delegationMatches = Boolean(
1051
1212
  adapter?.route !== "eip7702" || delegation.delegated === true && adapterCheck?.executorAddress && delegation.delegate?.toLowerCase() === adapterCheck.executorAddress.toLowerCase()
1052
1213
  );
1214
+ const delegatedPolicy = policy?.scope.mode === "eip7702";
1053
1215
  const policyReady = Boolean(
1054
- policy?.status === "active" && policy.authorized && authorizationVerified && policy.scope.mode === "offchain" && signerMatches && scope.valid
1216
+ policy?.status === "active" && policy.authorized && authorizationVerified && (policy.scope.mode === "offchain" || delegatedPolicy) && signerMatches && scope.valid
1055
1217
  );
1056
1218
  const adapterSponsorshipReady = Boolean(
1057
1219
  deps.sponsorship !== "required" || adapterCheck?.sponsored
1058
1220
  );
1059
1221
  const broadcastReady = Boolean(
1060
- policyReady && callCount === 1 && adapterCheck?.ready && adapterCheck.authorizationInstalled && adapterCheck.promptless && adapterSponsorshipReady && delegationMatches
1222
+ policyReady && callCount === 1 && adapterCheck?.ready && adapterCheck.authorizationInstalled && adapterCheck.promptless && adapterSponsorshipReady && delegationMatches && // An eip7702 policy executes only through a redemption: the key
1223
+ // signs nothing else, so only an eip7702 adapter can carry it.
1224
+ (!delegatedPolicy || adapter?.route === "eip7702")
1225
+ );
1226
+ const ready = Boolean(
1227
+ policyReady && (delegatedPolicy ? broadcastReady : plan.route || broadcastReady)
1061
1228
  );
1062
- const ready = Boolean(policyReady && (plan.route || broadcastReady));
1063
1229
  const reason = !policy ? "Policy not found." : policy.status !== "active" ? `Policy is ${policy.status}.` : !policy.authorized ? "The main wallet has not signed this policy." : !authorizationVerified ? "The persisted policy signature no longer matches its scope, signer, or origin." : !signerMatches ? "The connected account is not the policy signer." : !scope.valid ? scope.reason ?? "The transaction is outside the policy scope." : broadcastReady ? `The configured ${adapter?.route ?? "AA/7702"} adapter can submit this policy-authorized operation without another main-wallet prompt.${adapterCheck?.sponsored ? " Gas sponsorship is guaranteed." : ""} ${adapterCheck?.reason ?? "The on-chain execution adapter is ready."}` : !adapter ? `${plan.reason} The signed off-chain policy permits this request; an AA/7702 adapter is still required to broadcast without another wallet prompt.` : !adapterCheck?.ready || !adapterCheck.authorizationInstalled ? `${plan.reason} ${adapterCheck?.reason ?? "The execution adapter is not ready."}` : !adapterCheck.promptless ? `${plan.reason} The configured adapter would invoke the main wallet again, so promptless broadcast is not ready.` : !adapterSponsorshipReady ? `${plan.reason} Sponsored gas is required, but the configured adapter does not guarantee sponsorship.` : callCount !== 1 ? `${plan.reason} This policy adapter prepares one execution at a time and cannot claim a multi-call broadcast.` : !delegationMatches ? `${plan.reason} The configured EIP-7702 executor does not match the account's on-chain delegation.` : !plan.route ? plan.reason : `${plan.reason} ${adapterCheck.reason}`;
1064
1230
  return {
1065
1231
  policy,
@@ -1085,37 +1251,76 @@ function createDelegationPolicyFlow(deps) {
1085
1251
  digest,
1086
1252
  tx
1087
1253
  );
1254
+ const signRedemption = (policyId, prepared) => {
1255
+ const payload = prepared.payload;
1256
+ if (!payload?.outerTx || !payload.execution) {
1257
+ throw new import_connect_core16.WalletError(
1258
+ "invalid_input",
1259
+ "The EIP-7702 adapter did not return a redemption to sign."
1260
+ );
1261
+ }
1262
+ if (prepared.transaction.chainId !== void 0 && prepared.transaction.chainId !== payload.outerTx.chainId) {
1263
+ throw new import_connect_core16.WalletError(
1264
+ "chain_mismatch",
1265
+ `The delegation is for chain ${String(payload.outerTx.chainId)}, not ${prepared.transaction.chainId}.`
1266
+ );
1267
+ }
1268
+ const { execution } = payload;
1269
+ if (typeof execution.value !== "bigint" || execution.value < 0n || !sameExecutionIntent(prepared.transaction, {
1270
+ to: execution.target,
1271
+ data: execution.callData,
1272
+ value: `0x${execution.value.toString(16)}`,
1273
+ chainId: prepared.transaction.chainId
1274
+ })) {
1275
+ throw new import_connect_core16.WalletError(
1276
+ "invalid_input",
1277
+ "The EIP-7702 adapter's execution differs from the requested transaction."
1278
+ );
1279
+ }
1280
+ return manager.signDelegationRedemption(
1281
+ policyId,
1282
+ prepared.digest,
1283
+ payload.outerTx,
1284
+ payload.execution
1285
+ );
1286
+ };
1088
1287
  const signPolicyDigest = async (policyId, digest, tx) => {
1089
1288
  const preview = await previewPolicyExecution(policyId, tx, 1, "any");
1289
+ if (preview.policy?.scope.mode === "eip7702") {
1290
+ throw new import_connect_core16.WalletError(
1291
+ "method_not_allowed",
1292
+ "An EIP-7702 policy signs only its delegation redemptions; use executePolicy."
1293
+ );
1294
+ }
1090
1295
  if (!preview.ready) {
1091
- throw new import_connect_core15.WalletError("method_not_allowed", preview.reason);
1296
+ throw new import_connect_core16.WalletError("method_not_allowed", preview.reason);
1092
1297
  }
1093
1298
  return signVerified(policyId, digest, tx);
1094
1299
  };
1095
1300
  const executePolicy = async (policyId, tx) => {
1096
1301
  const adapter = deps.adapter;
1097
1302
  if (!adapter) {
1098
- throw new import_connect_core15.WalletError(
1303
+ throw new import_connect_core16.WalletError(
1099
1304
  "method_not_allowed",
1100
1305
  "No session-policy-aware AA/7702 execution adapter is configured."
1101
1306
  );
1102
1307
  }
1103
1308
  const preview = await previewPolicyExecution(policyId, tx, 1, "any");
1104
1309
  if (!preview.broadcastReady || !preview.policy) {
1105
- throw new import_connect_core15.WalletError("method_not_allowed", preview.reason);
1310
+ throw new import_connect_core16.WalletError("method_not_allowed", preview.reason);
1106
1311
  }
1107
1312
  const prepared = await adapter.prepare({
1108
1313
  policy: preview.policy,
1109
1314
  transaction: tx
1110
1315
  });
1111
1316
  if (!/^0x[0-9a-fA-F]{64}$/.test(prepared.digest)) {
1112
- throw new import_connect_core15.WalletError(
1317
+ throw new import_connect_core16.WalletError(
1113
1318
  "invalid_input",
1114
1319
  "Execution adapter returned a digest that is not 32 bytes."
1115
1320
  );
1116
1321
  }
1117
1322
  if (!sameExecutionIntent(tx, prepared.transaction)) {
1118
- throw new import_connect_core15.WalletError(
1323
+ throw new import_connect_core16.WalletError(
1119
1324
  "invalid_input",
1120
1325
  "Execution adapter changed the requested target, value, data, or chain."
1121
1326
  );
@@ -1127,23 +1332,19 @@ function createDelegationPolicyFlow(deps) {
1127
1332
  const delegation = deps.delegation();
1128
1333
  const currentDelegationMatches = adapter.route !== "eip7702" || delegation.delegated === true && currentCheck.executorAddress !== void 0 && delegation.delegate?.toLowerCase() === currentCheck.executorAddress.toLowerCase();
1129
1334
  if (!currentCheck.ready || !currentCheck.authorizationInstalled || !currentCheck.promptless || deps.sponsorship === "required" && !currentCheck.sponsored || !currentDelegationMatches) {
1130
- throw new import_connect_core15.WalletError(
1335
+ throw new import_connect_core16.WalletError(
1131
1336
  "method_not_allowed",
1132
1337
  currentCheck.reason || "The on-chain policy authorization is not ready."
1133
1338
  );
1134
1339
  }
1135
- const signature = await signVerified(
1136
- policyId,
1137
- prepared.digest,
1138
- prepared.transaction
1139
- );
1340
+ const signature = preview.policy.scope.mode === "eip7702" ? await signRedemption(policyId, prepared) : await signVerified(policyId, prepared.digest, prepared.transaction);
1140
1341
  const submission = await adapter.broadcast({
1141
1342
  policy: preview.policy,
1142
1343
  prepared,
1143
1344
  signature
1144
1345
  });
1145
1346
  if (submission.route !== adapter.route || submission.status !== "submitted" || !/^0x[0-9a-fA-F]{64}$/.test(submission.hash)) {
1146
- throw new import_connect_core15.WalletError(
1347
+ throw new import_connect_core16.WalletError(
1147
1348
  "invalid_input",
1148
1349
  "Execution adapter returned an invalid submission result."
1149
1350
  );
@@ -1161,8 +1362,146 @@ function createDelegationPolicyFlow(deps) {
1161
1362
  };
1162
1363
  }
1163
1364
 
1365
+ // src/payment-fetch.ts
1366
+ function record(value) {
1367
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
1368
+ }
1369
+ function text(value) {
1370
+ return typeof value === "string" && value !== "" ? value : null;
1371
+ }
1372
+ function requestUrl(input) {
1373
+ if (input instanceof URL) return input.href;
1374
+ if (typeof input !== "string") return input.url;
1375
+ try {
1376
+ return new Request(input).url;
1377
+ } catch {
1378
+ return input;
1379
+ }
1380
+ }
1381
+ function describePayment(input, result) {
1382
+ const paid = record(result.paid);
1383
+ if (!paid) return null;
1384
+ const resource = requestUrl(input);
1385
+ if (typeof paid.scheme === "string" && typeof paid.network === "string") {
1386
+ const settlement = record(result.settlement);
1387
+ return {
1388
+ protocol: "x402",
1389
+ resource,
1390
+ chainId: text(paid.network),
1391
+ asset: text(paid.asset),
1392
+ payTo: text(paid.payTo),
1393
+ amount: text(paid.amount),
1394
+ // A hash reported for another chain is not this payment's.
1395
+ reference: settlement?.success === true && settlement.network === paid.network ? text(settlement.transaction) : null
1396
+ };
1397
+ }
1398
+ const challenge = record(record(paid.challenge)?.params);
1399
+ const request = record(paid.request);
1400
+ if (challenge && request) {
1401
+ const chainId = challenge.method === "evm" && typeof request.chainId === "number" && Number.isSafeInteger(request.chainId) ? `eip155:${request.chainId}` : challenge.method === "solana" && typeof request.network === "string" && /^solana:[1-9A-HJ-NP-Za-km-z]{32}$/.test(request.network) ? request.network : null;
1402
+ return {
1403
+ protocol: "mpp",
1404
+ resource,
1405
+ chainId,
1406
+ asset: text(request.currency),
1407
+ payTo: text(request.recipient),
1408
+ amount: text(request.amount),
1409
+ reference: text(record(result.receipt)?.reference)
1410
+ };
1411
+ }
1412
+ return {
1413
+ protocol: "unknown",
1414
+ resource,
1415
+ chainId: null,
1416
+ asset: null,
1417
+ payTo: null,
1418
+ amount: null,
1419
+ reference: null
1420
+ };
1421
+ }
1422
+
1423
+ // src/solana-session-key.ts
1424
+ var import_connect_core17 = require("@naculus/connect-core");
1425
+ function send(rpc) {
1426
+ const sendTransaction = rpc.sendTransaction;
1427
+ if (!sendTransaction) {
1428
+ throw new import_connect_core17.WalletError(
1429
+ "invalid_input",
1430
+ "The Solana RPC must implement sendTransaction to approve or revoke."
1431
+ );
1432
+ }
1433
+ return (tx) => sendTransaction.call(rpc, tx);
1434
+ }
1435
+ function createSolanaSessionKeyFlow(deps) {
1436
+ const { manager, rpc, owner } = deps;
1437
+ const list = async () => (await manager.listSessions()).filter((key) => key.owner === owner.address).sort((a, b) => b.createdAt - a.createdAt);
1438
+ return {
1439
+ list,
1440
+ async create(scope) {
1441
+ const broadcast = send(rpc);
1442
+ const draft = await manager.createSessionKey(scope, owner.address, rpc);
1443
+ let approval;
1444
+ try {
1445
+ const { transaction, recentBlockhash } = await manager.prepareApproval(
1446
+ draft.id,
1447
+ rpc
1448
+ );
1449
+ const signed = await owner.signTransaction(transaction);
1450
+ approval = await manager.attachApproval(
1451
+ draft.id,
1452
+ signed,
1453
+ recentBlockhash
1454
+ );
1455
+ } catch (cause) {
1456
+ await manager.revoke(draft.id).catch(() => {
1457
+ });
1458
+ throw cause;
1459
+ }
1460
+ let signature;
1461
+ try {
1462
+ signature = await broadcast(approval);
1463
+ } catch (cause) {
1464
+ await manager.revoke(draft.id).catch(() => {
1465
+ });
1466
+ throw new import_connect_core17.WalletError(
1467
+ "rpc_error",
1468
+ `The approval could not be confirmed as sent (${cause instanceof Error ? cause.message : String(cause)}); the key is revoked here. Call revoke("${draft.id}") to send the on-chain Revoke in case it landed.`
1469
+ );
1470
+ }
1471
+ const key = (await list().catch(() => [])).find((k) => k.id === draft.id);
1472
+ return { key: key ?? { ...draft, status: "active" }, signature };
1473
+ },
1474
+ async revoke(id) {
1475
+ const key = (await list()).find((k) => k.id === id);
1476
+ if (!key) {
1477
+ throw new import_connect_core17.WalletError(
1478
+ "invalid_input",
1479
+ `No Solana session key ${id} for this owner.`
1480
+ );
1481
+ }
1482
+ await manager.revoke(id);
1483
+ const broadcast = send(rpc);
1484
+ const { transaction, recentBlockhash } = await manager.prepareRevocation(
1485
+ id,
1486
+ rpc
1487
+ );
1488
+ const signed = await owner.signTransaction(transaction);
1489
+ const revocation = (0, import_connect_core17.verifySignedOwnerTransaction)(signed, {
1490
+ kind: "revoke",
1491
+ revocation: {
1492
+ owner: key.owner,
1493
+ mint: key.scope.mint,
1494
+ tokenProgram: key.tokenProgram,
1495
+ recentBlockhash
1496
+ }
1497
+ });
1498
+ return { signature: await broadcast(revocation) };
1499
+ }
1500
+ };
1501
+ }
1502
+
1164
1503
  // src/web3-store.ts
1165
- var import_connect_core16 = require("@naculus/connect-core");
1504
+ var import_connect_core18 = require("@naculus/connect-core");
1166
1505
  function withTimeout(promise, ms, label = "Operation") {
1167
1506
  let timeoutId;
1168
1507
  const timeout = new Promise((_, reject) => {
@@ -1184,7 +1523,7 @@ async function withRetry(fn, options) {
1184
1523
  lastError = error;
1185
1524
  if (attempt < options.maxRetries) {
1186
1525
  const delay = options.baseDelay * 2 ** attempt;
1187
- import_connect_core16.logger.warn(
1526
+ import_connect_core18.logger.warn(
1188
1527
  "appkit/core",
1189
1528
  `Retry attempt ${attempt + 1}/${options.maxRetries} after ${delay}ms`
1190
1529
  );
@@ -1308,13 +1647,16 @@ function siwxResultToSession(result, now = /* @__PURE__ */ new Date()) {
1308
1647
  clearOneInAppNotification,
1309
1648
  compareCostsKey,
1310
1649
  createDecimalsCache,
1650
+ createDelegationFrameworkAdapter,
1311
1651
  createDelegationPolicyFlow,
1312
1652
  createNotification,
1313
1653
  createNotificationSettingsStorage,
1654
+ createSolanaSessionKeyFlow,
1314
1655
  decodeRevertReason,
1315
1656
  defaultSiwxDomain,
1316
1657
  defaultSiwxUri,
1317
1658
  describeChain,
1659
+ describePayment,
1318
1660
  eip155ChainIdToNumber,
1319
1661
  encodeErc20Approve,
1320
1662
  encodeErc20Transfer,