@anywalletkit/protocol-swap2-fynd 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/router.ts
21
+ var router_exports = {};
22
+ __export(router_exports, {
23
+ FYND_CHAINS: () => FYND_CHAINS,
24
+ FyndTransactionError: () => FyndTransactionError,
25
+ ROUTER_NATIVE: () => ROUTER_NATIVE,
26
+ TYCHO_ROUTER_ABI: () => TYCHO_ROUTER_ABI,
27
+ TYCHO_ROUTER_SELECTORS: () => TYCHO_ROUTER_SELECTORS,
28
+ checkFyndTransaction: () => checkFyndTransaction
29
+ });
30
+ module.exports = __toCommonJS(router_exports);
31
+ var import_viem = require("viem");
32
+ var FYND_CHAINS = Object.freeze({
33
+ 1: { slug: "ethereum", name: "Ethereum", router: "0x1644D2477f809cc2C71bCCFd6Dc9497E3F83210d" },
34
+ 8453: { slug: "base", name: "Base", router: "0xAbA5B53b03eAfaD1C5fc8BD5Fc765fC85Bb3de67" },
35
+ 42161: { slug: "arbitrum", name: "Arbitrum", router: "0x924F147c50eA59f5180a26031A8b65B2aA1e81Cd" },
36
+ 56: { slug: "bsc", name: "BNB Smart Chain", router: "0x7f3D12BBaFb8955E51B3ab9588b34c8ad95BDA4e" },
37
+ 137: { slug: "polygon", name: "Polygon", router: "0xbd4e6011F03355C2A377Fd9Af939322A7d0A1bC1" },
38
+ 130: { slug: "unichain", name: "Unichain", router: "0xCba5574597Ad00EA250Fd106DAB4Fc7461949635" },
39
+ 4663: { slug: "robinhood", name: "Robinhood Chain", router: "0x09215a470bD585E59EB3F4B612fBD2678131fF9e" }
40
+ });
41
+ var ROUTER_NATIVE = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
42
+ var ZERO = "0x0000000000000000000000000000000000000000";
43
+ var PARAMS = "uint256 amountIn, address tokenIn, address tokenOut, uint256 expectedAmountOut, uint256 minAmountOut";
44
+ var FEE = "(uint32 clientFeeBps, address clientFeeReceiver, uint256 maxClientContribution, uint256 deadline, bytes clientSignature) clientFeeParams";
45
+ var PERMIT = "((address token, uint160 amount, uint48 expiration, uint48 nonce) details, address spender, uint256 sigDeadline) permitSingle, bytes signature";
46
+ var TYCHO_ROUTER_ABI = (0, import_viem.parseAbi)([
47
+ `function singleSwap(${PARAMS}, address receiver, ${FEE}, bytes swapData) payable returns (uint256)`,
48
+ `function sequentialSwap(${PARAMS}, address receiver, ${FEE}, bytes swaps) payable returns (uint256)`,
49
+ `function splitSwap(${PARAMS}, uint256 nTokens, address receiver, ${FEE}, bytes swaps) payable returns (uint256)`
50
+ ]);
51
+ var TYCHO_ROUTER_SELECTORS = new Set(
52
+ [
53
+ `singleSwap(${PARAMS}, address receiver, ${FEE}, bytes swapData)`,
54
+ `sequentialSwap(${PARAMS}, address receiver, ${FEE}, bytes swaps)`,
55
+ `splitSwap(${PARAMS}, uint256 nTokens, address receiver, ${FEE}, bytes swaps)`,
56
+ `singleSwapUsingVault(${PARAMS}, address receiver, ${FEE}, bytes swapData)`,
57
+ `sequentialSwapUsingVault(${PARAMS}, address receiver, ${FEE}, bytes swaps)`,
58
+ `splitSwapUsingVault(${PARAMS}, uint256 nTokens, address receiver, ${FEE}, bytes swaps)`,
59
+ `singleSwapPermit2(${PARAMS}, address receiver, ${FEE}, ${PERMIT}, bytes swapData)`,
60
+ `sequentialSwapPermit2(${PARAMS}, address receiver, ${FEE}, ${PERMIT}, bytes swaps)`,
61
+ `splitSwapPermit2(${PARAMS}, uint256 nTokens, address receiver, ${FEE}, ${PERMIT}, bytes swaps)`
62
+ ].map((sig) => (0, import_viem.toFunctionSelector)(`function ${sig}`))
63
+ );
64
+ var FyndTransactionError = class extends Error {
65
+ constructor(message) {
66
+ super(`fynd: ${message}`);
67
+ this.name = "FyndTransactionError";
68
+ }
69
+ };
70
+ var sameAddress = (a, b) => {
71
+ try {
72
+ return (0, import_viem.getAddress)(a) === (0, import_viem.getAddress)(b);
73
+ } catch {
74
+ return false;
75
+ }
76
+ };
77
+ var routerToken = (t) => sameAddress(t, ZERO) ? ROUTER_NATIVE : t;
78
+ function checkFyndTransaction(tx, expected) {
79
+ const chain = FYND_CHAINS[expected.chainId];
80
+ if (!chain) throw new FyndTransactionError(`chain ${expected.chainId} has no pinned Tycho router`);
81
+ if (!sameAddress(tx.to, chain.router)) throw new FyndTransactionError(`the transaction targets ${tx.to}, not the Tycho router on ${chain.name}`);
82
+ if (sameAddress(expected.receiver, ZERO) || sameAddress(expected.receiver, chain.router)) throw new FyndTransactionError("invalid receiver");
83
+ if (expected.amountIn <= 0n || expected.minAmountOut <= 0n || expected.minAmountOut > expected.expectedAmountOut) {
84
+ throw new FyndTransactionError("invalid quote amounts");
85
+ }
86
+ const nativeIn = sameAddress(expected.tokenIn, ZERO);
87
+ if (tx.value !== (nativeIn ? expected.amountIn : 0n)) throw new FyndTransactionError("native value does not match the input");
88
+ if (!/^0x(?:[0-9a-fA-F]{2}){4,}$/.test(tx.data)) throw new FyndTransactionError("malformed calldata");
89
+ let call;
90
+ try {
91
+ call = (0, import_viem.decodeFunctionData)({ abi: TYCHO_ROUTER_ABI, data: tx.data });
92
+ } catch {
93
+ throw new FyndTransactionError("not a plain singleSwap / sequentialSwap / splitSwap call (Permit2 and vault variants are not used)");
94
+ }
95
+ const a = call.args;
96
+ const [amountIn, tokenIn, tokenOut, expectedAmountOut, minAmountOut] = a;
97
+ const isSplit = call.functionName === "splitSwap";
98
+ const receiver = isSplit ? a[6] : a[5];
99
+ const fee = isSplit ? a[7] : a[6];
100
+ const swaps = isSplit ? a[8] : a[7];
101
+ if (amountIn !== expected.amountIn) throw new FyndTransactionError(`calldata spends ${amountIn}, not ${expected.amountIn}`);
102
+ if (!sameAddress(tokenIn, routerToken(expected.tokenIn)) || !sameAddress(tokenOut, routerToken(expected.tokenOut))) {
103
+ throw new FyndTransactionError("calldata swaps other tokens than requested");
104
+ }
105
+ if (!sameAddress(receiver, expected.receiver)) throw new FyndTransactionError(`calldata pays ${receiver}, not ${expected.receiver}`);
106
+ if (expectedAmountOut !== expected.expectedAmountOut || minAmountOut !== expected.minAmountOut) {
107
+ throw new FyndTransactionError("calldata amounts do not match the quote");
108
+ }
109
+ if (fee.clientFeeBps !== 0 || fee.maxClientContribution !== 0n || !sameAddress(fee.clientFeeReceiver, ZERO)) {
110
+ throw new FyndTransactionError("calldata carries client-fee parameters \u2014 this module charges none");
111
+ }
112
+ if (swaps === "0x" || isSplit && a[5] < 2n) throw new FyndTransactionError("empty or invalid route");
113
+ return { method: call.functionName };
114
+ }
115
+ //# sourceMappingURL=router.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/router.ts"],"sourcesContent":["/**\n * TychoRouterV3 — the only contract a Fynd quote may settle through (Fynd\n * License §3: every settlement of a Fynd output goes through the router in\n * Propeller Heads' registry). This file pins the router per chain and checks\n * Fynd's encoded transaction against the request before anything is approved\n * or sent: right contract, right method, the exact input, the requested\n * tokens, the user as receiver, the quoted output and minimum, and no client\n * fee. The route inside `swaps` is opaque and is not inspected here — the\n * router itself reverts below `minAmountOut`.\n */\nimport {decodeFunctionData, getAddress, parseAbi, toFunctionSelector, type Hex} from \"viem\";\n\n/** Router registry: github.com/propeller-heads/tycho crates/tycho-execution/config/deployment_registry.json (TychoRouterV3, effective 2026-09-01). */\nexport const FYND_CHAINS: Readonly<Record<number, {slug: string; name: string; router: `0x${string}`}>> = Object.freeze({\n 1: {slug: \"ethereum\", name: \"Ethereum\", router: \"0x1644D2477f809cc2C71bCCFd6Dc9497E3F83210d\"},\n 8453: {slug: \"base\", name: \"Base\", router: \"0xAbA5B53b03eAfaD1C5fc8BD5Fc765fC85Bb3de67\"},\n 42161: {slug: \"arbitrum\", name: \"Arbitrum\", router: \"0x924F147c50eA59f5180a26031A8b65B2aA1e81Cd\"},\n 56: {slug: \"bsc\", name: \"BNB Smart Chain\", router: \"0x7f3D12BBaFb8955E51B3ab9588b34c8ad95BDA4e\"},\n 137: {slug: \"polygon\", name: \"Polygon\", router: \"0xbd4e6011F03355C2A377Fd9Af939322A7d0A1bC1\"},\n 130: {slug: \"unichain\", name: \"Unichain\", router: \"0xCba5574597Ad00EA250Fd106DAB4Fc7461949635\"},\n 4663: {slug: \"robinhood\", name: \"Robinhood Chain\", router: \"0x09215a470bD585E59EB3F4B612fBD2678131fF9e\"},\n});\n\n/** The router's native-coin sentinel (lib/NativeETH.sol); Fynd's API uses the zero address instead. */\nexport const ROUTER_NATIVE = \"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE\";\nconst ZERO = \"0x0000000000000000000000000000000000000000\";\n\nconst PARAMS = \"uint256 amountIn, address tokenIn, address tokenOut, uint256 expectedAmountOut, uint256 minAmountOut\";\nconst FEE = \"(uint32 clientFeeBps, address clientFeeReceiver, uint256 maxClientContribution, uint256 deadline, bytes clientSignature) clientFeeParams\";\nconst PERMIT = \"((address token, uint160 amount, uint48 expiration, uint48 nonce) details, address spender, uint256 sigDeadline) permitSingle, bytes signature\";\n\n/** The three entry points a wallet swap uses (plain `transferFrom` from the user). */\nexport const TYCHO_ROUTER_ABI = parseAbi([\n `function singleSwap(${PARAMS}, address receiver, ${FEE}, bytes swapData) payable returns (uint256)`,\n `function sequentialSwap(${PARAMS}, address receiver, ${FEE}, bytes swaps) payable returns (uint256)`,\n `function splitSwap(${PARAMS}, uint256 nTokens, address receiver, ${FEE}, bytes swaps) payable returns (uint256)`,\n]);\n\n/** Every swap entry point of the router (Permit2 and vault variants included) — for recognizing history. */\nexport const TYCHO_ROUTER_SELECTORS: ReadonlySet<string> = new Set(\n [\n `singleSwap(${PARAMS}, address receiver, ${FEE}, bytes swapData)`,\n `sequentialSwap(${PARAMS}, address receiver, ${FEE}, bytes swaps)`,\n `splitSwap(${PARAMS}, uint256 nTokens, address receiver, ${FEE}, bytes swaps)`,\n `singleSwapUsingVault(${PARAMS}, address receiver, ${FEE}, bytes swapData)`,\n `sequentialSwapUsingVault(${PARAMS}, address receiver, ${FEE}, bytes swaps)`,\n `splitSwapUsingVault(${PARAMS}, uint256 nTokens, address receiver, ${FEE}, bytes swaps)`,\n `singleSwapPermit2(${PARAMS}, address receiver, ${FEE}, ${PERMIT}, bytes swapData)`,\n `sequentialSwapPermit2(${PARAMS}, address receiver, ${FEE}, ${PERMIT}, bytes swaps)`,\n `splitSwapPermit2(${PARAMS}, uint256 nTokens, address receiver, ${FEE}, ${PERMIT}, bytes swaps)`,\n ].map((sig) => toFunctionSelector(`function ${sig}`)),\n);\n\nexport class FyndTransactionError extends Error {\n constructor(message: string) {\n super(`fynd: ${message}`);\n this.name = \"FyndTransactionError\";\n }\n}\n\nexport interface ExpectedSwap {\n chainId: number;\n /** ERC-20 address, or the zero address for the native coin. */\n tokenIn: string;\n tokenOut: string;\n amountIn: bigint;\n receiver: string;\n /** Fynd's gross `amount_out` (before the router fee and slippage). */\n expectedAmountOut: bigint;\n /** Fynd's `min_amount_received` — what the router enforces on-chain. */\n minAmountOut: bigint;\n}\n\nexport interface EncodedTx {\n to: string;\n value: bigint;\n data: string;\n}\n\nconst sameAddress = (a: string, b: string) => {\n try {\n return getAddress(a) === getAddress(b);\n } catch {\n return false;\n }\n};\nconst routerToken = (t: string) => (sameAddress(t, ZERO) ? ROUTER_NATIVE : t);\n\n/** Refuses (throws {@link FyndTransactionError}) unless `tx` is exactly the swap the wallet asked for. */\nexport function checkFyndTransaction(tx: EncodedTx, expected: ExpectedSwap): {method: \"singleSwap\" | \"sequentialSwap\" | \"splitSwap\"} {\n const chain = FYND_CHAINS[expected.chainId];\n if (!chain) throw new FyndTransactionError(`chain ${expected.chainId} has no pinned Tycho router`);\n if (!sameAddress(tx.to, chain.router)) throw new FyndTransactionError(`the transaction targets ${tx.to}, not the Tycho router on ${chain.name}`);\n if (sameAddress(expected.receiver, ZERO) || sameAddress(expected.receiver, chain.router)) throw new FyndTransactionError(\"invalid receiver\");\n if (expected.amountIn <= 0n || expected.minAmountOut <= 0n || expected.minAmountOut > expected.expectedAmountOut) {\n throw new FyndTransactionError(\"invalid quote amounts\");\n }\n const nativeIn = sameAddress(expected.tokenIn, ZERO);\n if (tx.value !== (nativeIn ? expected.amountIn : 0n)) throw new FyndTransactionError(\"native value does not match the input\");\n if (!/^0x(?:[0-9a-fA-F]{2}){4,}$/.test(tx.data)) throw new FyndTransactionError(\"malformed calldata\");\n\n let call: ReturnType<typeof decodeFunctionData<typeof TYCHO_ROUTER_ABI>>;\n try {\n call = decodeFunctionData({abi: TYCHO_ROUTER_ABI, data: tx.data as Hex});\n } catch {\n throw new FyndTransactionError(\"not a plain singleSwap / sequentialSwap / splitSwap call (Permit2 and vault variants are not used)\");\n }\n const a = call.args;\n const [amountIn, tokenIn, tokenOut, expectedAmountOut, minAmountOut] = a;\n const isSplit = call.functionName === \"splitSwap\";\n const receiver = (isSplit ? a[6] : a[5]) as string;\n const fee = (isSplit ? a[7] : a[6]) as {clientFeeBps: number; clientFeeReceiver: string; maxClientContribution: bigint; deadline: bigint; clientSignature: string};\n const swaps = (isSplit ? a[8] : a[7]) as string;\n if (amountIn !== expected.amountIn) throw new FyndTransactionError(`calldata spends ${amountIn}, not ${expected.amountIn}`);\n if (!sameAddress(tokenIn, routerToken(expected.tokenIn)) || !sameAddress(tokenOut, routerToken(expected.tokenOut))) {\n throw new FyndTransactionError(\"calldata swaps other tokens than requested\");\n }\n if (!sameAddress(receiver, expected.receiver)) throw new FyndTransactionError(`calldata pays ${receiver}, not ${expected.receiver}`);\n if (expectedAmountOut !== expected.expectedAmountOut || minAmountOut !== expected.minAmountOut) {\n throw new FyndTransactionError(\"calldata amounts do not match the quote\");\n }\n if (fee.clientFeeBps !== 0 || fee.maxClientContribution !== 0n || !sameAddress(fee.clientFeeReceiver, ZERO)) {\n throw new FyndTransactionError(\"calldata carries client-fee parameters — this module charges none\");\n }\n if (swaps === \"0x\" || (isSplit && (a[5] as bigint) < 2n)) throw new FyndTransactionError(\"empty or invalid route\");\n return {method: call.functionName};\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,185 @@
1
+ /** Router registry: github.com/propeller-heads/tycho crates/tycho-execution/config/deployment_registry.json (TychoRouterV3, effective 2026-09-01). */
2
+ declare const FYND_CHAINS: Readonly<Record<number, {
3
+ slug: string;
4
+ name: string;
5
+ router: `0x${string}`;
6
+ }>>;
7
+ /** The router's native-coin sentinel (lib/NativeETH.sol); Fynd's API uses the zero address instead. */
8
+ declare const ROUTER_NATIVE = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
9
+ /** The three entry points a wallet swap uses (plain `transferFrom` from the user). */
10
+ declare const TYCHO_ROUTER_ABI: readonly [{
11
+ readonly name: "singleSwap";
12
+ readonly type: "function";
13
+ readonly stateMutability: "payable";
14
+ readonly inputs: readonly [{
15
+ readonly type: "uint256";
16
+ readonly name: "amountIn";
17
+ }, {
18
+ readonly type: "address";
19
+ readonly name: "tokenIn";
20
+ }, {
21
+ readonly type: "address";
22
+ readonly name: "tokenOut";
23
+ }, {
24
+ readonly type: "uint256";
25
+ readonly name: "expectedAmountOut";
26
+ }, {
27
+ readonly type: "uint256";
28
+ readonly name: "minAmountOut";
29
+ }, {
30
+ readonly type: "address";
31
+ readonly name: "receiver";
32
+ }, {
33
+ readonly type: "tuple";
34
+ readonly components: readonly [{
35
+ readonly type: "uint32";
36
+ readonly name: "clientFeeBps";
37
+ }, {
38
+ readonly type: "address";
39
+ readonly name: "clientFeeReceiver";
40
+ }, {
41
+ readonly type: "uint256";
42
+ readonly name: "maxClientContribution";
43
+ }, {
44
+ readonly type: "uint256";
45
+ readonly name: "deadline";
46
+ }, {
47
+ readonly type: "bytes";
48
+ readonly name: "clientSignature";
49
+ }];
50
+ readonly name: "clientFeeParams";
51
+ }, {
52
+ readonly type: "bytes";
53
+ readonly name: "swapData";
54
+ }];
55
+ readonly outputs: readonly [{
56
+ readonly type: "uint256";
57
+ }];
58
+ }, {
59
+ readonly name: "sequentialSwap";
60
+ readonly type: "function";
61
+ readonly stateMutability: "payable";
62
+ readonly inputs: readonly [{
63
+ readonly type: "uint256";
64
+ readonly name: "amountIn";
65
+ }, {
66
+ readonly type: "address";
67
+ readonly name: "tokenIn";
68
+ }, {
69
+ readonly type: "address";
70
+ readonly name: "tokenOut";
71
+ }, {
72
+ readonly type: "uint256";
73
+ readonly name: "expectedAmountOut";
74
+ }, {
75
+ readonly type: "uint256";
76
+ readonly name: "minAmountOut";
77
+ }, {
78
+ readonly type: "address";
79
+ readonly name: "receiver";
80
+ }, {
81
+ readonly type: "tuple";
82
+ readonly components: readonly [{
83
+ readonly type: "uint32";
84
+ readonly name: "clientFeeBps";
85
+ }, {
86
+ readonly type: "address";
87
+ readonly name: "clientFeeReceiver";
88
+ }, {
89
+ readonly type: "uint256";
90
+ readonly name: "maxClientContribution";
91
+ }, {
92
+ readonly type: "uint256";
93
+ readonly name: "deadline";
94
+ }, {
95
+ readonly type: "bytes";
96
+ readonly name: "clientSignature";
97
+ }];
98
+ readonly name: "clientFeeParams";
99
+ }, {
100
+ readonly type: "bytes";
101
+ readonly name: "swaps";
102
+ }];
103
+ readonly outputs: readonly [{
104
+ readonly type: "uint256";
105
+ }];
106
+ }, {
107
+ readonly name: "splitSwap";
108
+ readonly type: "function";
109
+ readonly stateMutability: "payable";
110
+ readonly inputs: readonly [{
111
+ readonly type: "uint256";
112
+ readonly name: "amountIn";
113
+ }, {
114
+ readonly type: "address";
115
+ readonly name: "tokenIn";
116
+ }, {
117
+ readonly type: "address";
118
+ readonly name: "tokenOut";
119
+ }, {
120
+ readonly type: "uint256";
121
+ readonly name: "expectedAmountOut";
122
+ }, {
123
+ readonly type: "uint256";
124
+ readonly name: "minAmountOut";
125
+ }, {
126
+ readonly type: "uint256";
127
+ readonly name: "nTokens";
128
+ }, {
129
+ readonly type: "address";
130
+ readonly name: "receiver";
131
+ }, {
132
+ readonly type: "tuple";
133
+ readonly components: readonly [{
134
+ readonly type: "uint32";
135
+ readonly name: "clientFeeBps";
136
+ }, {
137
+ readonly type: "address";
138
+ readonly name: "clientFeeReceiver";
139
+ }, {
140
+ readonly type: "uint256";
141
+ readonly name: "maxClientContribution";
142
+ }, {
143
+ readonly type: "uint256";
144
+ readonly name: "deadline";
145
+ }, {
146
+ readonly type: "bytes";
147
+ readonly name: "clientSignature";
148
+ }];
149
+ readonly name: "clientFeeParams";
150
+ }, {
151
+ readonly type: "bytes";
152
+ readonly name: "swaps";
153
+ }];
154
+ readonly outputs: readonly [{
155
+ readonly type: "uint256";
156
+ }];
157
+ }];
158
+ /** Every swap entry point of the router (Permit2 and vault variants included) — for recognizing history. */
159
+ declare const TYCHO_ROUTER_SELECTORS: ReadonlySet<string>;
160
+ declare class FyndTransactionError extends Error {
161
+ constructor(message: string);
162
+ }
163
+ interface ExpectedSwap {
164
+ chainId: number;
165
+ /** ERC-20 address, or the zero address for the native coin. */
166
+ tokenIn: string;
167
+ tokenOut: string;
168
+ amountIn: bigint;
169
+ receiver: string;
170
+ /** Fynd's gross `amount_out` (before the router fee and slippage). */
171
+ expectedAmountOut: bigint;
172
+ /** Fynd's `min_amount_received` — what the router enforces on-chain. */
173
+ minAmountOut: bigint;
174
+ }
175
+ interface EncodedTx {
176
+ to: string;
177
+ value: bigint;
178
+ data: string;
179
+ }
180
+ /** Refuses (throws {@link FyndTransactionError}) unless `tx` is exactly the swap the wallet asked for. */
181
+ declare function checkFyndTransaction(tx: EncodedTx, expected: ExpectedSwap): {
182
+ method: "singleSwap" | "sequentialSwap" | "splitSwap";
183
+ };
184
+
185
+ export { type EncodedTx, type ExpectedSwap, FYND_CHAINS, FyndTransactionError, ROUTER_NATIVE, TYCHO_ROUTER_ABI, TYCHO_ROUTER_SELECTORS, checkFyndTransaction };
@@ -0,0 +1,185 @@
1
+ /** Router registry: github.com/propeller-heads/tycho crates/tycho-execution/config/deployment_registry.json (TychoRouterV3, effective 2026-09-01). */
2
+ declare const FYND_CHAINS: Readonly<Record<number, {
3
+ slug: string;
4
+ name: string;
5
+ router: `0x${string}`;
6
+ }>>;
7
+ /** The router's native-coin sentinel (lib/NativeETH.sol); Fynd's API uses the zero address instead. */
8
+ declare const ROUTER_NATIVE = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
9
+ /** The three entry points a wallet swap uses (plain `transferFrom` from the user). */
10
+ declare const TYCHO_ROUTER_ABI: readonly [{
11
+ readonly name: "singleSwap";
12
+ readonly type: "function";
13
+ readonly stateMutability: "payable";
14
+ readonly inputs: readonly [{
15
+ readonly type: "uint256";
16
+ readonly name: "amountIn";
17
+ }, {
18
+ readonly type: "address";
19
+ readonly name: "tokenIn";
20
+ }, {
21
+ readonly type: "address";
22
+ readonly name: "tokenOut";
23
+ }, {
24
+ readonly type: "uint256";
25
+ readonly name: "expectedAmountOut";
26
+ }, {
27
+ readonly type: "uint256";
28
+ readonly name: "minAmountOut";
29
+ }, {
30
+ readonly type: "address";
31
+ readonly name: "receiver";
32
+ }, {
33
+ readonly type: "tuple";
34
+ readonly components: readonly [{
35
+ readonly type: "uint32";
36
+ readonly name: "clientFeeBps";
37
+ }, {
38
+ readonly type: "address";
39
+ readonly name: "clientFeeReceiver";
40
+ }, {
41
+ readonly type: "uint256";
42
+ readonly name: "maxClientContribution";
43
+ }, {
44
+ readonly type: "uint256";
45
+ readonly name: "deadline";
46
+ }, {
47
+ readonly type: "bytes";
48
+ readonly name: "clientSignature";
49
+ }];
50
+ readonly name: "clientFeeParams";
51
+ }, {
52
+ readonly type: "bytes";
53
+ readonly name: "swapData";
54
+ }];
55
+ readonly outputs: readonly [{
56
+ readonly type: "uint256";
57
+ }];
58
+ }, {
59
+ readonly name: "sequentialSwap";
60
+ readonly type: "function";
61
+ readonly stateMutability: "payable";
62
+ readonly inputs: readonly [{
63
+ readonly type: "uint256";
64
+ readonly name: "amountIn";
65
+ }, {
66
+ readonly type: "address";
67
+ readonly name: "tokenIn";
68
+ }, {
69
+ readonly type: "address";
70
+ readonly name: "tokenOut";
71
+ }, {
72
+ readonly type: "uint256";
73
+ readonly name: "expectedAmountOut";
74
+ }, {
75
+ readonly type: "uint256";
76
+ readonly name: "minAmountOut";
77
+ }, {
78
+ readonly type: "address";
79
+ readonly name: "receiver";
80
+ }, {
81
+ readonly type: "tuple";
82
+ readonly components: readonly [{
83
+ readonly type: "uint32";
84
+ readonly name: "clientFeeBps";
85
+ }, {
86
+ readonly type: "address";
87
+ readonly name: "clientFeeReceiver";
88
+ }, {
89
+ readonly type: "uint256";
90
+ readonly name: "maxClientContribution";
91
+ }, {
92
+ readonly type: "uint256";
93
+ readonly name: "deadline";
94
+ }, {
95
+ readonly type: "bytes";
96
+ readonly name: "clientSignature";
97
+ }];
98
+ readonly name: "clientFeeParams";
99
+ }, {
100
+ readonly type: "bytes";
101
+ readonly name: "swaps";
102
+ }];
103
+ readonly outputs: readonly [{
104
+ readonly type: "uint256";
105
+ }];
106
+ }, {
107
+ readonly name: "splitSwap";
108
+ readonly type: "function";
109
+ readonly stateMutability: "payable";
110
+ readonly inputs: readonly [{
111
+ readonly type: "uint256";
112
+ readonly name: "amountIn";
113
+ }, {
114
+ readonly type: "address";
115
+ readonly name: "tokenIn";
116
+ }, {
117
+ readonly type: "address";
118
+ readonly name: "tokenOut";
119
+ }, {
120
+ readonly type: "uint256";
121
+ readonly name: "expectedAmountOut";
122
+ }, {
123
+ readonly type: "uint256";
124
+ readonly name: "minAmountOut";
125
+ }, {
126
+ readonly type: "uint256";
127
+ readonly name: "nTokens";
128
+ }, {
129
+ readonly type: "address";
130
+ readonly name: "receiver";
131
+ }, {
132
+ readonly type: "tuple";
133
+ readonly components: readonly [{
134
+ readonly type: "uint32";
135
+ readonly name: "clientFeeBps";
136
+ }, {
137
+ readonly type: "address";
138
+ readonly name: "clientFeeReceiver";
139
+ }, {
140
+ readonly type: "uint256";
141
+ readonly name: "maxClientContribution";
142
+ }, {
143
+ readonly type: "uint256";
144
+ readonly name: "deadline";
145
+ }, {
146
+ readonly type: "bytes";
147
+ readonly name: "clientSignature";
148
+ }];
149
+ readonly name: "clientFeeParams";
150
+ }, {
151
+ readonly type: "bytes";
152
+ readonly name: "swaps";
153
+ }];
154
+ readonly outputs: readonly [{
155
+ readonly type: "uint256";
156
+ }];
157
+ }];
158
+ /** Every swap entry point of the router (Permit2 and vault variants included) — for recognizing history. */
159
+ declare const TYCHO_ROUTER_SELECTORS: ReadonlySet<string>;
160
+ declare class FyndTransactionError extends Error {
161
+ constructor(message: string);
162
+ }
163
+ interface ExpectedSwap {
164
+ chainId: number;
165
+ /** ERC-20 address, or the zero address for the native coin. */
166
+ tokenIn: string;
167
+ tokenOut: string;
168
+ amountIn: bigint;
169
+ receiver: string;
170
+ /** Fynd's gross `amount_out` (before the router fee and slippage). */
171
+ expectedAmountOut: bigint;
172
+ /** Fynd's `min_amount_received` — what the router enforces on-chain. */
173
+ minAmountOut: bigint;
174
+ }
175
+ interface EncodedTx {
176
+ to: string;
177
+ value: bigint;
178
+ data: string;
179
+ }
180
+ /** Refuses (throws {@link FyndTransactionError}) unless `tx` is exactly the swap the wallet asked for. */
181
+ declare function checkFyndTransaction(tx: EncodedTx, expected: ExpectedSwap): {
182
+ method: "singleSwap" | "sequentialSwap" | "splitSwap";
183
+ };
184
+
185
+ export { type EncodedTx, type ExpectedSwap, FYND_CHAINS, FyndTransactionError, ROUTER_NATIVE, TYCHO_ROUTER_ABI, TYCHO_ROUTER_SELECTORS, checkFyndTransaction };
package/dist/router.js ADDED
@@ -0,0 +1,17 @@
1
+ import {
2
+ FYND_CHAINS,
3
+ FyndTransactionError,
4
+ ROUTER_NATIVE,
5
+ TYCHO_ROUTER_ABI,
6
+ TYCHO_ROUTER_SELECTORS,
7
+ checkFyndTransaction
8
+ } from "./chunk-F5P3PLJN.js";
9
+ export {
10
+ FYND_CHAINS,
11
+ FyndTransactionError,
12
+ ROUTER_NATIVE,
13
+ TYCHO_ROUTER_ABI,
14
+ TYCHO_ROUTER_SELECTORS,
15
+ checkFyndTransaction
16
+ };
17
+ //# sourceMappingURL=router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/module.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@anywalletkit/protocol-swap2-fynd",
3
+ "type": "swap2",
4
+ "chains": [
5
+ "eip155:1",
6
+ "eip155:56",
7
+ "eip155:130",
8
+ "eip155:137",
9
+ "eip155:4663",
10
+ "eip155:8453",
11
+ "eip155:42161"
12
+ ],
13
+ "tier": "core",
14
+ "maintainers": [
15
+ "@ultratestbro"
16
+ ],
17
+ "conformance": "0.x",
18
+ "repo": "https://github.com/ultratestbro/anywalletkit",
19
+ "path": "packages/protocol-swap2-fynd",
20
+ "description": "Fynd same-chain swaps: Propeller Heads' solver over Tycho-indexed pools, settled through TychoRouterV3; router, method, tokens, amounts, receiver and minimum checked against the request before signing. Fynd outputs are under the Fynd License.",
21
+ "addedAt": "2026-10-24"
22
+ }
package/package.json CHANGED
@@ -1,6 +1,86 @@
1
1
  {
2
2
  "name": "@anywalletkit/protocol-swap2-fynd",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "Fynd (Propeller Heads) same-chain swaps for Any Wallet Kit: exact-input routes across the pools Tycho indexes on Ethereum, Base, Arbitrum, BNB Chain, Polygon, Unichain and Robinhood Chain, settled through TychoRouterV3 with the transaction checked against the request before signing.",
5
+ "license": "Apache-2.0",
6
+ "homepage": "https://docs.anywallet.tech",
7
+ "bugs": {
8
+ "url": "https://github.com/ultratestbro/anywalletkit/issues"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/ultratestbro/anywalletkit.git",
13
+ "directory": "packages/protocol-swap2-fynd"
14
+ },
15
+ "type": "module",
16
+ "sideEffects": false,
17
+ "main": "./dist/index.cjs",
18
+ "module": "./dist/index.js",
19
+ "types": "./dist/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.js",
24
+ "require": "./dist/index.cjs"
25
+ },
26
+ "./provider": {
27
+ "types": "./dist/provider.d.ts",
28
+ "import": "./dist/provider.js",
29
+ "require": "./dist/provider.cjs"
30
+ },
31
+ "./router": {
32
+ "types": "./dist/router.d.ts",
33
+ "import": "./dist/router.js",
34
+ "require": "./dist/router.cjs"
35
+ },
36
+ "./activity": {
37
+ "types": "./dist/activity.d.ts",
38
+ "import": "./dist/activity.js",
39
+ "require": "./dist/activity.cjs"
40
+ },
41
+ "./protocol": {
42
+ "types": "./dist/protocol.d.ts",
43
+ "import": "./dist/protocol.js",
44
+ "require": "./dist/protocol.cjs"
45
+ },
46
+ "./package.json": "./package.json"
47
+ },
48
+ "files": [
49
+ "dist",
50
+ "README.md",
51
+ "module.json"
52
+ ],
53
+ "dependencies": {
54
+ "viem": "^2.57.2",
55
+ "@anywalletkit/protocol-swap2-core": "0.3.0",
56
+ "@anywalletkit/swap2-assets": "0.1.2"
57
+ },
58
+ "peerDependencies": {
59
+ "@anywalletkit/core": "0.3.0"
60
+ },
61
+ "devDependencies": {
62
+ "@types/node": "^22",
63
+ "vitest": "^5.0.3",
64
+ "@anywalletkit/core": "0.3.0",
65
+ "@anywalletkit/conformance": "0.3.0"
66
+ },
67
+ "keywords": [
68
+ "swap2",
69
+ "swap",
70
+ "dex-aggregator",
71
+ "fynd",
72
+ "tycho",
73
+ "evm",
74
+ "anywalletkit"
75
+ ],
76
+ "publishConfig": {
77
+ "access": "public",
78
+ "provenance": true
79
+ },
80
+ "scripts": {
81
+ "build": "tsup",
82
+ "typecheck": "tsc --noEmit",
83
+ "test": "vitest run",
84
+ "lint": "echo ok"
85
+ }
6
86
  }