@rhinestone/sdk 2.5.0 → 2.7.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.
Files changed (87) hide show
  1. package/dist/src/accounts/adapters/kernel.d.ts +0 -1
  2. package/dist/src/accounts/adapters/kernel.d.ts.map +1 -1
  3. package/dist/src/accounts/adapters/kernel.js +1 -13
  4. package/dist/src/accounts/kernel-signing.d.ts +3 -0
  5. package/dist/src/accounts/kernel-signing.d.ts.map +1 -0
  6. package/dist/src/accounts/kernel-signing.js +13 -0
  7. package/dist/src/api/account.d.ts.map +1 -1
  8. package/dist/src/api/account.js +118 -0
  9. package/dist/src/api/compose-types.d.ts +7 -0
  10. package/dist/src/api/compose-types.d.ts.map +1 -1
  11. package/dist/src/api/direct-signing.d.ts.map +1 -1
  12. package/dist/src/api/direct-signing.js +11 -6
  13. package/dist/src/api/signer-selection.js +11 -0
  14. package/dist/src/clients/orchestrator/client.js +1 -1
  15. package/dist/src/clients/orchestrator/public.d.ts +8 -1
  16. package/dist/src/clients/orchestrator/public.d.ts.map +1 -1
  17. package/dist/src/clients/orchestrator/types.d.ts +6 -0
  18. package/dist/src/clients/orchestrator/types.d.ts.map +1 -1
  19. package/dist/src/config/account.d.ts +152 -7
  20. package/dist/src/config/account.d.ts.map +1 -1
  21. package/dist/src/index.d.ts +2 -2
  22. package/dist/src/index.d.ts.map +1 -1
  23. package/dist/src/modules/validators/capabilities.d.ts.map +1 -1
  24. package/dist/src/modules/validators/capabilities.js +33 -10
  25. package/dist/src/modules/validators/contribution.d.ts.map +1 -1
  26. package/dist/src/modules/validators/contribution.js +12 -0
  27. package/dist/src/modules/validators/definition.d.ts.map +1 -1
  28. package/dist/src/modules/validators/definition.js +18 -0
  29. package/dist/src/modules/validators/permissions.d.ts +13 -0
  30. package/dist/src/modules/validators/permissions.d.ts.map +1 -1
  31. package/dist/src/modules/validators/permissions.js +140 -2
  32. package/dist/src/modules/validators/quorum.d.ts +66 -0
  33. package/dist/src/modules/validators/quorum.d.ts.map +1 -0
  34. package/dist/src/modules/validators/quorum.js +231 -0
  35. package/dist/src/modules/validators/resolve.d.ts.map +1 -1
  36. package/dist/src/modules/validators/resolve.js +5 -0
  37. package/dist/src/modules/validators/smart-sessions/resolve.d.ts.map +1 -1
  38. package/dist/src/modules/validators/smart-sessions/resolve.js +112 -13
  39. package/dist/src/modules/validators/smart-sessions/swap/fynd.d.ts +92 -0
  40. package/dist/src/modules/validators/smart-sessions/swap/fynd.d.ts.map +1 -0
  41. package/dist/src/modules/validators/smart-sessions/swap/fynd.js +101 -0
  42. package/dist/src/modules/validators/smart-sessions/swap/rhinestone.d.ts +147 -0
  43. package/dist/src/modules/validators/smart-sessions/swap/rhinestone.d.ts.map +1 -0
  44. package/dist/src/modules/validators/smart-sessions/swap/rhinestone.js +305 -0
  45. package/dist/src/modules/validators/smart-sessions/swap/rules.d.ts +73 -0
  46. package/dist/src/modules/validators/smart-sessions/swap/rules.d.ts.map +1 -0
  47. package/dist/src/modules/validators/smart-sessions/swap/rules.js +105 -0
  48. package/dist/src/modules/validators/smart-sessions/swap/scope.d.ts +31 -0
  49. package/dist/src/modules/validators/smart-sessions/swap/scope.d.ts.map +1 -0
  50. package/dist/src/modules/validators/smart-sessions/swap/scope.js +168 -0
  51. package/dist/src/modules/validators/smart-sessions/swap/zero-ex.d.ts +143 -0
  52. package/dist/src/modules/validators/smart-sessions/swap/zero-ex.d.ts.map +1 -0
  53. package/dist/src/modules/validators/smart-sessions/swap/zero-ex.js +164 -0
  54. package/dist/src/modules/validators/smart-sessions/types.d.ts +78 -0
  55. package/dist/src/modules/validators/smart-sessions/types.d.ts.map +1 -1
  56. package/dist/src/modules/validators/types.d.ts +26 -3
  57. package/dist/src/modules/validators/types.d.ts.map +1 -1
  58. package/dist/src/signing/hash.d.ts +9 -0
  59. package/dist/src/signing/hash.d.ts.map +1 -0
  60. package/dist/src/signing/hash.js +15 -0
  61. package/dist/src/signing/intent-plans/assemble.d.ts.map +1 -1
  62. package/dist/src/signing/intent-plans/assemble.js +10 -0
  63. package/dist/src/signing/intent-plans/independent.d.ts.map +1 -1
  64. package/dist/src/signing/intent-plans/independent.js +5 -1
  65. package/dist/src/signing/plan.d.ts.map +1 -1
  66. package/dist/src/signing/plan.js +14 -1
  67. package/dist/src/signing/quorum.d.ts +77 -0
  68. package/dist/src/signing/quorum.d.ts.map +1 -0
  69. package/dist/src/signing/quorum.js +52 -0
  70. package/dist/src/signing/signers/ecdsa.d.ts +1 -1
  71. package/dist/src/signing/signers/ecdsa.d.ts.map +1 -1
  72. package/dist/src/signing/signers/ecdsa.js +8 -0
  73. package/dist/src/signing/typed-data.d.ts +12 -6
  74. package/dist/src/signing/typed-data.d.ts.map +1 -1
  75. package/dist/src/signing/typed-data.js +9 -4
  76. package/dist/src/signing/types.d.ts +8 -0
  77. package/dist/src/signing/types.d.ts.map +1 -1
  78. package/dist/src/smart-sessions/index.d.ts +10 -5
  79. package/dist/src/smart-sessions/index.d.ts.map +1 -1
  80. package/dist/src/smart-sessions/index.js +6 -1
  81. package/dist/src/transactions/intents/sign-transaction.d.ts.map +1 -1
  82. package/dist/src/transactions/intents/sign-transaction.js +161 -47
  83. package/dist/src/transactions/user-operations/prepare.d.ts.map +1 -1
  84. package/dist/src/transactions/user-operations/prepare.js +3 -1
  85. package/dist/src/transactions/user-operations/validator-account.d.ts.map +1 -1
  86. package/dist/src/transactions/user-operations/validator-account.js +4 -0
  87. package/package.json +5 -1
@@ -9,6 +9,7 @@ import { DEFAULT_POLICY_ADDRESSES, resolvePolicyAddresses, } from './policies/ad
9
9
  import { expandCrossChainPermit, resolvePermit2ClaimPolicy, } from './policies/claim.js';
10
10
  import { encodeSessionPolicy } from './policies/encode.js';
11
11
  import { resolveSessionSigning } from './signing.js';
12
+ import { resolveSwapScope } from './swap/scope.js';
12
13
  export const SMART_SESSIONS_FALLBACK_TARGET_FLAG = '0x0000000000000000000000000000000000000001';
13
14
  export const SMART_SESSIONS_FALLBACK_TARGET_SELECTOR_FLAG = '0x00000001';
14
15
  export const SMART_SESSIONS_FALLBACK_TARGET_SELECTOR_FLAG_PERMITTED_TO_CALL_SMARTSESSION = '0x00000002';
@@ -31,16 +32,73 @@ export function resolveSessionData(definition, options = {}) {
31
32
  actionTarget: SMART_SESSIONS_FALLBACK_TARGET_FLAG,
32
33
  actionPolicies: [{ policy: addresses.sudo, initData: '0x' }],
33
34
  };
34
- const userActions = definition.permissions?.length
35
- ? resolvePermissions(definition.permissions)
36
- : [];
35
+ // `swap` is sugar over permissions + actions: it compiles to one merged
36
+ // approve plus one scoped swap action per venue, then rides the same
37
+ // resolution path. Venue routers, selectors and calldata offsets stay inside
38
+ // swap-venues.ts so they never reach the public surface (RHI-6286).
39
+ const swapScope = definition.swap
40
+ ? resolveSwapScope(definition.swap, definition.chain.id, environment)
41
+ : undefined;
42
+ // Declaring `swap` IS the restriction — a swap-scoped session that still
43
+ // carried the wildcard fallback would let the session key call anything the
44
+ // global intent-execution whitelist allows, which is the opposite of what the
45
+ // caller asked for. `restrictToActions` stays as the explicit spelling for
46
+ // sessions scoped by hand.
47
+ const restricted = definition.restrictToActions === true || swapScope !== undefined;
48
+ const permissions = [
49
+ ...(definition.permissions ?? []),
50
+ ...(swapScope?.permissions ?? []),
51
+ ];
52
+ const userActions = permissions.length ? resolvePermissions(permissions) : [];
53
+ // Raw scoped actions (target + selector + policies) for calls that can't be
54
+ // addressed by the ABI-name `permissions` sugar — e.g. a fynd swap scoped by
55
+ // its raw selector with no ABI (RHI-6286).
56
+ const rawActions = [
57
+ ...(definition.actions ?? []),
58
+ ...(swapScope?.actions ?? []),
59
+ ];
60
+ // A restricted session drops the fallback action, which is also where a
61
+ // cross-chain permit's spending-limit / time-frame guardrails live — so a
62
+ // restricted session combined with a permit would keep claim signing but lose
63
+ // maxAmount/deadline enforcement. These are different authorization surfaces;
64
+ // reject the combination rather than silently drop the guardrails.
65
+ if (restricted &&
66
+ (definition.crossChainPermits?.length || definition.claimPolicies?.length)) {
67
+ throw new Error('restrictToActions is incompatible with crossChainPermits/claimPolicies: ' +
68
+ 'dropping the fallback also drops the permit guardrails (spending/time ' +
69
+ 'limits). Use a restricted scoped-action session or a permit session, ' +
70
+ 'not both.');
71
+ }
72
+ // Guard raw actions from reintroducing the wildcard: reject one without
73
+ // target+selector (would map to the fallback flags), or one that targets the
74
+ // fallback sentinel outright — either would re-add the wildcard action that
75
+ // restrictToActions drops.
76
+ for (const a of rawActions) {
77
+ if (!('target' in a) || !('selector' in a)) {
78
+ throw new Error('definition.actions entries must be scoped (target + selector); a ' +
79
+ 'fallback-shaped action would map to the wildcard fallback target');
80
+ }
81
+ if (a.target.toLowerCase() ===
82
+ SMART_SESSIONS_FALLBACK_TARGET_FLAG.toLowerCase()) {
83
+ throw new Error('definition.actions must not target the fallback sentinel ' +
84
+ `(${SMART_SESSIONS_FALLBACK_TARGET_FLAG}) — it reintroduces the wildcard action`);
85
+ }
86
+ }
37
87
  const expandedPermits = (definition.crossChainPermits ?? []).map((input) => expandCrossChainPermit(resolveCrossChainPermission(input), environment));
38
88
  const permitFallbackPolicies = expandedPermits.flatMap(({ fallbackPolicies }) => fallbackPolicies);
89
+ // The wildcard intent-execution fallback. Dropped for a restricted session so
90
+ // the explicit permissions are the ONLY authorized ops — a non-listed selector
91
+ // then reverts instead of escaping via the global intent-execution target
92
+ // whitelist (RHI-6286).
93
+ const fallbackAction = {
94
+ policies: [{ type: 'intent-execution' }, ...permitFallbackPolicies],
95
+ };
39
96
  const injectedActions = [
40
97
  // Native-wrap `deposit()` is only permitted when the caller supplies the
41
98
  // chain's wrapped-native token (e.g. via `RhinestoneSDK.createSession`,
42
- // which resolves it from `/chains`).
43
- ...(options.wrappedNativeToken
99
+ // which resolves it from `/chains`). Dropped for a restricted session so it
100
+ // can't add an unrequested sudo action beyond the caller's permissions.
101
+ ...(options.wrappedNativeToken && !restricted
44
102
  ? [
45
103
  {
46
104
  target: options.wrappedNativeToken,
@@ -54,17 +112,45 @@ export function resolveSessionData(definition, options = {}) {
54
112
  },
55
113
  ]
56
114
  : []),
57
- {
58
- policies: [{ type: 'intent-execution' }, ...permitFallbackPolicies],
59
- },
115
+ ...(restricted ? [] : [fallbackAction]),
60
116
  {
61
117
  target: DUMMY_PRECLAIMOP_TARGET,
62
118
  selector: DUMMY_PRECLAIMOP_SELECTOR,
63
- policies: [{ type: 'sudo' }],
119
+ // The real pre-claim op carries no value, so cap it for a restricted
120
+ // session rather than granting sudo, which would let this injected action
121
+ // send native value to the dummy target.
122
+ //
123
+ // 1 wei, NOT 0: `ValueLimitPolicy.initializeWithMultiplexer` does
124
+ // `require(valueLimit != 0)`, so a zero limit reverts while the policy is
125
+ // being installed. That made every restricted session impossible to
126
+ // enable — the revert surfaces as `InvalidSignature()` from the emissary,
127
+ // which reads as a signature problem rather than a policy-init one.
128
+ // 1 wei is the smallest limit that installs, and the op carries no value.
129
+ policies: restricted
130
+ ? [{ type: 'value-limit', limit: 1n }]
131
+ : [{ type: 'sudo' }],
64
132
  },
65
133
  ];
66
- const actions = userActions.length || permitFallbackPolicies.length
67
- ? [...userActions, ...injectedActions].map((action) => ({
134
+ if (restricted && !userActions.length && !rawActions.length) {
135
+ throw new Error('restrictToActions drops the fallback, so the session must supply at ' +
136
+ 'least one permission or action — none were given');
137
+ }
138
+ // Raw actions bypass resolvePermissions' duplicate guard, so a raw action that
139
+ // collides with an ABI permission (or another raw action) on the same
140
+ // (target, selector) would map to the same on-chain action id and silently
141
+ // overwrite policy config — reject it instead.
142
+ const scoped = [...userActions, ...rawActions].filter((a) => 'target' in a && 'selector' in a);
143
+ const seen = new Set();
144
+ for (const a of scoped) {
145
+ const key = `${a.target.toLowerCase()}:${a.selector.toLowerCase()}`;
146
+ if (seen.has(key)) {
147
+ throw new Error(`Duplicate scoped action for (${a.target}, ${a.selector}) — permissions ` +
148
+ 'and actions share one on-chain action id; merge them into a single entry');
149
+ }
150
+ seen.add(key);
151
+ }
152
+ const actions = userActions.length || rawActions.length || permitFallbackPolicies.length
153
+ ? [...userActions, ...rawActions, ...injectedActions].map((action) => ({
68
154
  actionTargetSelector: 'selector' in action
69
155
  ? action.selector
70
156
  : SMART_SESSIONS_FALLBACK_TARGET_SELECTOR_FLAG,
@@ -81,8 +167,12 @@ export function resolveSessionData(definition, options = {}) {
81
167
  policy: PERMIT2_CLAIM_POLICY_ADDRESS,
82
168
  initData: encodePermit2ClaimPolicyInitData(resolvePermit2ClaimPolicy(policy)),
83
169
  }));
170
+ // A restricted session must not leave an open ERC-1271 signing surface: with
171
+ // signing defaulting to unrestricted, a session key limited to swap/approve
172
+ // could still sign e.g. a Permit2 approval off-chain and move funds. Default it
173
+ // to `disabled` when restricting; the caller can still opt into a signing policy.
84
174
  const erc7739Policies = resolveSessionSigning({
85
- signing: definition.signing,
175
+ signing: definition.signing ?? (restricted ? { mode: 'disabled' } : undefined),
86
176
  environment,
87
177
  addresses,
88
178
  });
@@ -108,7 +198,15 @@ export function toSession(definition, options = {}) {
108
198
  return {
109
199
  chain: definition.chain,
110
200
  owners: definition.owners,
111
- hasExplicitPermissions: Boolean(definition.permissions?.length),
201
+ // Drives `verifyExecutions`, which selects the signature mode: false lets an
202
+ // already-enabled session sign in pure ERC-1271 mode, and that mode never
203
+ // reaches the emissary's `verifyExecution` — so the action policies are
204
+ // never consulted. `swap` compiles to actions, so it MUST count here, or a
205
+ // swap-only session would silently stop being action-checked the moment it
206
+ // is enabled.
207
+ hasExplicitPermissions: Boolean(definition.permissions?.length ||
208
+ definition.actions?.length ||
209
+ definition.swap),
112
210
  permissionId: getPermissionIdFromData(data),
113
211
  sessionValidator: data.sessionValidator,
114
212
  sessionValidatorInitData: data.sessionValidatorInitData,
@@ -116,6 +214,7 @@ export function toSession(definition, options = {}) {
116
214
  erc7739Policies: data.erc7739Policies,
117
215
  actions: data.actions,
118
216
  claimPolicies: [...(definition.claimPolicies ?? []), ...expandedClaims],
217
+ ...(definition.swap ? { swap: definition.swap } : {}),
119
218
  };
120
219
  }
121
220
  export { DEFAULT_POLICY_ADDRESSES };
@@ -0,0 +1,92 @@
1
+ import { type Address } from 'viem';
2
+ import type { FyndVenue } from '../types.js';
3
+ import type { VenueContext, VenueScoping } from './rules.js';
4
+ /**
5
+ * fynd — Rhinestone's self-hosted Tycho aggregator.
6
+ *
7
+ * Unlike 0x there is no allowance-holder indirection: the TychoRouter is both
8
+ * the swap target and the ERC-20 approval spender, and every field we care about
9
+ * is a named static argument in the calldata head. That means nothing here is a
10
+ * magic offset — the rules are addressed by ABI parameter name.
11
+ */
12
+ /** Chains with a deployed, whitelisted TychoRouter. */
13
+ export declare const FYND_CHAIN_IDS: readonly [1, 56, 130, 137, 8453, 9745, 42161];
14
+ export type FyndChainId = (typeof FYND_CHAIN_IDS)[number];
15
+ /**
16
+ * TychoRouter per chain — the contract fynd's encoded fills target.
17
+ *
18
+ * Mirrors `fynd.routers` in the orchestrator's quoters.jsonnet. A chain is
19
+ * listed only when the router is whitelisted in `IntentExecutionPolicy`, since a
20
+ * quoter enabled without a whitelisted target produces quotes that revert.
21
+ */
22
+ export declare const FYND_ROUTERS: Record<FyndChainId, Address>;
23
+ /**
24
+ * TychoRouter `singleSwap`, the entrypoint fynd's encoded quotes call.
25
+ *
26
+ * Only the argument TYPES determine the selector and the head layout, and those
27
+ * are pinned by {@link FYND_SWAP_SELECTOR}'s assertion in the test suite. The
28
+ * NAMES of the first five are corroborated by observed fynd calldata; the
29
+ * trailing tuple's component names are our own labels and carry no guarantee —
30
+ * we never address them, since a tuple containing `bytes` is dynamic and sits
31
+ * behind a pointer.
32
+ */
33
+ export declare const tychoRouterAbi: readonly [{
34
+ readonly type: "function";
35
+ readonly name: "singleSwap";
36
+ readonly stateMutability: "payable";
37
+ readonly inputs: readonly [{
38
+ readonly name: "amountIn";
39
+ readonly type: "uint256";
40
+ }, {
41
+ readonly name: "tokenIn";
42
+ readonly type: "address";
43
+ }, {
44
+ readonly name: "tokenOut";
45
+ readonly type: "address";
46
+ }, {
47
+ readonly name: "minAmountOut";
48
+ readonly type: "uint256";
49
+ }, {
50
+ readonly name: "receiver";
51
+ readonly type: "address";
52
+ }, {
53
+ readonly name: "permit";
54
+ readonly type: "tuple";
55
+ readonly components: readonly [{
56
+ readonly name: "nonce";
57
+ readonly type: "uint16";
58
+ }, {
59
+ readonly name: "token";
60
+ readonly type: "address";
61
+ }, {
62
+ readonly name: "amount";
63
+ readonly type: "uint256";
64
+ }, {
65
+ readonly name: "deadline";
66
+ readonly type: "uint256";
67
+ }, {
68
+ readonly name: "signature";
69
+ readonly type: "bytes";
70
+ }];
71
+ }, {
72
+ readonly name: "swap";
73
+ readonly type: "bytes";
74
+ }];
75
+ readonly outputs: readonly [{
76
+ readonly name: "amountOut";
77
+ readonly type: "uint256";
78
+ }];
79
+ }];
80
+ export declare const FYND_SWAP_SELECTOR: `0x${string}`;
81
+ /**
82
+ * Scope a session to fynd swaps.
83
+ *
84
+ * Needs no addresses: the router is a per-chain deployment the SDK knows, and
85
+ * because it is also the approval spender there is no separate allowance target
86
+ * to reason about.
87
+ */
88
+ export declare function fynd(options?: {
89
+ maxSpend?: bigint;
90
+ }): FyndVenue;
91
+ export declare function scopeFynd(ctx: VenueContext): VenueScoping;
92
+ //# sourceMappingURL=fynd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fynd.d.ts","sourceRoot":"","sources":["../../../../../../modules/validators/smart-sessions/swap/fynd.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,OAAO,EAAsB,MAAM,MAAM,CAAA;AAEjE,OAAO,KAAK,EAAE,SAAS,EAAkC,MAAM,UAAU,CAAA;AACzE,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAGzD;;;;;;;GAOG;AAEH,uDAAuD;AACvD,eAAO,MAAM,cAAc,+CAAgD,CAAA;AAE3E,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAA;AAEzD;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,WAAW,EAAE,OAAO,CAQrD,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0BH,CAAA;AAExB,eAAO,MAAM,kBAAkB,eAAwC,CAAA;AAQvE;;;;;;GAMG;AACH,wBAAgB,IAAI,CAAC,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,SAAS,CAKnE;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,YAAY,GAAG,YAAY,CAoBzD"}
@@ -0,0 +1,101 @@
1
+ import { toFunctionSelector } from 'viem';
2
+ import { namedParamOffsets } from '../../permissions.js';
3
+ import { cumulativeCap, pin, swapAction } from './rules.js';
4
+ /**
5
+ * fynd — Rhinestone's self-hosted Tycho aggregator.
6
+ *
7
+ * Unlike 0x there is no allowance-holder indirection: the TychoRouter is both
8
+ * the swap target and the ERC-20 approval spender, and every field we care about
9
+ * is a named static argument in the calldata head. That means nothing here is a
10
+ * magic offset — the rules are addressed by ABI parameter name.
11
+ */
12
+ /** Chains with a deployed, whitelisted TychoRouter. */
13
+ export const FYND_CHAIN_IDS = [1, 56, 130, 137, 8453, 9745, 42161];
14
+ /**
15
+ * TychoRouter per chain — the contract fynd's encoded fills target.
16
+ *
17
+ * Mirrors `fynd.routers` in the orchestrator's quoters.jsonnet. A chain is
18
+ * listed only when the router is whitelisted in `IntentExecutionPolicy`, since a
19
+ * quoter enabled without a whitelisted target produces quotes that revert.
20
+ */
21
+ export const FYND_ROUTERS = {
22
+ 1: '0xda892c989d07a18b5dd3f392d949f00df15c5736',
23
+ 56: '0x99748cbd931cb367dad265c5b2b4bd306d448e99',
24
+ 130: '0x764bc67b1036b00bc91221e988261f971a1c7ce4',
25
+ 137: '0x7cb3e87095f6cf95982dc6f57445171a6d3b511c',
26
+ 8453: '0x2d3524b9b5dae34b646614eebb1e038d403e4cac',
27
+ 9745: '0x8f9b3b0451efff0ae8100428aee35fa3cbc0b769',
28
+ 42161: '0xc1f838a5382bbb5729a0801c8ba73dfc861c4d34',
29
+ };
30
+ /**
31
+ * TychoRouter `singleSwap`, the entrypoint fynd's encoded quotes call.
32
+ *
33
+ * Only the argument TYPES determine the selector and the head layout, and those
34
+ * are pinned by {@link FYND_SWAP_SELECTOR}'s assertion in the test suite. The
35
+ * NAMES of the first five are corroborated by observed fynd calldata; the
36
+ * trailing tuple's component names are our own labels and carry no guarantee —
37
+ * we never address them, since a tuple containing `bytes` is dynamic and sits
38
+ * behind a pointer.
39
+ */
40
+ export const tychoRouterAbi = [
41
+ {
42
+ type: 'function',
43
+ name: 'singleSwap',
44
+ stateMutability: 'payable',
45
+ inputs: [
46
+ { name: 'amountIn', type: 'uint256' },
47
+ { name: 'tokenIn', type: 'address' },
48
+ { name: 'tokenOut', type: 'address' },
49
+ { name: 'minAmountOut', type: 'uint256' },
50
+ { name: 'receiver', type: 'address' },
51
+ {
52
+ name: 'permit',
53
+ type: 'tuple',
54
+ components: [
55
+ { name: 'nonce', type: 'uint16' },
56
+ { name: 'token', type: 'address' },
57
+ { name: 'amount', type: 'uint256' },
58
+ { name: 'deadline', type: 'uint256' },
59
+ { name: 'signature', type: 'bytes' },
60
+ ],
61
+ },
62
+ { name: 'swap', type: 'bytes' },
63
+ ],
64
+ outputs: [{ name: 'amountOut', type: 'uint256' }],
65
+ },
66
+ ];
67
+ export const FYND_SWAP_SELECTOR = toFunctionSelector(tychoRouterAbi[0]);
68
+ /** Head offsets derived from the ABI — never hardcoded. */
69
+ const OFFSETS = namedParamOffsets(tychoRouterAbi, 'singleSwap');
70
+ /**
71
+ * Scope a session to fynd swaps.
72
+ *
73
+ * Needs no addresses: the router is a per-chain deployment the SDK knows, and
74
+ * because it is also the approval spender there is no separate allowance target
75
+ * to reason about.
76
+ */
77
+ export function fynd(options = {}) {
78
+ return {
79
+ id: 'fynd',
80
+ ...(options.maxSpend !== undefined ? { maxSpend: options.maxSpend } : {}),
81
+ };
82
+ }
83
+ export function scopeFynd(ctx) {
84
+ const router = FYND_ROUTERS[ctx.chainId];
85
+ if (router === undefined) {
86
+ throw new Error(`fynd is not available on chain ${ctx.chainId}. ` +
87
+ `Supported: ${FYND_CHAIN_IDS.join(', ')}.`);
88
+ }
89
+ const rules = [
90
+ pin(OFFSETS.tokenIn, ctx.sellToken),
91
+ pin(OFFSETS.tokenOut, ctx.buyToken),
92
+ pin(OFFSETS.receiver, ctx.recipient),
93
+ ];
94
+ if (ctx.cap !== undefined) {
95
+ rules.push(cumulativeCap(OFFSETS.amountIn, ctx.cap));
96
+ }
97
+ return {
98
+ approveSpenders: [router],
99
+ actions: [swapAction(router, FYND_SWAP_SELECTOR, rules)],
100
+ };
101
+ }
@@ -0,0 +1,147 @@
1
+ import { type Address } from 'viem';
2
+ import type { RhinestoneSwapVenue, RoutedRhinestoneSwapVenue } from '../types.js';
3
+ import type { VenueContext, VenueScoping } from './rules.js';
4
+ /**
5
+ * The Rhinestone Swapper — the route the orchestrator actually uses for
6
+ * same-chain smart-account swaps, and the right layer to scope a session at.
7
+ *
8
+ * Scoping an aggregator directly (see `zero-ex.ts`, `fynd.ts`) describes a call
9
+ * the account does not make on this route: the orchestrator wraps whichever
10
+ * quoter wins behind its own Swapper, so a session scoped to 0x's
11
+ * AllowanceHolder rejects its own intended swap with `InvalidSignature()`.
12
+ * Verified live on Plasma — the account's sell token moves to the Swapper, and
13
+ * the aggregator never appears in the account's ops.
14
+ *
15
+ * Scoping here is also strictly better:
16
+ * - aggregator-agnostic, so whichever quoter wins is irrelevant
17
+ * - no 0x Settler rotation problem, because this contract is ours
18
+ * - one approve target per chain (the proxy) instead of one per venue
19
+ * - the fields we care about are typed head args on a contract we control,
20
+ * with the aggregator route demoted to an opaque `calls[]` tail
21
+ *
22
+ * What the Swapper guarantees (verified in compact-utils/src/swapper):
23
+ * at most `amountIn` of `tokenIn` is pulled, once, from `msg.sender`; the
24
+ * recipient's measured balance delta must be >= `minAmountOut` or it reverts;
25
+ * every refund and sweep goes to `msg.sender`. The account cannot be drained
26
+ * beyond the pulled amount: the tail runs as the Swapper, which holds no
27
+ * allowance from the account, and `SwapperLib.runRoute` forbids the tail from
28
+ * calling the proxy that does.
29
+ *
30
+ * Be precise about what that bounds, though. A capped `amountIn` bounds how
31
+ * much can LEAVE; it does not ensure anything comes back. `minAmountOut` is a
32
+ * caller-supplied argument this scoping does not pin, and the contract accepts
33
+ * zero — so with an unconstrained route a compromised key can spend up to the
34
+ * cap and receive nothing. Naming a venue closes that by pinning the
35
+ * route, leaving the pulled input nowhere to go but a real aggregator.
36
+ */
37
+ /**
38
+ * Both entrypoints share a head layout, which is what lets one venue cover
39
+ * both: `tokenIn`@0, sell amount@32, `tokenOut`@64, `recipient`@160. The sell
40
+ * amount is `amountIn` for exact-in and `amountInMax` for exact-out — in both
41
+ * cases the ceiling on what leaves the account, which is exactly what to cap.
42
+ */
43
+ export declare const swapperAbi: readonly [{
44
+ readonly type: "function";
45
+ readonly name: "swapExactIn";
46
+ readonly stateMutability: "payable";
47
+ readonly inputs: readonly [{
48
+ readonly name: "tokenIn";
49
+ readonly type: "address";
50
+ }, {
51
+ readonly name: "amountIn";
52
+ readonly type: "uint256";
53
+ }, {
54
+ readonly name: "tokenOut";
55
+ readonly type: "address";
56
+ }, {
57
+ readonly name: "minAmountOut";
58
+ readonly type: "uint256";
59
+ }, {
60
+ readonly name: "quotedAmountOut";
61
+ readonly type: "uint256";
62
+ }, {
63
+ readonly name: "recipient";
64
+ readonly type: "address";
65
+ }, {
66
+ readonly name: "orderRef";
67
+ readonly type: "uint256";
68
+ }, {
69
+ readonly name: "calls";
70
+ readonly type: "tuple[]";
71
+ readonly components: readonly [{
72
+ readonly name: "target";
73
+ readonly type: "address";
74
+ }, {
75
+ readonly name: "value";
76
+ readonly type: "uint256";
77
+ }, {
78
+ readonly name: "data";
79
+ readonly type: "bytes";
80
+ }];
81
+ }];
82
+ readonly outputs: readonly [{
83
+ readonly name: "amountOut";
84
+ readonly type: "uint256";
85
+ }];
86
+ }, {
87
+ readonly type: "function";
88
+ readonly name: "swapExactOut";
89
+ readonly stateMutability: "payable";
90
+ readonly inputs: readonly [{
91
+ readonly name: "tokenIn";
92
+ readonly type: "address";
93
+ }, {
94
+ readonly name: "amountInMax";
95
+ readonly type: "uint256";
96
+ }, {
97
+ readonly name: "tokenOut";
98
+ readonly type: "address";
99
+ }, {
100
+ readonly name: "amountOut";
101
+ readonly type: "uint256";
102
+ }, {
103
+ readonly name: "quotedAmountIn";
104
+ readonly type: "uint256";
105
+ }, {
106
+ readonly name: "recipient";
107
+ readonly type: "address";
108
+ }, {
109
+ readonly name: "orderRef";
110
+ readonly type: "uint256";
111
+ }, {
112
+ readonly name: "calls";
113
+ readonly type: "tuple[]";
114
+ readonly components: readonly [{
115
+ readonly name: "target";
116
+ readonly type: "address";
117
+ }, {
118
+ readonly name: "value";
119
+ readonly type: "uint256";
120
+ }, {
121
+ readonly name: "data";
122
+ readonly type: "bytes";
123
+ }];
124
+ }];
125
+ readonly outputs: readonly [{
126
+ readonly name: "amountSpent";
127
+ readonly type: "uint256";
128
+ }];
129
+ }];
130
+ export declare const SWAP_EXACT_IN_SELECTOR: `0x${string}`;
131
+ export declare const SWAP_EXACT_OUT_SELECTOR: `0x${string}`;
132
+ export declare function swapperAddresses(environment: 'production' | 'development'): {
133
+ swapper: Address;
134
+ proxy: Address;
135
+ };
136
+ /**
137
+ * Route swaps through the Rhinestone Swapper — the default, and the only venue
138
+ * that matches what the orchestrator emits for same-chain smart-account swaps.
139
+ *
140
+ * Takes no addresses and no aggregator: which quoter fills the swap is the
141
+ * orchestrator's decision, and this scoping holds whichever one wins.
142
+ */
143
+ export declare function rhinestoneSwap(options?: {
144
+ maxSpend?: bigint;
145
+ }): RhinestoneSwapVenue;
146
+ export declare function scopeRhinestone(venue: RoutedRhinestoneSwapVenue, ctx: VenueContext): VenueScoping;
147
+ //# sourceMappingURL=rhinestone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rhinestone.d.ts","sourceRoot":"","sources":["../../../../../../modules/validators/smart-sessions/swap/rhinestone.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,OAAO,EAAgC,MAAM,MAAM,CAAA;AAE3E,OAAO,KAAK,EACV,mBAAmB,EACnB,yBAAyB,EAE1B,MAAM,UAAU,CAAA;AAEjB,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAIzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiDC,CAAA;AAExB,eAAO,MAAM,sBAAsB,eAAoC,CAAA;AACvE,eAAO,MAAM,uBAAuB,eAAoC,CAAA;AAqBxE,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,YAAY,GAAG,aAAa,GAAG;IAC3E,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,OAAO,CAAA;CACf,CAIA;AAkJD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,mBAAmB,CAKrB;AACD,wBAAgB,eAAe,CAC7B,KAAK,EAAE,yBAAyB,EAChC,GAAG,EAAE,YAAY,GAChB,YAAY,CAgFd"}