@peddles/sdk 0.2.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 (124) hide show
  1. package/CLAUDE_PROMPT.md +159 -0
  2. package/LICENSE +21 -0
  3. package/README.md +186 -0
  4. package/dist/abis.d.ts +19 -0
  5. package/dist/abis.d.ts.map +1 -0
  6. package/dist/abis.generated.d.ts +2029 -0
  7. package/dist/abis.generated.d.ts.map +1 -0
  8. package/dist/abis.generated.js +2723 -0
  9. package/dist/abis.generated.js.map +1 -0
  10. package/dist/abis.js +19 -0
  11. package/dist/abis.js.map +1 -0
  12. package/dist/artDex.d.ts +937 -0
  13. package/dist/artDex.d.ts.map +1 -0
  14. package/dist/artDex.js +155 -0
  15. package/dist/artDex.js.map +1 -0
  16. package/dist/client.d.ts +10 -0
  17. package/dist/client.d.ts.map +1 -0
  18. package/dist/client.js +2 -0
  19. package/dist/client.js.map +1 -0
  20. package/dist/clog.d.ts +75 -0
  21. package/dist/clog.d.ts.map +1 -0
  22. package/dist/clog.js +91 -0
  23. package/dist/clog.js.map +1 -0
  24. package/dist/clogInflow.d.ts +136 -0
  25. package/dist/clogInflow.d.ts.map +1 -0
  26. package/dist/clogInflow.js +177 -0
  27. package/dist/clogInflow.js.map +1 -0
  28. package/dist/deployments.d.ts +30 -0
  29. package/dist/deployments.d.ts.map +1 -0
  30. package/dist/deployments.generated.d.ts +92 -0
  31. package/dist/deployments.generated.d.ts.map +1 -0
  32. package/dist/deployments.generated.js +93 -0
  33. package/dist/deployments.generated.js.map +1 -0
  34. package/dist/deployments.js +52 -0
  35. package/dist/deployments.js.map +1 -0
  36. package/dist/feeSplit.d.ts +54 -0
  37. package/dist/feeSplit.d.ts.map +1 -0
  38. package/dist/feeSplit.js +69 -0
  39. package/dist/feeSplit.js.map +1 -0
  40. package/dist/feeTerms.d.ts +46 -0
  41. package/dist/feeTerms.d.ts.map +1 -0
  42. package/dist/feeTerms.js +80 -0
  43. package/dist/feeTerms.js.map +1 -0
  44. package/dist/index.d.ts +102 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +108 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/launch/abi.generated.d.ts +2667 -0
  49. package/dist/launch/abi.generated.d.ts.map +1 -0
  50. package/dist/launch/abi.generated.js +2472 -0
  51. package/dist/launch/abi.generated.js.map +1 -0
  52. package/dist/launch/addresses.d.ts +24 -0
  53. package/dist/launch/addresses.d.ts.map +1 -0
  54. package/dist/launch/addresses.js +35 -0
  55. package/dist/launch/addresses.js.map +1 -0
  56. package/dist/launch/feeTerms.d.ts +52 -0
  57. package/dist/launch/feeTerms.d.ts.map +1 -0
  58. package/dist/launch/feeTerms.js +72 -0
  59. package/dist/launch/feeTerms.js.map +1 -0
  60. package/dist/launch/index.d.ts +33 -0
  61. package/dist/launch/index.d.ts.map +1 -0
  62. package/dist/launch/index.js +24 -0
  63. package/dist/launch/index.js.map +1 -0
  64. package/dist/launch/revert.d.ts +23 -0
  65. package/dist/launch/revert.d.ts.map +1 -0
  66. package/dist/launch/revert.js +169 -0
  67. package/dist/launch/revert.js.map +1 -0
  68. package/dist/launch/salt.d.ts +90 -0
  69. package/dist/launch/salt.d.ts.map +1 -0
  70. package/dist/launch/salt.js +153 -0
  71. package/dist/launch/salt.js.map +1 -0
  72. package/dist/launch/stockCall.d.ts +122 -0
  73. package/dist/launch/stockCall.d.ts.map +1 -0
  74. package/dist/launch/stockCall.js +144 -0
  75. package/dist/launch/stockCall.js.map +1 -0
  76. package/dist/launch/tickMath.d.ts +39 -0
  77. package/dist/launch/tickMath.d.ts.map +1 -0
  78. package/dist/launch/tickMath.js +109 -0
  79. package/dist/launch/tickMath.js.map +1 -0
  80. package/dist/launch/variants.d.ts +64 -0
  81. package/dist/launch/variants.d.ts.map +1 -0
  82. package/dist/launch/variants.js +131 -0
  83. package/dist/launch/variants.js.map +1 -0
  84. package/dist/launch/wethCall.d.ts +133 -0
  85. package/dist/launch/wethCall.d.ts.map +1 -0
  86. package/dist/launch/wethCall.js +173 -0
  87. package/dist/launch/wethCall.js.map +1 -0
  88. package/dist/launch/wethPlan.d.ts +71 -0
  89. package/dist/launch/wethPlan.d.ts.map +1 -0
  90. package/dist/launch/wethPlan.js +146 -0
  91. package/dist/launch/wethPlan.js.map +1 -0
  92. package/dist/perps/abi.generated.d.ts +794 -0
  93. package/dist/perps/abi.generated.d.ts.map +1 -0
  94. package/dist/perps/abi.generated.js +738 -0
  95. package/dist/perps/abi.generated.js.map +1 -0
  96. package/dist/perps/index.d.ts +160 -0
  97. package/dist/perps/index.d.ts.map +1 -0
  98. package/dist/perps/index.js +147 -0
  99. package/dist/perps/index.js.map +1 -0
  100. package/package.json +105 -0
  101. package/src/abis.generated.ts +2738 -0
  102. package/src/abis.ts +35 -0
  103. package/src/artDex.ts +254 -0
  104. package/src/client.ts +10 -0
  105. package/src/clog.ts +171 -0
  106. package/src/clogInflow.ts +217 -0
  107. package/src/deployments.generated.ts +98 -0
  108. package/src/deployments.ts +60 -0
  109. package/src/feeSplit.ts +76 -0
  110. package/src/feeTerms.ts +137 -0
  111. package/src/index.ts +233 -0
  112. package/src/launch/abi.generated.ts +2481 -0
  113. package/src/launch/addresses.ts +58 -0
  114. package/src/launch/feeTerms.ts +93 -0
  115. package/src/launch/index.ts +93 -0
  116. package/src/launch/revert.ts +188 -0
  117. package/src/launch/salt.ts +216 -0
  118. package/src/launch/stockCall.ts +223 -0
  119. package/src/launch/tickMath.ts +139 -0
  120. package/src/launch/variants.ts +183 -0
  121. package/src/launch/wethCall.ts +240 -0
  122. package/src/launch/wethPlan.ts +257 -0
  123. package/src/perps/abi.generated.ts +741 -0
  124. package/src/perps/index.ts +263 -0
@@ -0,0 +1,263 @@
1
+ /**
2
+ * @peddles/sdk/perps — the Peddles perp launchpad (`PeddlesPerpFactory`).
3
+ *
4
+ * A perp market is a launched token whose Uniswap v4 pool runs through `PeddlesPerpHook`, which
5
+ * adds leveraged longs and shorts against the pool's own liquidity. One `create` call deploys the
6
+ * token, mines-in the hook, seeds the curve and (optionally) makes the creator's first buy.
7
+ *
8
+ * WHAT THIS MODULE DOES, AND WHAT IT LEAVES TO YOU:
9
+ *
10
+ * - `perpContracts(chainId)` the factory, hook deployer and treasury for a chain — or a throw.
11
+ * - `readPerpBase(...)` whether a base asset is allowed, and its curve, read LIVE.
12
+ * - `isValidPerpHookAddress` the factory's own hook-address check, pure.
13
+ * - `predictPerpHook/Token` the two CREATE2 addresses, derived locally.
14
+ * - `buildPerpCreate(...)` the exact `{ to, data, value }` for `create`.
15
+ *
16
+ * MINING THE HOOK SALT IS YOUR JOB. Uniswap v4 reads a hook's permissions from the low 14 bits of
17
+ * its address, so `hookSalt` must be searched until `predictPerpHook(...)` passes
18
+ * `isValidPerpHookAddress` — about 16,384 keccak attempts on average, a second or two. The recipe:
19
+ *
20
+ * 1. tokenInitCodeHash = factory.tokenInitCodeHash(name, symbol, tokenUri) (read)
21
+ * token = predictPerpToken(factory, tokenSalt, tokenInitCodeHash)
22
+ * confirm with factory.predictToken(tokenSalt, name, symbol, tokenUri)
23
+ * 2. hookInitCodeHash = factory.hookInitCodeHash(token) (read)
24
+ * 3. loop: salt → predictPerpHook(hookDeployer, salt, hookInitCodeHash) until
25
+ * isValidPerpHookAddress(address); start from a CSPRNG salt, not zero.
26
+ * confirm with factory.predictHook(salt, token)
27
+ * 4. buildPerpCreate({ ..., tokenSalt, hookSalt }) → simulate → sign.
28
+ *
29
+ * Run the loop off the UI thread. The Peddles app does it in a Web Worker
30
+ * (`apps/web/src/features/perps/launch/perpMine.worker.ts` over `perpSalt.ts#mineHookSalt`): a
31
+ * preallocated `0xff ‖ deployer ‖ salt ‖ initCodeHash` buffer, only the salt bytes incremented, one
32
+ * keccak per attempt, the low two bytes of the digest compared against the mask.
33
+ *
34
+ * Neither salt is bound to the sender. Anyone who copies a pending `create` and lands first takes
35
+ * the addresses; the original then reverts and pays only gas.
36
+ *
37
+ * `create` is NOT payable — the factory charges no launch fee — and a seed buy is pulled from the
38
+ * signer with `transferFrom`, so it needs an exact ERC-20 approval of `seedBuyBase` to the factory
39
+ * first. Every amount is a `bigint` in the base asset's own base units.
40
+ */
41
+ import type { Address, Hex } from 'viem';
42
+ import { encodeFunctionData, getAddress, getContractAddress, isAddress } from 'viem';
43
+ import type { ReadClient } from '../client.js';
44
+ import { isKnownChain, UnknownChainError } from '../deployments.js';
45
+ import { PERP_DEPLOYMENTS, type PerpChainId } from '../deployments.generated.js';
46
+ import { perpErrorsAbi, perpFactoryAbi, perpHookErrorsAbi, v4HooksErrorsAbi } from './abi.generated.js';
47
+
48
+ export { perpErrorsAbi, perpFactoryAbi, perpHookErrorsAbi, v4HooksErrorsAbi, PERP_DEPLOYMENTS };
49
+ export type { PerpChainId };
50
+
51
+ /* ------------------------------------------------------------------------------ addresses */
52
+
53
+ /** One chain's perp launchpad. */
54
+ export interface PerpContracts {
55
+ readonly chainId: number;
56
+ readonly factory: Address;
57
+ /** The contract that runs the hook's CREATE2 — the `from` of every hook address. */
58
+ readonly hookDeployer: Address;
59
+ /** The fee registry (`PeddlesPerpTreasury`) every market routes its spot fees through. */
60
+ readonly treasury: Address;
61
+ /** SSTORE2 pointer holding the hook's creation code. */
62
+ readonly hookCodePointer: Address;
63
+ /**
64
+ * Bases the deploy whitelisted, by symbol. CANDIDATES ONLY — whether one is allowed, and on
65
+ * what curve, is `readPerpBase`'s answer, never this list's.
66
+ */
67
+ readonly baseCandidates: Readonly<Record<string, Address>>;
68
+ }
69
+
70
+ /** Thrown for a chain the SDK knows but which has no live perp launchpad. */
71
+ export class PerpsUnavailableError extends Error {
72
+ readonly chainId: number;
73
+ constructor(chainId: number) {
74
+ super(
75
+ `Peddles perps are not deployed on chain ${chainId}. ` +
76
+ `Chains with perps: ${Object.keys(PERP_DEPLOYMENTS).join(', ')}.`,
77
+ );
78
+ this.name = 'PerpsUnavailableError';
79
+ this.chainId = chainId;
80
+ }
81
+ }
82
+
83
+ export function hasPerps(chainId: number): chainId is PerpChainId {
84
+ return Object.prototype.hasOwnProperty.call(PERP_DEPLOYMENTS, chainId);
85
+ }
86
+
87
+ /** Chains this build ships a perp launchpad for. */
88
+ export function perpChains(): number[] {
89
+ return Object.keys(PERP_DEPLOYMENTS).map(Number);
90
+ }
91
+
92
+ /**
93
+ * The perp contracts for one chain. FAILS CLOSED: an unknown chain throws `UnknownChainError`,
94
+ * a known chain without perps throws `PerpsUnavailableError` — never another chain's addresses.
95
+ */
96
+ export function perpContracts(chainId: number): PerpContracts {
97
+ if (!isKnownChain(chainId)) throw new UnknownChainError(chainId);
98
+ if (!hasPerps(chainId)) throw new PerpsUnavailableError(chainId);
99
+ const d = PERP_DEPLOYMENTS[chainId];
100
+ return {
101
+ chainId,
102
+ factory: d.PeddlesPerpFactory,
103
+ hookDeployer: d.PeddlesPerpHookDeployer,
104
+ treasury: d.PeddlesPerpTreasury,
105
+ hookCodePointer: d.hookCodePointer,
106
+ baseCandidates: d.baseCandidates,
107
+ };
108
+ }
109
+
110
+ /* ------------------------------------------------------------------------------ live reads */
111
+
112
+ /** A base asset's launch policy, as the factory reports it. */
113
+ export interface PerpBase {
114
+ readonly base: Address;
115
+ /** Launches on this base are accepted. `create` reverts `NotWhitelisted` otherwise. */
116
+ readonly allowed: boolean;
117
+ /** The curve's virtual base reserve `V`, in the base asset's base units. */
118
+ readonly v: bigint;
119
+ /** The band width `W`, in the base asset's base units. */
120
+ readonly tickWidth: bigint;
121
+ }
122
+
123
+ /**
124
+ * `factory.bases(base)` — read live. The creator has no curve choice: every market on a base is
125
+ * launched on exactly these values, so read them before offering the base.
126
+ */
127
+ export async function readPerpBase(client: ReadClient, chainId: number, base: Address): Promise<PerpBase> {
128
+ const { factory } = perpContracts(chainId);
129
+ const [allowed, v, tickWidth] = (await client.readContract({
130
+ address: factory,
131
+ abi: perpFactoryAbi,
132
+ functionName: 'bases',
133
+ args: [base],
134
+ })) as readonly [boolean, bigint, bigint];
135
+ return { base: getAddress(base), allowed, v, tickWidth };
136
+ }
137
+
138
+ /* ------------------------------------------------------------------------------ hook address */
139
+
140
+ /**
141
+ * The v4 hook permission bits a perp hook carries — `Hooks.*_FLAG` in v4-core, exactly the set
142
+ * `PeddlesPerpHook.getHookPermissions()` turns on. Identical on every chain.
143
+ */
144
+ export const PERP_HOOK_FLAG = {
145
+ BEFORE_INITIALIZE: 1 << 13,
146
+ BEFORE_ADD_LIQUIDITY: 1 << 11,
147
+ BEFORE_REMOVE_LIQUIDITY: 1 << 9,
148
+ BEFORE_SWAP: 1 << 7,
149
+ AFTER_SWAP: 1 << 6,
150
+ BEFORE_SWAP_RETURNS_DELTA: 1 << 3,
151
+ AFTER_SWAP_RETURNS_DELTA: 1 << 2,
152
+ } as const;
153
+
154
+ /** `PeddlesPerpFactory.FLAG_MASK` — v4's 14 permission bits. */
155
+ export const PERP_HOOK_FLAG_MASK = 0x3fff;
156
+
157
+ /** `PeddlesPerpFactory.FLAGS` — 0x2ACC. */
158
+ export const PERP_HOOK_FLAGS = Object.values(PERP_HOOK_FLAG).reduce((all, bit) => all | bit, 0);
159
+
160
+ /**
161
+ * `uint160(hook) & FLAG_MASK == FLAGS` — the factory's own `BadHookAddr` check, pure. Anything
162
+ * that is not a 20-byte hex address is `false`.
163
+ */
164
+ export function isValidPerpHookAddress(address: string): boolean {
165
+ if (!/^0x[0-9a-fA-F]{40}$/.test(address)) return false;
166
+ const low = parseInt(address.slice(-4), 16);
167
+ return (low & PERP_HOOK_FLAG_MASK) === PERP_HOOK_FLAGS;
168
+ }
169
+
170
+ const BYTES32 = /^0x[0-9a-fA-F]{64}$/;
171
+
172
+ function assertBytes32(name: string, value: string): asserts value is Hex {
173
+ if (!BYTES32.test(value)) throw new Error(`${name} must be 32 bytes of hex (0x + 64 digits).`);
174
+ }
175
+
176
+ /**
177
+ * The hook address `create` will deploy for `hookSalt`: the HOOK DEPLOYER's CREATE2 (not the
178
+ * factory's). `hookInitCodeHash` is `factory.hookInitCodeHash(token)` — read it, never assume it.
179
+ */
180
+ export function predictPerpHook(hookDeployer: Address, hookSalt: Hex, hookInitCodeHash: Hex): Address {
181
+ assertBytes32('hookSalt', hookSalt);
182
+ assertBytes32('hookInitCodeHash', hookInitCodeHash);
183
+ return getContractAddress({ opcode: 'CREATE2', from: hookDeployer, salt: hookSalt, bytecodeHash: hookInitCodeHash });
184
+ }
185
+
186
+ /**
187
+ * The token address `create` will deploy for `tokenSalt`: the FACTORY's CREATE2.
188
+ * `tokenInitCodeHash` is `factory.tokenInitCodeHash(name, symbol, tokenUri)`.
189
+ */
190
+ export function predictPerpToken(factory: Address, tokenSalt: Hex, tokenInitCodeHash: Hex): Address {
191
+ assertBytes32('tokenSalt', tokenSalt);
192
+ assertBytes32('tokenInitCodeHash', tokenInitCodeHash);
193
+ return getContractAddress({ opcode: 'CREATE2', from: factory, salt: tokenSalt, bytecodeHash: tokenInitCodeHash });
194
+ }
195
+
196
+ /* ------------------------------------------------------------------------------ create */
197
+
198
+ export interface PerpCreateParams {
199
+ readonly chainId: number;
200
+ /** Permanent. Non-empty — the factory reverts `BadParams` otherwise. */
201
+ readonly name: string;
202
+ /** Permanent. Non-empty. */
203
+ readonly symbol: string;
204
+ /** IPFS URI of the metadata document. Permanent. */
205
+ readonly tokenUri: string;
206
+ /** The base asset. Must be `allowed` per `readPerpBase` at send time. */
207
+ readonly base: Address;
208
+ readonly tokenSalt: Hex;
209
+ /** A MINED salt — see the module doc. Checked against the hook-address rule only if you pass `hookAddress`. */
210
+ readonly hookSalt: Hex;
211
+ /** Optional creator first buy, in the base asset's base units. `0n` for none. */
212
+ readonly seedBuyBase: bigint;
213
+ /**
214
+ * Optional: the hook address you mined for `hookSalt` (from `predictPerpHook`). When given, a
215
+ * value that does not carry the permission bits is refused here instead of reverting on chain.
216
+ */
217
+ readonly hookAddress?: Address;
218
+ }
219
+
220
+ /** A transaction request for the user's wallet. */
221
+ export interface PerpCreateCall {
222
+ readonly to: Address;
223
+ readonly data: Hex;
224
+ /** Always `0n`: `create` is not payable. */
225
+ readonly value: bigint;
226
+ }
227
+
228
+ /**
229
+ * The exact `{ to, data, value }` for `PeddlesPerpFactory.create` on `chainId`. Pure — no reads.
230
+ *
231
+ * Refuses (throws) what the factory would refuse and what cannot be encoded honestly: an unknown
232
+ * or perp-less chain, an empty name or symbol, a malformed base or salt, a negative or non-bigint
233
+ * seed buy, and a supplied `hookAddress` that fails `isValidPerpHookAddress`. It does NOT prove the
234
+ * base is allowed or the salt is mined — simulate the call before asking anyone to sign it.
235
+ */
236
+ export function buildPerpCreate(params: PerpCreateParams): PerpCreateCall {
237
+ const { factory } = perpContracts(params.chainId);
238
+ if (params.name.length === 0 || params.symbol.length === 0) throw new Error('name and symbol must be non-empty.');
239
+ if (!isAddress(params.base, { strict: false })) throw new Error('base must be an address.');
240
+ assertBytes32('tokenSalt', params.tokenSalt);
241
+ assertBytes32('hookSalt', params.hookSalt);
242
+ if (typeof params.seedBuyBase !== 'bigint') throw new Error('seedBuyBase must be a bigint in base units.');
243
+ if (params.seedBuyBase < 0n) throw new Error('seedBuyBase must not be negative.');
244
+ if (params.hookAddress !== undefined && !isValidPerpHookAddress(params.hookAddress)) {
245
+ throw new Error('hookAddress does not carry the perp hook permission bits — mine another hookSalt.');
246
+ }
247
+ const data = encodeFunctionData({
248
+ abi: perpFactoryAbi,
249
+ functionName: 'create',
250
+ args: [
251
+ {
252
+ name: params.name,
253
+ symbol: params.symbol,
254
+ tokenUri: params.tokenUri,
255
+ base: getAddress(params.base),
256
+ tokenSalt: params.tokenSalt,
257
+ hookSalt: params.hookSalt,
258
+ seedBuyBase: params.seedBuyBase,
259
+ },
260
+ ],
261
+ });
262
+ return { to: factory, data, value: 0n };
263
+ }