@rhinestone/sdk 1.13.4 → 1.15.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 (61) hide show
  1. package/dist/src/accounts/index.d.ts +5 -2
  2. package/dist/src/accounts/index.d.ts.map +1 -1
  3. package/dist/src/accounts/index.js +43 -25
  4. package/dist/src/accounts/safe.d.ts.map +1 -1
  5. package/dist/src/accounts/safe.js +2 -0
  6. package/dist/src/accounts/signing/common.d.ts +7 -1
  7. package/dist/src/accounts/signing/common.d.ts.map +1 -1
  8. package/dist/src/accounts/signing/common.js +90 -15
  9. package/dist/src/accounts/signing/quorum.d.ts +78 -0
  10. package/dist/src/accounts/signing/quorum.d.ts.map +1 -0
  11. package/dist/src/accounts/signing/quorum.js +41 -0
  12. package/dist/src/actions/smart-sessions.d.ts +2 -1
  13. package/dist/src/actions/smart-sessions.d.ts.map +1 -1
  14. package/dist/src/actions/smart-sessions.js +6 -0
  15. package/dist/src/execution/index.d.ts +2 -1
  16. package/dist/src/execution/index.d.ts.map +1 -1
  17. package/dist/src/execution/index.js +5 -4
  18. package/dist/src/execution/quoters.d.ts +7 -0
  19. package/dist/src/execution/quoters.d.ts.map +1 -0
  20. package/dist/src/execution/quoters.js +55 -0
  21. package/dist/src/execution/utils.d.ts +2 -2
  22. package/dist/src/execution/utils.d.ts.map +1 -1
  23. package/dist/src/execution/utils.js +70 -6
  24. package/dist/src/index.d.ts +3 -3
  25. package/dist/src/index.d.ts.map +1 -1
  26. package/dist/src/index.js +7 -0
  27. package/dist/src/modules/validators/core.d.ts.map +1 -1
  28. package/dist/src/modules/validators/core.js +5 -0
  29. package/dist/src/modules/validators/quorum.d.ts +56 -0
  30. package/dist/src/modules/validators/quorum.d.ts.map +1 -0
  31. package/dist/src/modules/validators/quorum.js +215 -0
  32. package/dist/src/modules/validators/smart-sessions/swap/abi.d.ts +3 -0
  33. package/dist/src/modules/validators/smart-sessions/swap/abi.d.ts.map +1 -0
  34. package/dist/src/modules/validators/smart-sessions/swap/abi.js +67 -0
  35. package/dist/src/modules/validators/smart-sessions/swap/fynd.d.ts +92 -0
  36. package/dist/src/modules/validators/smart-sessions/swap/fynd.d.ts.map +1 -0
  37. package/dist/src/modules/validators/smart-sessions/swap/fynd.js +106 -0
  38. package/dist/src/modules/validators/smart-sessions/swap/rhinestone.d.ts +147 -0
  39. package/dist/src/modules/validators/smart-sessions/swap/rhinestone.d.ts.map +1 -0
  40. package/dist/src/modules/validators/smart-sessions/swap/rhinestone.js +311 -0
  41. package/dist/src/modules/validators/smart-sessions/swap/rules.d.ts +73 -0
  42. package/dist/src/modules/validators/smart-sessions/swap/rules.d.ts.map +1 -0
  43. package/dist/src/modules/validators/smart-sessions/swap/rules.js +112 -0
  44. package/dist/src/modules/validators/smart-sessions/swap/scope.d.ts +18 -0
  45. package/dist/src/modules/validators/smart-sessions/swap/scope.d.ts.map +1 -0
  46. package/dist/src/modules/validators/smart-sessions/swap/scope.js +124 -0
  47. package/dist/src/modules/validators/smart-sessions/swap/zero-ex.d.ts +143 -0
  48. package/dist/src/modules/validators/smart-sessions/swap/zero-ex.d.ts.map +1 -0
  49. package/dist/src/modules/validators/smart-sessions/swap/zero-ex.js +170 -0
  50. package/dist/src/modules/validators/smart-sessions.d.ts +9 -3
  51. package/dist/src/modules/validators/smart-sessions.d.ts.map +1 -1
  52. package/dist/src/modules/validators/smart-sessions.js +229 -89
  53. package/dist/src/orchestrator/consts.d.ts +1 -1
  54. package/dist/src/orchestrator/consts.js +1 -1
  55. package/dist/src/orchestrator/index.d.ts +2 -2
  56. package/dist/src/orchestrator/index.d.ts.map +1 -1
  57. package/dist/src/orchestrator/types.d.ts +12 -1
  58. package/dist/src/orchestrator/types.d.ts.map +1 -1
  59. package/dist/src/types.d.ts +84 -4
  60. package/dist/src/types.d.ts.map +1 -1
  61. package/package.json +7 -2
@@ -0,0 +1,215 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildQuorumMerkleTree = buildQuorumMerkleTree;
4
+ exports.encodeQuorumErc1271Signature = encodeQuorumErc1271Signature;
5
+ exports.encodeQuorumMerkleSignature = encodeQuorumMerkleSignature;
6
+ exports.encodeQuorumOwnerSignatures = encodeQuorumOwnerSignatures;
7
+ exports.getQuorumConfigOwners = getQuorumConfigOwners;
8
+ exports.getQuorumMerkleRootSignableHash = getQuorumMerkleRootSignableHash;
9
+ exports.getQuorumMockSignature = getQuorumMockSignature;
10
+ exports.getQuorumSignableHash = getQuorumSignableHash;
11
+ exports.getQuorumValidator = getQuorumValidator;
12
+ const viem_1 = require("viem");
13
+ const common_1 = require("../common");
14
+ const ordering_1 = require("./ordering");
15
+ const MAX_QUORUM_OWNERS = 32;
16
+ const MAX_UINT96 = (1n << 96n) - 1n;
17
+ const MAX_SIGNATURE_LENGTH = 65_535;
18
+ const QUORUM_MOCK_SIGNATURE = `0x${'00'.repeat(64)}1b`;
19
+ const QUORUM_MESSAGE_TYPEHASH = (0, viem_1.keccak256)((0, viem_1.toHex)('WeightedOwnableValidatorMessage(address validator,uint256 chainId,address account,bytes32 hash)'));
20
+ function getQuorumSignableHash(input) {
21
+ return (0, viem_1.keccak256)((0, viem_1.encodeAbiParameters)([
22
+ { type: 'bytes32' },
23
+ { type: 'address' },
24
+ { type: 'uint256' },
25
+ { type: 'address' },
26
+ { type: 'bytes32' },
27
+ ], [
28
+ QUORUM_MESSAGE_TYPEHASH,
29
+ input.validator,
30
+ BigInt(input.chainId),
31
+ input.account,
32
+ input.hash,
33
+ ]));
34
+ }
35
+ function buildQuorumMerkleTree(operations) {
36
+ if (operations.length < 2) {
37
+ throw new Error('Quorum Merkle signing requires at least two operations');
38
+ }
39
+ const leaves = operations.map(({ account, digest }) => (0, viem_1.keccak256)((0, viem_1.encodeAbiParameters)([{ type: 'address' }, { type: 'bytes32' }], [account, digest])));
40
+ const layers = [leaves];
41
+ while (layers.at(-1).length > 1) {
42
+ const current = layers.at(-1);
43
+ const next = [];
44
+ for (let index = 0; index < current.length; index += 2) {
45
+ next.push(index + 1 < current.length
46
+ ? hashQuorumMerklePair(current[index], current[index + 1])
47
+ : current[index]);
48
+ }
49
+ layers.push(next);
50
+ }
51
+ const root = layers.at(-1)[0];
52
+ return {
53
+ root,
54
+ operations: operations.map(({ account, digest }, leafIndex) => {
55
+ const proof = [];
56
+ let index = leafIndex;
57
+ for (const layer of layers.slice(0, -1)) {
58
+ const siblingIndex = index % 2 === 0 ? index + 1 : index - 1;
59
+ if (siblingIndex < layer.length)
60
+ proof.push(layer[siblingIndex]);
61
+ index = Math.floor(index / 2);
62
+ }
63
+ return { account, digest, leaf: leaves[leafIndex], root, proof };
64
+ }),
65
+ };
66
+ }
67
+ function getQuorumMerkleRootSignableHash(input) {
68
+ return (0, viem_1.hashTypedData)({
69
+ domain: {
70
+ name: 'Quorum Signer',
71
+ version: '1',
72
+ verifyingContract: input.validator,
73
+ },
74
+ types: {
75
+ WeightedMerkleRoot: [{ name: 'root', type: 'bytes32' }],
76
+ },
77
+ primaryType: 'WeightedMerkleRoot',
78
+ message: { root: input.root },
79
+ });
80
+ }
81
+ function getQuorumValidator(config) {
82
+ const owners = resolveOwners(config).sort((left, right) => (0, ordering_1.compareHexValues)(left.signer, right.signer));
83
+ validateQuorumConfig(owners, config.thresholdWeight);
84
+ return {
85
+ address: config.module,
86
+ initData: (0, viem_1.encodeAbiParameters)([
87
+ { name: 'thresholdWeight', type: 'uint256' },
88
+ {
89
+ name: 'owners',
90
+ type: 'tuple[]',
91
+ components: [
92
+ { name: 'addr', type: 'address' },
93
+ { name: 'weight', type: 'uint96' },
94
+ ],
95
+ },
96
+ ], [
97
+ config.thresholdWeight,
98
+ owners.map(({ signer, weight }) => ({ addr: signer, weight })),
99
+ ]),
100
+ deInitData: '0x',
101
+ additionalContext: '0x',
102
+ type: common_1.MODULE_TYPE_ID_VALIDATOR,
103
+ };
104
+ }
105
+ function encodeQuorumOwnerSignatures(input) {
106
+ validateQuorumConfig(input.owners, input.thresholdWeight);
107
+ const configured = new Map(input.owners.map((owner) => [owner.ownerId, owner]));
108
+ const contributions = new Map();
109
+ for (const contribution of input.signatures) {
110
+ if (!configured.has(contribution.ownerId)) {
111
+ throw new Error(`Unknown validator owner ${contribution.ownerId}`);
112
+ }
113
+ if (contributions.has(contribution.ownerId)) {
114
+ throw new Error(`Duplicate validator owner ${contribution.ownerId}`);
115
+ }
116
+ contributions.set(contribution.ownerId, contribution.signature);
117
+ }
118
+ const selected = input.owners
119
+ .filter((owner) => contributions.has(owner.ownerId))
120
+ .sort((left, right) => (0, ordering_1.compareHexValues)(left.signer, right.signer));
121
+ const signedWeight = selected.reduce((sum, owner) => sum + owner.weight, 0n);
122
+ if (signedWeight < input.thresholdWeight) {
123
+ throw new Error(`Insufficient validator contribution weight: required ${input.thresholdWeight}, received ${signedWeight}`);
124
+ }
125
+ return encodeQuorumEntries(selected.map((owner) => ({
126
+ signer: owner.signer,
127
+ signature: contributions.get(owner.ownerId),
128
+ })));
129
+ }
130
+ function encodeQuorumErc1271Signature(input) {
131
+ if (input.signatures === '0x') {
132
+ throw new Error('Quorum owner signatures must not be empty');
133
+ }
134
+ return (0, viem_1.concat)(['0x00', input.signatures]);
135
+ }
136
+ function encodeQuorumMerkleSignature(input) {
137
+ if (input.operation.proof.length < 1 || input.operation.proof.length > 32) {
138
+ throw new Error('Quorum Merkle proof length must be between 1 and 32');
139
+ }
140
+ if (input.signatures === '0x') {
141
+ throw new Error('Quorum Merkle signatures must not be empty');
142
+ }
143
+ return (0, viem_1.concat)([
144
+ (0, viem_1.toHex)(input.operation.proof.length, { size: 1 }),
145
+ input.operation.root,
146
+ ...input.operation.proof,
147
+ input.signatures,
148
+ ]);
149
+ }
150
+ function getQuorumMockSignature(config) {
151
+ const owners = resolveOwners(config);
152
+ validateQuorumConfig(owners, config.thresholdWeight);
153
+ return encodeQuorumErc1271Signature({
154
+ signatures: encodeQuorumEntries(owners
155
+ .map(({ signer }) => ({ signer, signature: QUORUM_MOCK_SIGNATURE }))
156
+ .sort((left, right) => (0, ordering_1.compareHexValues)(left.signer, right.signer))),
157
+ });
158
+ }
159
+ function getQuorumConfigOwners(config) {
160
+ return resolveOwners(config);
161
+ }
162
+ function encodeQuorumEntries(entries) {
163
+ if (entries.length === 0) {
164
+ throw new Error('Quorum signature must not be empty');
165
+ }
166
+ let previous;
167
+ const packed = entries.map(({ signer, signature }) => {
168
+ if (previous && (0, ordering_1.compareHexValues)(previous, signer) >= 0) {
169
+ throw new Error('Quorum signer addresses must be strictly sorted and unique');
170
+ }
171
+ previous = signer;
172
+ const signatureLength = (0, viem_1.size)(signature);
173
+ if (signatureLength > MAX_SIGNATURE_LENGTH) {
174
+ throw new Error(`Quorum inner signature exceeds ${MAX_SIGNATURE_LENGTH} bytes`);
175
+ }
176
+ return (0, viem_1.concat)([signer, (0, viem_1.toHex)(signatureLength, { size: 2 }), signature]);
177
+ });
178
+ return (0, viem_1.concat)(packed);
179
+ }
180
+ function resolveOwners(config) {
181
+ return config.owners.map(({ account, weight }) => ({
182
+ ownerId: account.address.toLowerCase(),
183
+ signer: account.address,
184
+ weight,
185
+ }));
186
+ }
187
+ function validateQuorumConfig(owners, thresholdWeight) {
188
+ if (owners.length < 1 || owners.length > MAX_QUORUM_OWNERS) {
189
+ throw new Error(`Quorum owners length must be between 1 and ${MAX_QUORUM_OWNERS}`);
190
+ }
191
+ const seen = new Set();
192
+ let totalWeight = 0n;
193
+ for (const [index, owner] of owners.entries()) {
194
+ const signer = owner.signer.toLowerCase();
195
+ if (signer === viem_1.zeroAddress) {
196
+ throw new Error(`Quorum owner ${index} cannot be the zero address`);
197
+ }
198
+ if (seen.has(signer)) {
199
+ throw new Error(`Duplicate quorum owner ${owner.signer}`);
200
+ }
201
+ seen.add(signer);
202
+ if (owner.weight <= 0n || owner.weight > MAX_UINT96) {
203
+ throw new Error(`Quorum owner ${owner.signer} weight must fit uint96 and be non-zero`);
204
+ }
205
+ totalWeight += owner.weight;
206
+ }
207
+ if (thresholdWeight <= 0n || thresholdWeight > totalWeight) {
208
+ throw new Error(`Quorum threshold weight must be between 1 and total owner weight ${totalWeight}`);
209
+ }
210
+ }
211
+ function hashQuorumMerklePair(left, right) {
212
+ return (0, ordering_1.compareHexValues)(left, right) < 0
213
+ ? (0, viem_1.keccak256)((0, viem_1.concat)([left, right]))
214
+ : (0, viem_1.keccak256)((0, viem_1.concat)([right, left]));
215
+ }
@@ -0,0 +1,3 @@
1
+ import type { Abi } from 'viem';
2
+ export declare function namedParamOffsets(abi: Abi, functionName: string): Record<string, bigint>;
3
+ //# sourceMappingURL=abi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abi.d.ts","sourceRoot":"","sources":["../../../../../../modules/validators/smart-sessions/swap/abi.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAA6B,MAAM,MAAM,CAAA;AAuD1D,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,GAAG,EACR,YAAY,EAAE,MAAM,GACnB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAcxB"}
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.namedParamOffsets = namedParamOffsets;
4
+ function isStaticAbiType(type) {
5
+ if (type === 'address' || type === 'bool')
6
+ return true;
7
+ if (/^u?int\d*$/.test(type))
8
+ return true;
9
+ if (/^bytes\d+$/.test(type)) {
10
+ const size = Number.parseInt(type.slice(5), 10);
11
+ return size >= 1 && size <= 32;
12
+ }
13
+ return false;
14
+ }
15
+ const ARRAY_SUFFIX = /^(.*)\[(\d*)\]$/;
16
+ function withType(param, type) {
17
+ return { ...param, type };
18
+ }
19
+ function tupleComponents(param) {
20
+ return param.components ?? [];
21
+ }
22
+ function isDynamicAbiParam(param) {
23
+ if (param.type === 'bytes' || param.type === 'string')
24
+ return true;
25
+ const array = ARRAY_SUFFIX.exec(param.type);
26
+ if (array) {
27
+ const [, elementType, length] = array;
28
+ if (length === '')
29
+ return true;
30
+ return isDynamicAbiParam(withType(param, elementType));
31
+ }
32
+ if (param.type === 'tuple') {
33
+ return tupleComponents(param).some(isDynamicAbiParam);
34
+ }
35
+ return false;
36
+ }
37
+ function headSize(param) {
38
+ if (isDynamicAbiParam(param))
39
+ return 32;
40
+ const array = ARRAY_SUFFIX.exec(param.type);
41
+ if (array) {
42
+ const [, elementType, length] = array;
43
+ return Number.parseInt(length, 10) * headSize(withType(param, elementType));
44
+ }
45
+ if (param.type === 'tuple') {
46
+ return tupleComponents(param).reduce((sum, item) => sum + headSize(item), 0);
47
+ }
48
+ return 32;
49
+ }
50
+ function headOffset(inputs, index) {
51
+ let offset = 0;
52
+ for (let i = 0; i < index; i++)
53
+ offset += headSize(inputs[i]);
54
+ return BigInt(offset);
55
+ }
56
+ function namedParamOffsets(abi, functionName) {
57
+ const entry = abi.find((item) => item.type === 'function' && item.name === functionName);
58
+ if (!entry)
59
+ throw new Error(`Function "${functionName}" not found in ABI`);
60
+ const offsets = {};
61
+ entry.inputs.forEach((param, index) => {
62
+ if (param.name && isStaticAbiType(param.type)) {
63
+ offsets[param.name] = headOffset(entry.inputs, index);
64
+ }
65
+ });
66
+ return offsets;
67
+ }
@@ -0,0 +1,92 @@
1
+ import { type Address } from 'viem';
2
+ import type { FyndVenue } from '../../../../types';
3
+ import type { VenueContext, VenueScoping } from './rules';
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;AACjE,OAAO,KAAK,EACV,SAAS,EAEV,MAAM,mBAAmB,CAAA;AAE1B,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,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FYND_SWAP_SELECTOR = exports.tychoRouterAbi = exports.FYND_ROUTERS = exports.FYND_CHAIN_IDS = void 0;
4
+ exports.fynd = fynd;
5
+ exports.scopeFynd = scopeFynd;
6
+ const viem_1 = require("viem");
7
+ const abi_1 = require("./abi");
8
+ const rules_1 = require("./rules");
9
+ /**
10
+ * fynd — Rhinestone's self-hosted Tycho aggregator.
11
+ *
12
+ * Unlike 0x there is no allowance-holder indirection: the TychoRouter is both
13
+ * the swap target and the ERC-20 approval spender, and every field we care about
14
+ * is a named static argument in the calldata head. That means nothing here is a
15
+ * magic offset — the rules are addressed by ABI parameter name.
16
+ */
17
+ /** Chains with a deployed, whitelisted TychoRouter. */
18
+ exports.FYND_CHAIN_IDS = [1, 56, 130, 137, 8453, 9745, 42161];
19
+ /**
20
+ * TychoRouter per chain — the contract fynd's encoded fills target.
21
+ *
22
+ * Mirrors `fynd.routers` in the orchestrator's quoters.jsonnet. A chain is
23
+ * listed only when the router is whitelisted in `IntentExecutionPolicy`, since a
24
+ * quoter enabled without a whitelisted target produces quotes that revert.
25
+ */
26
+ exports.FYND_ROUTERS = {
27
+ 1: '0xda892c989d07a18b5dd3f392d949f00df15c5736',
28
+ 56: '0x99748cbd931cb367dad265c5b2b4bd306d448e99',
29
+ 130: '0x764bc67b1036b00bc91221e988261f971a1c7ce4',
30
+ 137: '0x7cb3e87095f6cf95982dc6f57445171a6d3b511c',
31
+ 8453: '0x2d3524b9b5dae34b646614eebb1e038d403e4cac',
32
+ 9745: '0x8f9b3b0451efff0ae8100428aee35fa3cbc0b769',
33
+ 42161: '0xc1f838a5382bbb5729a0801c8ba73dfc861c4d34',
34
+ };
35
+ /**
36
+ * TychoRouter `singleSwap`, the entrypoint fynd's encoded quotes call.
37
+ *
38
+ * Only the argument TYPES determine the selector and the head layout, and those
39
+ * are pinned by {@link FYND_SWAP_SELECTOR}'s assertion in the test suite. The
40
+ * NAMES of the first five are corroborated by observed fynd calldata; the
41
+ * trailing tuple's component names are our own labels and carry no guarantee —
42
+ * we never address them, since a tuple containing `bytes` is dynamic and sits
43
+ * behind a pointer.
44
+ */
45
+ exports.tychoRouterAbi = [
46
+ {
47
+ type: 'function',
48
+ name: 'singleSwap',
49
+ stateMutability: 'payable',
50
+ inputs: [
51
+ { name: 'amountIn', type: 'uint256' },
52
+ { name: 'tokenIn', type: 'address' },
53
+ { name: 'tokenOut', type: 'address' },
54
+ { name: 'minAmountOut', type: 'uint256' },
55
+ { name: 'receiver', type: 'address' },
56
+ {
57
+ name: 'permit',
58
+ type: 'tuple',
59
+ components: [
60
+ { name: 'nonce', type: 'uint16' },
61
+ { name: 'token', type: 'address' },
62
+ { name: 'amount', type: 'uint256' },
63
+ { name: 'deadline', type: 'uint256' },
64
+ { name: 'signature', type: 'bytes' },
65
+ ],
66
+ },
67
+ { name: 'swap', type: 'bytes' },
68
+ ],
69
+ outputs: [{ name: 'amountOut', type: 'uint256' }],
70
+ },
71
+ ];
72
+ exports.FYND_SWAP_SELECTOR = (0, viem_1.toFunctionSelector)(exports.tychoRouterAbi[0]);
73
+ /** Head offsets derived from the ABI — never hardcoded. */
74
+ const OFFSETS = (0, abi_1.namedParamOffsets)(exports.tychoRouterAbi, 'singleSwap');
75
+ /**
76
+ * Scope a session to fynd swaps.
77
+ *
78
+ * Needs no addresses: the router is a per-chain deployment the SDK knows, and
79
+ * because it is also the approval spender there is no separate allowance target
80
+ * to reason about.
81
+ */
82
+ function fynd(options = {}) {
83
+ return {
84
+ id: 'fynd',
85
+ ...(options.maxSpend !== undefined ? { maxSpend: options.maxSpend } : {}),
86
+ };
87
+ }
88
+ function scopeFynd(ctx) {
89
+ const router = exports.FYND_ROUTERS[ctx.chainId];
90
+ if (router === undefined) {
91
+ throw new Error(`fynd is not available on chain ${ctx.chainId}. ` +
92
+ `Supported: ${exports.FYND_CHAIN_IDS.join(', ')}.`);
93
+ }
94
+ const rules = [
95
+ (0, rules_1.pin)(OFFSETS.tokenIn, ctx.sellToken),
96
+ (0, rules_1.pin)(OFFSETS.tokenOut, ctx.buyToken),
97
+ (0, rules_1.pin)(OFFSETS.receiver, ctx.recipient),
98
+ ];
99
+ if (ctx.cap !== undefined) {
100
+ rules.push((0, rules_1.cumulativeCap)(OFFSETS.amountIn, ctx.cap));
101
+ }
102
+ return {
103
+ approveSpenders: [router],
104
+ actions: [(0, rules_1.swapAction)(router, exports.FYND_SWAP_SELECTOR, rules)],
105
+ };
106
+ }
@@ -0,0 +1,147 @@
1
+ import { type Address } from 'viem';
2
+ import type { RhinestoneSwapVenue, RoutedRhinestoneSwapVenue } from '../../../../types';
3
+ import type { VenueContext, VenueScoping } from './rules';
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;AAC3E,OAAO,KAAK,EACV,mBAAmB,EACnB,yBAAyB,EAE1B,MAAM,mBAAmB,CAAA;AAG1B,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"}