@rhinestone/sdk 2.0.0-beta.3 → 2.0.0-beta.31
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/src/accounts/error.d.ts +2 -9
- package/dist/src/accounts/error.d.ts.map +1 -1
- package/dist/src/accounts/error.js +5 -11
- package/dist/src/accounts/hca.d.ts +25 -0
- package/dist/src/accounts/hca.d.ts.map +1 -0
- package/dist/src/accounts/hca.js +145 -0
- package/dist/src/accounts/index.d.ts +9 -9
- package/dist/src/accounts/index.d.ts.map +1 -1
- package/dist/src/accounts/index.js +112 -41
- package/dist/src/accounts/kernel.d.ts +1 -2
- package/dist/src/accounts/kernel.d.ts.map +1 -1
- package/dist/src/accounts/kernel.js +1 -8
- package/dist/src/accounts/nexus.d.ts +3 -2
- package/dist/src/accounts/nexus.d.ts.map +1 -1
- package/dist/src/accounts/nexus.js +33 -8
- package/dist/src/accounts/safe.d.ts +1 -2
- package/dist/src/accounts/safe.d.ts.map +1 -1
- package/dist/src/accounts/safe.js +3 -9
- package/dist/src/accounts/signing/common.d.ts +1 -4
- package/dist/src/accounts/signing/common.d.ts.map +1 -1
- package/dist/src/accounts/signing/common.js +7 -11
- package/dist/src/accounts/signing/message.d.ts.map +1 -1
- package/dist/src/accounts/signing/message.js +1 -4
- package/dist/src/accounts/signing/typedData.d.ts.map +1 -1
- package/dist/src/accounts/signing/typedData.js +1 -4
- package/dist/src/accounts/startale.d.ts +1 -2
- package/dist/src/accounts/startale.d.ts.map +1 -1
- package/dist/src/accounts/startale.js +2 -5
- package/dist/src/actions/ecdsa.js +5 -5
- package/dist/src/actions/index.js +2 -2
- package/dist/src/actions/mfa.js +2 -2
- package/dist/src/actions/passkeys.js +2 -2
- package/dist/src/actions/smart-sessions.d.ts +29 -5
- package/dist/src/actions/smart-sessions.d.ts.map +1 -1
- package/dist/src/actions/smart-sessions.js +38 -10
- package/dist/src/errors/index.d.ts +4 -4
- package/dist/src/errors/index.d.ts.map +1 -1
- package/dist/src/errors/index.js +6 -6
- package/dist/src/execution/error.d.ts +8 -21
- package/dist/src/execution/error.d.ts.map +1 -1
- package/dist/src/execution/error.js +7 -21
- package/dist/src/execution/index.d.ts +17 -17
- package/dist/src/execution/index.d.ts.map +1 -1
- package/dist/src/execution/index.js +22 -41
- package/dist/src/execution/utils.d.ts +15 -11
- package/dist/src/execution/utils.d.ts.map +1 -1
- package/dist/src/execution/utils.js +279 -75
- package/dist/src/index.d.ts +236 -37
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +60 -115
- package/dist/src/jwt-server/jcs.d.ts +1 -1
- package/dist/src/jwt-server/jcs.js +4 -2
- package/dist/src/modules/index.d.ts.map +1 -1
- package/dist/src/modules/index.js +0 -5
- package/dist/src/modules/read.d.ts +3 -2
- package/dist/src/modules/read.d.ts.map +1 -1
- package/dist/src/modules/read.js +33 -4
- package/dist/src/modules/validators/core.d.ts +5 -5
- package/dist/src/modules/validators/core.d.ts.map +1 -1
- package/dist/src/modules/validators/core.js +45 -34
- package/dist/src/modules/validators/cross-chain-permits.d.ts +11 -0
- package/dist/src/modules/validators/cross-chain-permits.d.ts.map +1 -0
- package/dist/src/modules/validators/cross-chain-permits.js +76 -0
- package/dist/src/modules/validators/permissions.d.ts +1 -0
- package/dist/src/modules/validators/permissions.d.ts.map +1 -1
- package/dist/src/modules/validators/permissions.js +162 -17
- package/dist/src/modules/validators/policies/claim/arbiters.d.ts +22 -0
- package/dist/src/modules/validators/policies/claim/arbiters.d.ts.map +1 -0
- package/dist/src/modules/validators/policies/claim/arbiters.js +57 -0
- package/dist/src/modules/validators/smart-sessions.d.ts +38 -9
- package/dist/src/modules/validators/smart-sessions.d.ts.map +1 -1
- package/dist/src/modules/validators/smart-sessions.js +509 -92
- package/dist/src/orchestrator/caip2.d.ts +9 -3
- package/dist/src/orchestrator/caip2.d.ts.map +1 -1
- package/dist/src/orchestrator/caip2.js +40 -5
- package/dist/src/orchestrator/client.d.ts.map +1 -1
- package/dist/src/orchestrator/client.js +44 -21
- package/dist/src/orchestrator/consts.d.ts +1 -1
- package/dist/src/orchestrator/consts.d.ts.map +1 -1
- package/dist/src/orchestrator/consts.js +1 -1
- package/dist/src/orchestrator/destinations.d.ts +24 -0
- package/dist/src/orchestrator/destinations.d.ts.map +1 -0
- package/dist/src/orchestrator/destinations.js +55 -0
- package/dist/src/orchestrator/error.d.ts +87 -4
- package/dist/src/orchestrator/error.d.ts.map +1 -1
- package/dist/src/orchestrator/error.js +237 -3
- package/dist/src/orchestrator/index.d.ts +6 -5
- package/dist/src/orchestrator/index.d.ts.map +1 -1
- package/dist/src/orchestrator/index.js +4 -3
- package/dist/src/orchestrator/registry.d.ts +2 -1
- package/dist/src/orchestrator/registry.d.ts.map +1 -1
- package/dist/src/orchestrator/registry.js +13 -0
- package/dist/src/orchestrator/types.d.ts +114 -28
- package/dist/src/orchestrator/types.d.ts.map +1 -1
- package/dist/src/orchestrator/types.js +1 -5
- package/dist/src/smart-sessions/index.d.ts +5 -0
- package/dist/src/smart-sessions/index.d.ts.map +1 -0
- package/dist/src/smart-sessions/index.js +6 -0
- package/dist/src/types.d.ts +298 -29
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/index.d.ts +59 -0
- package/dist/src/utils/index.d.ts.map +1 -1
- package/dist/src/utils/index.js +59 -0
- package/package.json +4 -8
- package/dist/src/actions/recovery.d.ts +0 -33
- package/dist/src/actions/recovery.d.ts.map +0 -1
- package/dist/src/actions/recovery.js +0 -189
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LibZip } from 'solady';
|
|
2
|
-
import { concat, createPublicClient, encodeAbiParameters, encodeFunctionData, encodePacked, hashStruct, isHex, keccak256, maxUint256, padHex, size, toFunctionSelector, toHex, zeroAddress, zeroHash, } from 'viem';
|
|
2
|
+
import { concat, createPublicClient, encodeAbiParameters, encodeFunctionData, encodePacked, hashStruct, isAddressEqual, isHex, keccak256, maxUint256, padHex, size, toFunctionSelector, toHex, zeroAddress, zeroHash, } from 'viem';
|
|
3
3
|
import { mainnet } from 'viem/chains';
|
|
4
4
|
import { getAccountProvider } from '../../accounts/index.js';
|
|
5
5
|
import { K1_DEFAULT_VALIDATOR_ADDRESS } from '../../accounts/startale.js';
|
|
@@ -9,8 +9,10 @@ import { signTypedData } from '../../execution/utils.js';
|
|
|
9
9
|
import { getChainById, getWrappedTokenAddress, } from '../../orchestrator/registry.js';
|
|
10
10
|
import smartSessionEmissaryAbi from '../abi/smart-session-emissary.js';
|
|
11
11
|
import { MODULE_TYPE_ID_VALIDATOR } from '../common.js';
|
|
12
|
-
import { getOwnerValidator, getValidator, SMART_SESSION_EMISSARY_ADDRESS, SMART_SESSION_EMISSARY_ADDRESS_DEV, } from './core.js';
|
|
13
|
-
import {
|
|
12
|
+
import { getOwnerValidator, getValidator, ownerSetUsesEns, SMART_SESSION_EMISSARY_ADDRESS, SMART_SESSION_EMISSARY_ADDRESS_DEV, } from './core.js';
|
|
13
|
+
import { resolveCrossChainPermission } from './cross-chain-permits.js';
|
|
14
|
+
import { FAR_FUTURE_MS, resolvePermissions } from './permissions.js';
|
|
15
|
+
import { getArbitersForSettlementLayers } from './policies/claim/arbiters.js';
|
|
14
16
|
import { encodePermit2ClaimPolicyInitData, PERMIT2_CLAIM_POLICY_ADDRESS, } from './policies/claim/permit2.js';
|
|
15
17
|
const types = {
|
|
16
18
|
PolicyData: [
|
|
@@ -68,12 +70,13 @@ const SMART_SESSIONS_FALLBACK_TARGET_SELECTOR_FLAG_PERMITTED_TO_CALL_SMARTSESSIO
|
|
|
68
70
|
// intentionally uncommon.
|
|
69
71
|
const DUMMY_PRECLAIMOP_TARGET = '0x0000000000000000000000000000000000000420';
|
|
70
72
|
const DUMMY_PRECLAIMOP_SELECTOR = '0x69123456';
|
|
71
|
-
const SPENDING_LIMITS_POLICY_ADDRESS = '
|
|
72
|
-
const TIME_FRAME_POLICY_ADDRESS = '
|
|
73
|
-
const SUDO_POLICY_ADDRESS = '
|
|
74
|
-
const UNIVERSAL_ACTION_POLICY_ADDRESS = '
|
|
75
|
-
const
|
|
76
|
-
const
|
|
73
|
+
const SPENDING_LIMITS_POLICY_ADDRESS = '0x000000000033212E272655D8a22402Db819477A6';
|
|
74
|
+
const TIME_FRAME_POLICY_ADDRESS = '0x0000000000D30f611fA3bf652ac6879428586930';
|
|
75
|
+
const SUDO_POLICY_ADDRESS = '0x0000000000FEEc8D74e3143fBaBbca515358d869';
|
|
76
|
+
const UNIVERSAL_ACTION_POLICY_ADDRESS = '0x0000000000714Cf48FcF88A0bFBa70d313415032';
|
|
77
|
+
const ARG_POLICY_ADDRESS = '0x0000000000167edE64D8751daACDdC0312565a73';
|
|
78
|
+
const USAGE_LIMIT_POLICY_ADDRESS = '0x00000000001d4479FA2A947026204d0283ceDe4B';
|
|
79
|
+
const VALUE_LIMIT_POLICY_ADDRESS = '0x000000000021dC45451291BCDfc9f0B46d6f0278';
|
|
77
80
|
const INTENT_EXECUTION_POLICY_ADDRESS = '0xe9eA54d063975cDee9e06b7636d5563d95a7A23C';
|
|
78
81
|
const INTENT_EXECUTION_POLICY_ADDRESS_DEV = '0xa09b47de6e510cbdc18b97e9239bedcb44fb4901';
|
|
79
82
|
const ACTION_CONDITION_EQUAL = 0;
|
|
@@ -83,6 +86,44 @@ const ACTION_CONDITION_GREATER_THAN_OR_EQUAL = 3;
|
|
|
83
86
|
const ACTION_CONDITION_LESS_THAN_OR_EQUAL = 4;
|
|
84
87
|
const ACTION_CONDITION_NOT_EQUAL = 5;
|
|
85
88
|
const ACTION_CONDITION_IN_RANGE = 6;
|
|
89
|
+
// ArgPolicy expression-tree node packing — must match ArgPolicyTreeLib.sol:
|
|
90
|
+
// bits 0..1 : node type (0=rule, 1=NOT, 2=AND, 3=OR)
|
|
91
|
+
// bits 2..9 : rule index (rule nodes only)
|
|
92
|
+
// bits 10..17 : left/only child index
|
|
93
|
+
// bits 18..25 : right child index (AND/OR only)
|
|
94
|
+
const ARG_NODE_TYPE_RULE = 0n;
|
|
95
|
+
const ARG_NODE_TYPE_NOT = 1n;
|
|
96
|
+
const ARG_NODE_TYPE_AND = 2n;
|
|
97
|
+
const ARG_NODE_TYPE_OR = 3n;
|
|
98
|
+
const ARG_RULE_INDEX_SHIFT = 2n;
|
|
99
|
+
const ARG_LEFT_CHILD_SHIFT = 10n;
|
|
100
|
+
const ARG_RIGHT_CHILD_SHIFT = 18n;
|
|
101
|
+
// On-chain caps from ArgPolicyTreeLib.sol; fail early instead of letting
|
|
102
|
+
// the deployment revert with `TooManyRules` / `TooManyNodes`.
|
|
103
|
+
const ARG_MAX_RULES = 128;
|
|
104
|
+
const ARG_MAX_NODES = 256;
|
|
105
|
+
const DEFAULT_POLICY_ADDRESSES = {
|
|
106
|
+
sudo: SUDO_POLICY_ADDRESS,
|
|
107
|
+
universalAction: UNIVERSAL_ACTION_POLICY_ADDRESS,
|
|
108
|
+
argPolicy: ARG_POLICY_ADDRESS,
|
|
109
|
+
spendingLimits: SPENDING_LIMITS_POLICY_ADDRESS,
|
|
110
|
+
timeFrame: TIME_FRAME_POLICY_ADDRESS,
|
|
111
|
+
usageLimit: USAGE_LIMIT_POLICY_ADDRESS,
|
|
112
|
+
valueLimit: VALUE_LIMIT_POLICY_ADDRESS,
|
|
113
|
+
};
|
|
114
|
+
function resolvePolicyAddresses(overrides) {
|
|
115
|
+
if (!overrides)
|
|
116
|
+
return DEFAULT_POLICY_ADDRESSES;
|
|
117
|
+
return {
|
|
118
|
+
sudo: overrides.sudo ?? DEFAULT_POLICY_ADDRESSES.sudo,
|
|
119
|
+
universalAction: overrides.universalAction ?? DEFAULT_POLICY_ADDRESSES.universalAction,
|
|
120
|
+
argPolicy: overrides.argPolicy ?? DEFAULT_POLICY_ADDRESSES.argPolicy,
|
|
121
|
+
spendingLimits: overrides.spendingLimits ?? DEFAULT_POLICY_ADDRESSES.spendingLimits,
|
|
122
|
+
timeFrame: overrides.timeFrame ?? DEFAULT_POLICY_ADDRESSES.timeFrame,
|
|
123
|
+
usageLimit: overrides.usageLimit ?? DEFAULT_POLICY_ADDRESSES.usageLimit,
|
|
124
|
+
valueLimit: overrides.valueLimit ?? DEFAULT_POLICY_ADDRESSES.valueLimit,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
86
127
|
function packSignature(signers, validatorSignature) {
|
|
87
128
|
const session = signers.session;
|
|
88
129
|
const permissionId = getPermissionId(session);
|
|
@@ -389,8 +430,77 @@ async function getEnableSessionCall(account, session, enableSessionSignature, ha
|
|
|
389
430
|
}),
|
|
390
431
|
};
|
|
391
432
|
}
|
|
433
|
+
// SignedPermissionDisable(address account,bytes32 permissionId,bytes12 lockTag,uint256 expires,uint256 nonce)
|
|
434
|
+
// Vendored from contracts/smart-sessions-v2/src/lib/HashLibV2.sol.
|
|
435
|
+
const SIGNED_PERMISSION_DISABLE_TYPEHASH = '0x098b3120e60a8adc9d970dec9c1f8796974a3ab6154f995ad56ee7b9a38d8836';
|
|
436
|
+
// Builds a `removeConfig` call that disables a single smart session.
|
|
437
|
+
//
|
|
438
|
+
// The disable user-signature (`disableData.userSig`) is left empty: the
|
|
439
|
+
// emissary only verifies it when `msg.sender != account`
|
|
440
|
+
// (SignatureLib.verifySignatures), and here the account itself executes the
|
|
441
|
+
// call, so the disable is authorized by the outer transaction the user signs
|
|
442
|
+
// normally. The `disableDigest` leaf still has to match on-chain, so we build
|
|
443
|
+
// it from the current emissary nonce + `expires`. No-allocator flow only
|
|
444
|
+
// (`allocator == zeroAddress`), matching how enable is used today.
|
|
445
|
+
async function getDisableSessionCall(account, session, expires, provider, useDevContracts) {
|
|
446
|
+
const lockTag = '0x000000000000000000000000';
|
|
447
|
+
const permissionId = getPermissionId(session);
|
|
448
|
+
const nonce = await getSessionNonce(account, session, lockTag, provider, useDevContracts);
|
|
449
|
+
// SmartSessionLens reuses the session multichain wrapper for the disable
|
|
450
|
+
// digest, plugging this leaf in as `sessionDigest` (HashLibV2.disableDigest).
|
|
451
|
+
const disableDigest = keccak256(encodeAbiParameters([
|
|
452
|
+
{ type: 'bytes32' },
|
|
453
|
+
{ type: 'address' },
|
|
454
|
+
{ type: 'bytes32' },
|
|
455
|
+
{ type: 'bytes12' },
|
|
456
|
+
{ type: 'uint256' },
|
|
457
|
+
{ type: 'uint256' },
|
|
458
|
+
], [
|
|
459
|
+
SIGNED_PERMISSION_DISABLE_TYPEHASH,
|
|
460
|
+
account,
|
|
461
|
+
permissionId,
|
|
462
|
+
lockTag,
|
|
463
|
+
expires,
|
|
464
|
+
nonce,
|
|
465
|
+
]));
|
|
466
|
+
const hashesAndChainIds = [
|
|
467
|
+
{ chainId: BigInt(session.chain.id), sessionDigest: disableDigest },
|
|
468
|
+
];
|
|
469
|
+
return {
|
|
470
|
+
to: getSmartSessionEmissaryAddress(useDevContracts),
|
|
471
|
+
data: encodeFunctionData({
|
|
472
|
+
abi: smartSessionEmissaryAbi,
|
|
473
|
+
functionName: 'removeConfig',
|
|
474
|
+
args: [
|
|
475
|
+
account,
|
|
476
|
+
{
|
|
477
|
+
scope: SCOPE_MULTICHAIN,
|
|
478
|
+
resetPeriod: RESET_PERIOD_ONE_WEEK,
|
|
479
|
+
allocator: zeroAddress,
|
|
480
|
+
permissionId,
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
allocatorSig: '0x',
|
|
484
|
+
userSig: '0x',
|
|
485
|
+
expires,
|
|
486
|
+
session: {
|
|
487
|
+
chainDigestIndex: 0,
|
|
488
|
+
hashesAndChainIds,
|
|
489
|
+
},
|
|
490
|
+
},
|
|
491
|
+
],
|
|
492
|
+
}),
|
|
493
|
+
};
|
|
494
|
+
}
|
|
392
495
|
function toSession(definition, options = {}) {
|
|
393
|
-
const
|
|
496
|
+
const addresses = resolvePolicyAddresses(definition.policyAddresses);
|
|
497
|
+
const sessionData = resolveSessionData(definition, options.useDevContracts, addresses);
|
|
498
|
+
// Cross-chain permits add synthesized claim policies that must appear
|
|
499
|
+
// on the returned `Session` too — `getSessionData(session)` re-encodes
|
|
500
|
+
// from `session.claimPolicies` at signing time, so missing entries
|
|
501
|
+
// here would diverge from what `resolveSessionData` already baked
|
|
502
|
+
// into `sessionData.claimPolicies` for the permission-ID hash.
|
|
503
|
+
const expandedCrossChainClaims = (definition.crossChainPermits ?? []).map((p) => expandCrossChainPermit(p, options.useDevContracts).claim);
|
|
394
504
|
return {
|
|
395
505
|
chain: definition.chain,
|
|
396
506
|
owners: definition.owners,
|
|
@@ -401,8 +511,174 @@ function toSession(definition, options = {}) {
|
|
|
401
511
|
salt: sessionData.salt,
|
|
402
512
|
erc7739Policies: sessionData.erc7739Policies,
|
|
403
513
|
actions: sessionData.actions,
|
|
404
|
-
claimPolicies:
|
|
514
|
+
claimPolicies: [
|
|
515
|
+
...(definition.claimPolicies ?? []),
|
|
516
|
+
...expandedCrossChainClaims,
|
|
517
|
+
],
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Expands one {@link CrossChainPermit} into:
|
|
522
|
+
*
|
|
523
|
+
* - a {@link Permit2ClaimPolicy} for `SessionDefinition.claimPolicies`
|
|
524
|
+
* (gates Permit2 arbiter settlement) — settlement layers are resolved
|
|
525
|
+
* to canonical arbiter addresses via
|
|
526
|
+
* {@link getArbitersForSettlementLayers}.
|
|
527
|
+
* - optional {@link SpendingLimitsPolicy} / {@link TimeFramePolicy}
|
|
528
|
+
* entries for the session's fallback action — the claim policy itself
|
|
529
|
+
* doesn't enforce amounts or expiry on-chain, so we lift those
|
|
530
|
+
* guarantees into action-level policies that do.
|
|
531
|
+
*
|
|
532
|
+
* An Intent Executor policy is intentionally NOT synthesized: the
|
|
533
|
+
* params-bearing on-chain contract is still being designed in
|
|
534
|
+
* smart-sessions-v2 (PR #46). Once it lands, this helper grows a second
|
|
535
|
+
* branch that emits matching constraints there.
|
|
536
|
+
*/
|
|
537
|
+
function expandCrossChainPermit(input, useDevContracts) {
|
|
538
|
+
const permit = resolveCrossChainPermission(input);
|
|
539
|
+
// `from`/`to` are optional: an absent leg list means "no token/chain
|
|
540
|
+
// restriction on that side" — we emit `undefined` so the underlying
|
|
541
|
+
// Permit2 policy skips the check entirely (matching how every other
|
|
542
|
+
// policy field treats undefined as unrestricted).
|
|
543
|
+
const sourceTokens = permit.from?.length
|
|
544
|
+
? permit.from.map(({ chain, token }) => ({ chain, address: token }))
|
|
545
|
+
: undefined;
|
|
546
|
+
const destinationTokens = permit.to?.length
|
|
547
|
+
? permit.to.map(({ chain, token }) => ({ chain, address: token }))
|
|
548
|
+
: undefined;
|
|
549
|
+
// Only emit a `recipients` whitelist when at least one destination
|
|
550
|
+
// leg pins a concrete recipient (or the explicit `'any'` sentinel).
|
|
551
|
+
// Leaving the field undefined disables the on-chain recipient check
|
|
552
|
+
// entirely; emitting `['any', ...]` would force the contract into
|
|
553
|
+
// recipient-check mode without actually restricting anything.
|
|
554
|
+
const recipientsList = (permit.to ?? [])
|
|
555
|
+
.filter((t) => t.recipient !== undefined)
|
|
556
|
+
.map((t) => ({
|
|
557
|
+
chain: t.chain,
|
|
558
|
+
address: t.recipient,
|
|
559
|
+
}));
|
|
560
|
+
const recipients = recipientsList.length ? recipientsList : undefined;
|
|
561
|
+
const permitDeadline = permit.validAfter !== undefined || permit.validUntil !== undefined
|
|
562
|
+
? { min: permit.validAfter, max: permit.validUntil }
|
|
563
|
+
: undefined;
|
|
564
|
+
// Resolve the dev-friendly settlement-layer selectors into the actual
|
|
565
|
+
// Permit2 arbiter addresses the on-chain policy enforces. Empty or
|
|
566
|
+
// undefined ⇒ union of all supported layers (never an empty
|
|
567
|
+
// whitelist that disables the on-chain check).
|
|
568
|
+
const spenders = getArbitersForSettlementLayers(permit.settlementLayers, useDevContracts);
|
|
569
|
+
const claim = {
|
|
570
|
+
type: 'permit2',
|
|
571
|
+
spenders,
|
|
572
|
+
sourceTokens,
|
|
573
|
+
destinationTokens,
|
|
574
|
+
recipients,
|
|
575
|
+
recipientIsAccount: permit.recipientIsAccount,
|
|
576
|
+
permitDeadline,
|
|
577
|
+
fillDeadline: permit.fillDeadline,
|
|
405
578
|
};
|
|
579
|
+
const fallbackPolicies = [];
|
|
580
|
+
// Per-origin amount caps: the Permit2 claim policy doesn't enforce
|
|
581
|
+
// amounts on-chain, so we lift those caps into a separate
|
|
582
|
+
// `SpendingLimitsPolicy` on the fallback action. This keeps the
|
|
583
|
+
// user-facing promise ("at most X of token Y") honest.
|
|
584
|
+
const limits = (permit.from ?? [])
|
|
585
|
+
.filter((f) => f.maxAmount !== undefined)
|
|
586
|
+
.map((f) => ({ token: f.token, amount: f.maxAmount }));
|
|
587
|
+
if (limits.length) {
|
|
588
|
+
fallbackPolicies.push({ type: 'spending-limits', limits });
|
|
589
|
+
}
|
|
590
|
+
// Time-frame: the on-chain TimeFramePolicy expects millisecond inputs
|
|
591
|
+
// (see `case 'time-frame'` in getPolicyData), while CrossChainPermit
|
|
592
|
+
// uses unix-seconds bigints — matching the Permit2 deadline
|
|
593
|
+
// convention. We convert at the boundary so both encoders see their
|
|
594
|
+
// preferred unit.
|
|
595
|
+
//
|
|
596
|
+
// One-sided bounds get always-passing defaults, mirroring the
|
|
597
|
+
// permission resolver: an unset `validUntil` defaults to the year-2100
|
|
598
|
+
// sentinel (NOT 0 — that would make the policy expired the moment
|
|
599
|
+
// `validAfter` is reached), and an unset `validAfter` defaults to 0.
|
|
600
|
+
if (permitDeadline) {
|
|
601
|
+
const validUntilMs = permit.validUntil !== undefined
|
|
602
|
+
? Number(permit.validUntil * 1000n)
|
|
603
|
+
: FAR_FUTURE_MS;
|
|
604
|
+
const validAfterMs = permit.validAfter !== undefined ? Number(permit.validAfter * 1000n) : 0;
|
|
605
|
+
fallbackPolicies.push({
|
|
606
|
+
type: 'time-frame',
|
|
607
|
+
validUntil: validUntilMs,
|
|
608
|
+
validAfter: validAfterMs,
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
return { claim, fallbackPolicies };
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* True when `message` satisfies every dimension `policy` constrains. Used to
|
|
615
|
+
* pick the correct claim policy at Permit2-signing time when a session holds
|
|
616
|
+
* more than one (e.g. a user claim policy plus one or more cross-chain
|
|
617
|
+
* permits).
|
|
618
|
+
*
|
|
619
|
+
* The on-chain emissary validates a Permit2 claim against the matching claim
|
|
620
|
+
* policy, and `buildPermit2ClaimPolicyCalldata` expands the message
|
|
621
|
+
* differently per policy (the calldata layout depends on which mode bits a
|
|
622
|
+
* policy enables). Signing calldata for the wrong policy decodes incorrectly
|
|
623
|
+
* on-chain and fails ERC-1271 validation — hence we must match.
|
|
624
|
+
*
|
|
625
|
+
* Only constrained dimensions are checked; an unset policy field imposes no
|
|
626
|
+
* requirement (mirrors the contract's "skip the check" semantics). Source
|
|
627
|
+
* tokens are matched by address alone because the Permit2 message carries no
|
|
628
|
+
* origin chain id; destination tokens / recipients are scoped to the
|
|
629
|
+
* mandate's `targetChain`.
|
|
630
|
+
*/
|
|
631
|
+
function permit2ClaimPolicyMatchesMessage(policy, message) {
|
|
632
|
+
// Spender (arbiter) — the primary discriminator between settlement layers.
|
|
633
|
+
if (policy.spenders?.length) {
|
|
634
|
+
if (!policy.spenders.some((s) => isAddressEqual(s, message.spender))) {
|
|
635
|
+
return false;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
// Source tokens: every permitted token must be allow-listed (by address).
|
|
639
|
+
if (policy.sourceTokens?.length) {
|
|
640
|
+
const allowed = new Set(policy.sourceTokens.map((t) => t.address.toLowerCase()));
|
|
641
|
+
if (!message.permitted.every((p) => allowed.has(p.token.toLowerCase()))) {
|
|
642
|
+
return false;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
const targetChain = message.mandate.target.targetChain;
|
|
646
|
+
// Destination tokens: every mandate output token must be allow-listed for
|
|
647
|
+
// the destination chain.
|
|
648
|
+
if (policy.destinationTokens?.length) {
|
|
649
|
+
const allowed = new Set(policy.destinationTokens
|
|
650
|
+
.filter((t) => BigInt(t.chain.id) === targetChain)
|
|
651
|
+
.map((t) => t.address.toLowerCase()));
|
|
652
|
+
if (!message.mandate.target.tokenOut.every((o) => allowed.has(o.token.toLowerCase()))) {
|
|
653
|
+
return false;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
// Recipient: scoped to the destination chain. An `'any'` entry for that
|
|
657
|
+
// chain accepts any recipient.
|
|
658
|
+
if (policy.recipients?.length) {
|
|
659
|
+
const entries = policy.recipients.filter((r) => BigInt(r.chain.id) === targetChain);
|
|
660
|
+
if (entries.length) {
|
|
661
|
+
const recipient = message.mandate.target.recipient;
|
|
662
|
+
const matches = entries.some((r) => r.address === 'any' || isAddressEqual(r.address, recipient));
|
|
663
|
+
if (!matches)
|
|
664
|
+
return false;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
return true;
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Selects the claim policy whose constraints the given Permit2 message
|
|
671
|
+
* satisfies. With 0–1 policies the choice is trivial; with several, returns
|
|
672
|
+
* the first match in list order (aligns with the contract iterating its
|
|
673
|
+
* stored claim policies). Falls back to the first policy when nothing matches
|
|
674
|
+
* so behaviour is never worse than the previous always-`[0]` logic — the
|
|
675
|
+
* resulting on-chain failure is then identical to before.
|
|
676
|
+
*/
|
|
677
|
+
function selectPermit2ClaimPolicyForMessage(claimPolicies, message) {
|
|
678
|
+
if (claimPolicies.length <= 1)
|
|
679
|
+
return claimPolicies[0];
|
|
680
|
+
return (claimPolicies.find((p) => permit2ClaimPolicyMatchesMessage(p, message)) ??
|
|
681
|
+
claimPolicies[0]);
|
|
406
682
|
}
|
|
407
683
|
function resolvePermit2ClaimPolicy(policy) {
|
|
408
684
|
return {
|
|
@@ -429,7 +705,13 @@ function resolvePermit2ClaimPolicy(policy) {
|
|
|
429
705
|
})),
|
|
430
706
|
};
|
|
431
707
|
}
|
|
432
|
-
function resolveSessionData(session, useDevContracts) {
|
|
708
|
+
function resolveSessionData(session, useDevContracts, addresses = DEFAULT_POLICY_ADDRESSES) {
|
|
709
|
+
// ENS validation is HCA-only, and HCA accounts cannot install the smart
|
|
710
|
+
// session validator, so an ENS session owner would silently resolve to the
|
|
711
|
+
// HCA module and sign/enable against a validator the account does not have.
|
|
712
|
+
if (ownerSetUsesEns(session.owners)) {
|
|
713
|
+
throw new Error('ENS owners are not supported for smart sessions');
|
|
714
|
+
}
|
|
433
715
|
const validator = getValidator(session.owners);
|
|
434
716
|
const allowedContent = [
|
|
435
717
|
{
|
|
@@ -441,7 +723,7 @@ function resolveSessionData(session, useDevContracts) {
|
|
|
441
723
|
allowedERC7739Content: allowedContent,
|
|
442
724
|
erc1271Policies: [
|
|
443
725
|
{
|
|
444
|
-
policy:
|
|
726
|
+
policy: addresses.sudo,
|
|
445
727
|
initData: '0x',
|
|
446
728
|
},
|
|
447
729
|
],
|
|
@@ -451,7 +733,7 @@ function resolveSessionData(session, useDevContracts) {
|
|
|
451
733
|
actionTarget: SMART_SESSIONS_FALLBACK_TARGET_FLAG,
|
|
452
734
|
actionPolicies: [
|
|
453
735
|
{
|
|
454
|
-
policy:
|
|
736
|
+
policy: addresses.sudo,
|
|
455
737
|
initData: '0x',
|
|
456
738
|
},
|
|
457
739
|
],
|
|
@@ -459,6 +741,14 @@ function resolveSessionData(session, useDevContracts) {
|
|
|
459
741
|
const userActions = session.permissions?.length
|
|
460
742
|
? resolvePermissions(session.permissions)
|
|
461
743
|
: [];
|
|
744
|
+
// Expand every cross-chain permit into a (claim, fallbackPolicies)
|
|
745
|
+
// pair. The claim policies go onto `session.claimPolicies` (Permit2
|
|
746
|
+
// settlement); the fallback policies (SpendingLimits / TimeFrame
|
|
747
|
+
// derived from maxAmount / validUntil) are appended to the injected
|
|
748
|
+
// fallback action below.
|
|
749
|
+
const expandedPermits = (session.crossChainPermits ?? []).map((p) => expandCrossChainPermit(p, useDevContracts));
|
|
750
|
+
const extraClaimPolicies = expandedPermits.map((e) => e.claim);
|
|
751
|
+
const permitFallbackPolicies = expandedPermits.flatMap((e) => e.fallbackPolicies);
|
|
462
752
|
const injectedActions = [
|
|
463
753
|
// Native token wrapping
|
|
464
754
|
{
|
|
@@ -471,8 +761,17 @@ function resolveSessionData(session, useDevContracts) {
|
|
|
471
761
|
stateMutability: 'payable',
|
|
472
762
|
}),
|
|
473
763
|
},
|
|
474
|
-
// Intent-execution fallback for any non-scoped call.
|
|
475
|
-
|
|
764
|
+
// Intent-execution fallback for any non-scoped call. Cross-chain
|
|
765
|
+
// permits attach their synthesized SpendingLimits / TimeFrame
|
|
766
|
+
// guardrails to this same fallback so the on-chain emissary applies
|
|
767
|
+
// them when the orchestrator drives a bridge through the fallback
|
|
768
|
+
// action path.
|
|
769
|
+
{
|
|
770
|
+
policies: [
|
|
771
|
+
{ type: 'intent-execution' },
|
|
772
|
+
...permitFallbackPolicies,
|
|
773
|
+
],
|
|
774
|
+
},
|
|
476
775
|
// Dummy action: allows the filler to call verifyExecution in ENABLE mode using
|
|
477
776
|
// an injected dummy preclaimop so any session can be enabled on-chain without
|
|
478
777
|
// a separate UserOp, regardless of whether it has claim or action policies.
|
|
@@ -483,7 +782,12 @@ function resolveSessionData(session, useDevContracts) {
|
|
|
483
782
|
},
|
|
484
783
|
];
|
|
485
784
|
const allActions = [...userActions, ...injectedActions];
|
|
486
|
-
|
|
785
|
+
// When there are cross-chain permits but no explicit user actions, we
|
|
786
|
+
// still want the injected fallback (with its synthesized guardrails)
|
|
787
|
+
// to land on-chain — otherwise the legacy `[sudoAction]` path drops
|
|
788
|
+
// the SpendingLimits / TimeFrame the user explicitly asked for.
|
|
789
|
+
const needsInjection = userActions.length || permitFallbackPolicies.length;
|
|
790
|
+
const actions = needsInjection
|
|
487
791
|
? allActions.map((action) => ({
|
|
488
792
|
actionTargetSelector: 'selector' in action
|
|
489
793
|
? action.selector
|
|
@@ -491,24 +795,33 @@ function resolveSessionData(session, useDevContracts) {
|
|
|
491
795
|
actionTarget: 'target' in action
|
|
492
796
|
? action.target
|
|
493
797
|
: SMART_SESSIONS_FALLBACK_TARGET_FLAG,
|
|
494
|
-
actionPolicies: action.policies?.map((policy) => getPolicyData(policy, useDevContracts)) ?? [
|
|
798
|
+
actionPolicies: action.policies?.map((policy) => getPolicyData(policy, useDevContracts, addresses)) ?? [
|
|
495
799
|
{
|
|
496
|
-
policy:
|
|
800
|
+
policy: addresses.sudo,
|
|
497
801
|
initData: '0x',
|
|
498
802
|
},
|
|
499
803
|
],
|
|
500
804
|
}))
|
|
501
805
|
: [sudoAction];
|
|
806
|
+
// Concatenate user-supplied claim policies with the ones synthesized
|
|
807
|
+
// from cross-chain permits. Order is not security-sensitive — the
|
|
808
|
+
// on-chain contract iterates the full list — but we keep user
|
|
809
|
+
// policies first for deterministic permission IDs across SDK
|
|
810
|
+
// releases that add permit expansion.
|
|
811
|
+
const allClaimPolicies = [
|
|
812
|
+
...(session.claimPolicies ?? []),
|
|
813
|
+
...extraClaimPolicies,
|
|
814
|
+
];
|
|
502
815
|
return {
|
|
503
816
|
sessionValidator: validator.address,
|
|
504
817
|
salt: zeroHash,
|
|
505
818
|
sessionValidatorInitData: validator.initData,
|
|
506
819
|
erc7739Policies: erc7739Data,
|
|
507
820
|
actions,
|
|
508
|
-
claimPolicies:
|
|
821
|
+
claimPolicies: allClaimPolicies.map((policy) => ({
|
|
509
822
|
policy: PERMIT2_CLAIM_POLICY_ADDRESS,
|
|
510
823
|
initData: encodePermit2ClaimPolicyInitData(resolvePermit2ClaimPolicy(policy)),
|
|
511
|
-
}))
|
|
824
|
+
})),
|
|
512
825
|
};
|
|
513
826
|
}
|
|
514
827
|
function getSessionData(session) {
|
|
@@ -547,11 +860,88 @@ function getPermissionIdFromData(sessionData) {
|
|
|
547
860
|
sessionData.salt,
|
|
548
861
|
]));
|
|
549
862
|
}
|
|
550
|
-
function
|
|
863
|
+
function getActionConditionId(condition) {
|
|
864
|
+
switch (condition) {
|
|
865
|
+
case 'equal':
|
|
866
|
+
return ACTION_CONDITION_EQUAL;
|
|
867
|
+
case 'greaterThan':
|
|
868
|
+
return ACTION_CONDITION_GREATER_THAN;
|
|
869
|
+
case 'lessThan':
|
|
870
|
+
return ACTION_CONDITION_LESS_THAN;
|
|
871
|
+
case 'greaterThanOrEqual':
|
|
872
|
+
return ACTION_CONDITION_GREATER_THAN_OR_EQUAL;
|
|
873
|
+
case 'lessThanOrEqual':
|
|
874
|
+
return ACTION_CONDITION_LESS_THAN_OR_EQUAL;
|
|
875
|
+
case 'notEqual':
|
|
876
|
+
return ACTION_CONDITION_NOT_EQUAL;
|
|
877
|
+
case 'inRange':
|
|
878
|
+
return ACTION_CONDITION_IN_RANGE;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
function encodeActionParamRule(rule) {
|
|
882
|
+
const ref = isHex(rule.referenceValue)
|
|
883
|
+
? padHex(rule.referenceValue)
|
|
884
|
+
: toHex(rule.referenceValue, { size: 32 });
|
|
885
|
+
return {
|
|
886
|
+
condition: getActionConditionId(rule.condition),
|
|
887
|
+
offset: rule.calldataOffset,
|
|
888
|
+
isLimited: rule.usageLimit !== undefined,
|
|
889
|
+
ref,
|
|
890
|
+
usage: {
|
|
891
|
+
limit: rule.usageLimit ?? 0n,
|
|
892
|
+
used: 0n,
|
|
893
|
+
},
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
// Compile an ArgPolicyExpression AST into the on-chain wire format
|
|
897
|
+
// (flat rules[] + bit-packed nodes[] + rootNodeIndex). Post-order walk:
|
|
898
|
+
// children get appended before their parent so a parent's child indices
|
|
899
|
+
// always reference earlier slots.
|
|
900
|
+
function compileArgPolicyExpression(expr) {
|
|
901
|
+
const rules = [];
|
|
902
|
+
const nodes = [];
|
|
903
|
+
function walk(e) {
|
|
904
|
+
switch (e.type) {
|
|
905
|
+
case 'rule': {
|
|
906
|
+
const ruleIdx = rules.length;
|
|
907
|
+
rules.push(encodeActionParamRule(e.rule));
|
|
908
|
+
const nodeIdx = nodes.length;
|
|
909
|
+
nodes.push(ARG_NODE_TYPE_RULE | (BigInt(ruleIdx) << ARG_RULE_INDEX_SHIFT));
|
|
910
|
+
return nodeIdx;
|
|
911
|
+
}
|
|
912
|
+
case 'not': {
|
|
913
|
+
const childIdx = walk(e.child);
|
|
914
|
+
const nodeIdx = nodes.length;
|
|
915
|
+
nodes.push(ARG_NODE_TYPE_NOT | (BigInt(childIdx) << ARG_LEFT_CHILD_SHIFT));
|
|
916
|
+
return nodeIdx;
|
|
917
|
+
}
|
|
918
|
+
case 'and':
|
|
919
|
+
case 'or': {
|
|
920
|
+
const leftIdx = walk(e.left);
|
|
921
|
+
const rightIdx = walk(e.right);
|
|
922
|
+
const nodeIdx = nodes.length;
|
|
923
|
+
const nodeType = e.type === 'and' ? ARG_NODE_TYPE_AND : ARG_NODE_TYPE_OR;
|
|
924
|
+
nodes.push(nodeType |
|
|
925
|
+
(BigInt(leftIdx) << ARG_LEFT_CHILD_SHIFT) |
|
|
926
|
+
(BigInt(rightIdx) << ARG_RIGHT_CHILD_SHIFT));
|
|
927
|
+
return nodeIdx;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
const rootNodeIndex = walk(expr);
|
|
932
|
+
if (rules.length > ARG_MAX_RULES) {
|
|
933
|
+
throw new Error(`ArgPolicy expression has ${rules.length} rules, max is ${ARG_MAX_RULES}`);
|
|
934
|
+
}
|
|
935
|
+
if (nodes.length > ARG_MAX_NODES) {
|
|
936
|
+
throw new Error(`ArgPolicy expression has ${nodes.length} nodes, max is ${ARG_MAX_NODES}`);
|
|
937
|
+
}
|
|
938
|
+
return { rules, packedNodes: nodes, rootNodeIndex };
|
|
939
|
+
}
|
|
940
|
+
function getPolicyData(policy, useDevContracts, addresses = DEFAULT_POLICY_ADDRESSES) {
|
|
551
941
|
switch (policy.type) {
|
|
552
942
|
case 'sudo':
|
|
553
943
|
return {
|
|
554
|
-
policy:
|
|
944
|
+
policy: addresses.sudo,
|
|
555
945
|
initData: '0x',
|
|
556
946
|
};
|
|
557
947
|
case 'intent-execution':
|
|
@@ -562,24 +952,6 @@ function getPolicyData(policy, useDevContracts) {
|
|
|
562
952
|
initData: '0x',
|
|
563
953
|
};
|
|
564
954
|
case 'universal-action': {
|
|
565
|
-
function getCondition(condition) {
|
|
566
|
-
switch (condition) {
|
|
567
|
-
case 'equal':
|
|
568
|
-
return ACTION_CONDITION_EQUAL;
|
|
569
|
-
case 'greaterThan':
|
|
570
|
-
return ACTION_CONDITION_GREATER_THAN;
|
|
571
|
-
case 'lessThan':
|
|
572
|
-
return ACTION_CONDITION_LESS_THAN;
|
|
573
|
-
case 'greaterThanOrEqual':
|
|
574
|
-
return ACTION_CONDITION_GREATER_THAN_OR_EQUAL;
|
|
575
|
-
case 'lessThanOrEqual':
|
|
576
|
-
return ACTION_CONDITION_LESS_THAN_OR_EQUAL;
|
|
577
|
-
case 'notEqual':
|
|
578
|
-
return ACTION_CONDITION_NOT_EQUAL;
|
|
579
|
-
case 'inRange':
|
|
580
|
-
return ACTION_CONDITION_IN_RANGE;
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
955
|
const MAX_RULES = 16;
|
|
584
956
|
const rules = createFixedArray(MAX_RULES, () => ({
|
|
585
957
|
condition: ACTION_CONDITION_EQUAL,
|
|
@@ -589,23 +961,10 @@ function getPolicyData(policy, useDevContracts) {
|
|
|
589
961
|
usage: { limit: 0n, used: 0n },
|
|
590
962
|
}));
|
|
591
963
|
for (let i = 0; i < policy.rules.length; i++) {
|
|
592
|
-
|
|
593
|
-
const ref = isHex(rule.referenceValue)
|
|
594
|
-
? padHex(rule.referenceValue)
|
|
595
|
-
: toHex(rule.referenceValue, { size: 32 });
|
|
596
|
-
rules[i] = {
|
|
597
|
-
condition: getCondition(rule.condition),
|
|
598
|
-
offset: rule.calldataOffset,
|
|
599
|
-
isLimited: rule.usageLimit !== undefined,
|
|
600
|
-
ref,
|
|
601
|
-
usage: {
|
|
602
|
-
limit: rule.usageLimit ? rule.usageLimit : 0n,
|
|
603
|
-
used: 0n,
|
|
604
|
-
},
|
|
605
|
-
};
|
|
964
|
+
rules[i] = encodeActionParamRule(policy.rules[i]);
|
|
606
965
|
}
|
|
607
966
|
return {
|
|
608
|
-
policy:
|
|
967
|
+
policy: addresses.universalAction,
|
|
609
968
|
initData: encodeAbiParameters([
|
|
610
969
|
{
|
|
611
970
|
components: [
|
|
@@ -674,17 +1033,69 @@ function getPolicyData(policy, useDevContracts) {
|
|
|
674
1033
|
]),
|
|
675
1034
|
};
|
|
676
1035
|
}
|
|
1036
|
+
case 'arg-policy': {
|
|
1037
|
+
const { rules, packedNodes, rootNodeIndex } = compileArgPolicyExpression(policy.expression);
|
|
1038
|
+
return {
|
|
1039
|
+
policy: addresses.argPolicy,
|
|
1040
|
+
initData: encodeAbiParameters([
|
|
1041
|
+
{
|
|
1042
|
+
components: [
|
|
1043
|
+
{ name: 'valueLimitPerUse', type: 'uint256' },
|
|
1044
|
+
{
|
|
1045
|
+
components: [
|
|
1046
|
+
{ name: 'rootNodeIndex', type: 'uint8' },
|
|
1047
|
+
{
|
|
1048
|
+
components: [
|
|
1049
|
+
{ name: 'condition', type: 'uint8' },
|
|
1050
|
+
{ name: 'offset', type: 'uint64' },
|
|
1051
|
+
{ name: 'isLimited', type: 'bool' },
|
|
1052
|
+
{ name: 'ref', type: 'bytes32' },
|
|
1053
|
+
{
|
|
1054
|
+
components: [
|
|
1055
|
+
{ name: 'limit', type: 'uint256' },
|
|
1056
|
+
{ name: 'used', type: 'uint256' },
|
|
1057
|
+
],
|
|
1058
|
+
name: 'usage',
|
|
1059
|
+
type: 'tuple',
|
|
1060
|
+
},
|
|
1061
|
+
],
|
|
1062
|
+
name: 'rules',
|
|
1063
|
+
type: 'tuple[]',
|
|
1064
|
+
},
|
|
1065
|
+
{ name: 'packedNodes', type: 'uint256[]' },
|
|
1066
|
+
],
|
|
1067
|
+
name: 'paramRules',
|
|
1068
|
+
type: 'tuple',
|
|
1069
|
+
},
|
|
1070
|
+
],
|
|
1071
|
+
name: 'ActionConfig',
|
|
1072
|
+
type: 'tuple',
|
|
1073
|
+
},
|
|
1074
|
+
], [
|
|
1075
|
+
{
|
|
1076
|
+
valueLimitPerUse: policy.valueLimitPerUse ?? 0n,
|
|
1077
|
+
paramRules: {
|
|
1078
|
+
rootNodeIndex,
|
|
1079
|
+
rules,
|
|
1080
|
+
packedNodes,
|
|
1081
|
+
},
|
|
1082
|
+
},
|
|
1083
|
+
]),
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
677
1086
|
case 'spending-limits': {
|
|
678
1087
|
const tokens = policy.limits.map(({ token }) => token);
|
|
679
1088
|
const limits = policy.limits.map(({ amount }) => amount);
|
|
680
1089
|
return {
|
|
681
|
-
policy:
|
|
1090
|
+
policy: addresses.spendingLimits,
|
|
682
1091
|
initData: encodeAbiParameters([{ type: 'address[]' }, { type: 'uint256[]' }], [tokens, limits]),
|
|
683
1092
|
};
|
|
684
1093
|
}
|
|
685
1094
|
case 'time-frame': {
|
|
1095
|
+
// Deployed TimeFramePolicy slices initData[0:12] and unpacks as
|
|
1096
|
+
// uint48 validUntil || uint48 validAfter (high 48 bits || low 48 bits).
|
|
686
1097
|
return {
|
|
687
|
-
policy:
|
|
1098
|
+
policy: addresses.timeFrame,
|
|
688
1099
|
initData: encodePacked(['uint48', 'uint48'], [
|
|
689
1100
|
Math.floor(policy.validUntil / 1000),
|
|
690
1101
|
Math.floor(policy.validAfter / 1000),
|
|
@@ -693,13 +1104,13 @@ function getPolicyData(policy, useDevContracts) {
|
|
|
693
1104
|
}
|
|
694
1105
|
case 'usage-limit': {
|
|
695
1106
|
return {
|
|
696
|
-
policy:
|
|
1107
|
+
policy: addresses.usageLimit,
|
|
697
1108
|
initData: encodePacked(['uint128'], [policy.limit]),
|
|
698
1109
|
};
|
|
699
1110
|
}
|
|
700
1111
|
case 'value-limit': {
|
|
701
1112
|
return {
|
|
702
|
-
policy:
|
|
1113
|
+
policy: addresses.valueLimit,
|
|
703
1114
|
initData: encodeAbiParameters([{ type: 'uint256' }], [policy.limit]),
|
|
704
1115
|
};
|
|
705
1116
|
}
|
|
@@ -726,41 +1137,47 @@ function getSmartSessionEmissaryAddress(useDevContracts) {
|
|
|
726
1137
|
? SMART_SESSION_EMISSARY_ADDRESS_DEV
|
|
727
1138
|
: SMART_SESSION_EMISSARY_ADDRESS;
|
|
728
1139
|
}
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
function buildMockSignature(session, useDevContracts, chainCount = 1, targetChainId) {
|
|
1140
|
+
function buildMockSignature(session, useDevContracts, chainCount = 1, targetChainId,
|
|
1141
|
+
// Defaults to the historical first-use ENABLE shape.
|
|
1142
|
+
shape = 'enable') {
|
|
1143
|
+
// packSignature keys ENABLE vs USE on enableData *presence* within the
|
|
1144
|
+
// verifyExecutions branch, and takes the ERC-1271 branch when verifyExecutions
|
|
1145
|
+
// is false — so derive both from the requested shape.
|
|
1146
|
+
const verifyExecutions = shape !== 'erc1271';
|
|
1147
|
+
const includeEnableData = shape === 'enable';
|
|
738
1148
|
const emissaryAddress = getSmartSessionEmissaryAddress(useDevContracts);
|
|
739
|
-
//
|
|
740
|
-
//
|
|
741
|
-
//
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
1149
|
+
// Include enableData only when the session is actually being enabled (ENABLE
|
|
1150
|
+
// shape) — its presence is what makes packSignature emit 0x01 vs 0x00, and it's
|
|
1151
|
+
// ignored entirely by the USE / ERC-1271 shapes. Built lazily so steady-state
|
|
1152
|
+
// shapes skip the chainId-entry allocation.
|
|
1153
|
+
let enableData;
|
|
1154
|
+
if (includeEnableData) {
|
|
1155
|
+
// Use targetChainId when provided (per-chain mockSignatures path) so the mock
|
|
1156
|
+
// emissary's chainId check passes on the correct chain. Falls back to
|
|
1157
|
+
// session.chain.id for the global mockSignature (single-chain path).
|
|
1158
|
+
const primaryChainId = targetChainId ?? session.chain.id;
|
|
1159
|
+
// Normalize chainCount to a finite positive integer — guards against NaN/
|
|
1160
|
+
// undefined from callers (e.g. `sourceChains?.length`) that would otherwise
|
|
1161
|
+
// produce an empty array and silently drop the ChainId check.
|
|
1162
|
+
const safeChainCount = Number.isFinite(chainCount) && chainCount > 0 ? Math.floor(chainCount) : 1;
|
|
1163
|
+
// First entry is the real chain ID (for the ChainId check), the rest are
|
|
1164
|
+
// chainId 0 placeholders. Hash mismatch is skipped by the mock emissary, so
|
|
1165
|
+
// sessionDigest can be zeroHash throughout.
|
|
1166
|
+
const hashesAndChainIds = Array.from({ length: safeChainCount }, (_, i) => ({
|
|
1167
|
+
chainId: i === 0 ? BigInt(primaryChainId) : 0n,
|
|
1168
|
+
sessionDigest: zeroHash,
|
|
1169
|
+
}));
|
|
1170
|
+
enableData = {
|
|
760
1171
|
userSignature: `0x${'00'.repeat(65)}`,
|
|
761
1172
|
hashesAndChainIds,
|
|
762
1173
|
sessionToEnableIndex: 0,
|
|
763
|
-
}
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
const dummySigners = {
|
|
1177
|
+
type: 'experimental_session',
|
|
1178
|
+
session,
|
|
1179
|
+
verifyExecutions,
|
|
1180
|
+
...(enableData && { enableData }),
|
|
764
1181
|
};
|
|
765
1182
|
const dummyValidatorSignature = `0x${'00'.repeat(65)}`;
|
|
766
1183
|
const sigData = packSignature(dummySigners, dummyValidatorSignature);
|
|
@@ -769,4 +1186,4 @@ function buildMockSignature(session, useDevContracts, chainCount = 1, targetChai
|
|
|
769
1186
|
function createFixedArray(length, getValue) {
|
|
770
1187
|
return Array.from({ length }, (_, i) => getValue(i));
|
|
771
1188
|
}
|
|
772
|
-
export { SMART_SESSION_EMISSARY_ADDRESS, SMART_SESSION_EMISSARY_ADDRESS_DEV, SMART_SESSIONS_FALLBACK_TARGET_FLAG, SMART_SESSIONS_FALLBACK_TARGET_SELECTOR_FLAG, SMART_SESSIONS_FALLBACK_TARGET_SELECTOR_FLAG_PERMITTED_TO_CALL_SMARTSESSION, DUMMY_PRECLAIMOP_TARGET, DUMMY_PRECLAIMOP_SELECTOR, SPENDING_LIMITS_POLICY_ADDRESS, TIME_FRAME_POLICY_ADDRESS, SUDO_POLICY_ADDRESS, UNIVERSAL_ACTION_POLICY_ADDRESS, USAGE_LIMIT_POLICY_ADDRESS, VALUE_LIMIT_POLICY_ADDRESS, INTENT_EXECUTION_POLICY_ADDRESS, packSignature, toSession, resolvePermit2ClaimPolicy, getSessionData, getPolicyData, getEnableSessionCall, getPermissionId, getSmartSessionValidator, getSessionDetails, isSessionEnabled, signEnableSession, buildMockSignature, };
|
|
1189
|
+
export { SMART_SESSION_EMISSARY_ADDRESS, SMART_SESSION_EMISSARY_ADDRESS_DEV, SMART_SESSIONS_FALLBACK_TARGET_FLAG, SMART_SESSIONS_FALLBACK_TARGET_SELECTOR_FLAG, SMART_SESSIONS_FALLBACK_TARGET_SELECTOR_FLAG_PERMITTED_TO_CALL_SMARTSESSION, DUMMY_PRECLAIMOP_TARGET, DUMMY_PRECLAIMOP_SELECTOR, SPENDING_LIMITS_POLICY_ADDRESS, TIME_FRAME_POLICY_ADDRESS, SUDO_POLICY_ADDRESS, UNIVERSAL_ACTION_POLICY_ADDRESS, ARG_POLICY_ADDRESS, USAGE_LIMIT_POLICY_ADDRESS, VALUE_LIMIT_POLICY_ADDRESS, INTENT_EXECUTION_POLICY_ADDRESS, packSignature, toSession, resolvePermit2ClaimPolicy, selectPermit2ClaimPolicyForMessage, getSessionData, getPolicyData, getEnableSessionCall, getDisableSessionCall, getPermissionId, getSmartSessionValidator, getSessionDetails, isSessionEnabled, signEnableSession, buildMockSignature, };
|