@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.
- package/CLAUDE_PROMPT.md +159 -0
- package/LICENSE +21 -0
- package/README.md +186 -0
- package/dist/abis.d.ts +19 -0
- package/dist/abis.d.ts.map +1 -0
- package/dist/abis.generated.d.ts +2029 -0
- package/dist/abis.generated.d.ts.map +1 -0
- package/dist/abis.generated.js +2723 -0
- package/dist/abis.generated.js.map +1 -0
- package/dist/abis.js +19 -0
- package/dist/abis.js.map +1 -0
- package/dist/artDex.d.ts +937 -0
- package/dist/artDex.d.ts.map +1 -0
- package/dist/artDex.js +155 -0
- package/dist/artDex.js.map +1 -0
- package/dist/client.d.ts +10 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +2 -0
- package/dist/client.js.map +1 -0
- package/dist/clog.d.ts +75 -0
- package/dist/clog.d.ts.map +1 -0
- package/dist/clog.js +91 -0
- package/dist/clog.js.map +1 -0
- package/dist/clogInflow.d.ts +136 -0
- package/dist/clogInflow.d.ts.map +1 -0
- package/dist/clogInflow.js +177 -0
- package/dist/clogInflow.js.map +1 -0
- package/dist/deployments.d.ts +30 -0
- package/dist/deployments.d.ts.map +1 -0
- package/dist/deployments.generated.d.ts +92 -0
- package/dist/deployments.generated.d.ts.map +1 -0
- package/dist/deployments.generated.js +93 -0
- package/dist/deployments.generated.js.map +1 -0
- package/dist/deployments.js +52 -0
- package/dist/deployments.js.map +1 -0
- package/dist/feeSplit.d.ts +54 -0
- package/dist/feeSplit.d.ts.map +1 -0
- package/dist/feeSplit.js +69 -0
- package/dist/feeSplit.js.map +1 -0
- package/dist/feeTerms.d.ts +46 -0
- package/dist/feeTerms.d.ts.map +1 -0
- package/dist/feeTerms.js +80 -0
- package/dist/feeTerms.js.map +1 -0
- package/dist/index.d.ts +102 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +108 -0
- package/dist/index.js.map +1 -0
- package/dist/launch/abi.generated.d.ts +2667 -0
- package/dist/launch/abi.generated.d.ts.map +1 -0
- package/dist/launch/abi.generated.js +2472 -0
- package/dist/launch/abi.generated.js.map +1 -0
- package/dist/launch/addresses.d.ts +24 -0
- package/dist/launch/addresses.d.ts.map +1 -0
- package/dist/launch/addresses.js +35 -0
- package/dist/launch/addresses.js.map +1 -0
- package/dist/launch/feeTerms.d.ts +52 -0
- package/dist/launch/feeTerms.d.ts.map +1 -0
- package/dist/launch/feeTerms.js +72 -0
- package/dist/launch/feeTerms.js.map +1 -0
- package/dist/launch/index.d.ts +33 -0
- package/dist/launch/index.d.ts.map +1 -0
- package/dist/launch/index.js +24 -0
- package/dist/launch/index.js.map +1 -0
- package/dist/launch/revert.d.ts +23 -0
- package/dist/launch/revert.d.ts.map +1 -0
- package/dist/launch/revert.js +169 -0
- package/dist/launch/revert.js.map +1 -0
- package/dist/launch/salt.d.ts +90 -0
- package/dist/launch/salt.d.ts.map +1 -0
- package/dist/launch/salt.js +153 -0
- package/dist/launch/salt.js.map +1 -0
- package/dist/launch/stockCall.d.ts +122 -0
- package/dist/launch/stockCall.d.ts.map +1 -0
- package/dist/launch/stockCall.js +144 -0
- package/dist/launch/stockCall.js.map +1 -0
- package/dist/launch/tickMath.d.ts +39 -0
- package/dist/launch/tickMath.d.ts.map +1 -0
- package/dist/launch/tickMath.js +109 -0
- package/dist/launch/tickMath.js.map +1 -0
- package/dist/launch/variants.d.ts +64 -0
- package/dist/launch/variants.d.ts.map +1 -0
- package/dist/launch/variants.js +131 -0
- package/dist/launch/variants.js.map +1 -0
- package/dist/launch/wethCall.d.ts +133 -0
- package/dist/launch/wethCall.d.ts.map +1 -0
- package/dist/launch/wethCall.js +173 -0
- package/dist/launch/wethCall.js.map +1 -0
- package/dist/launch/wethPlan.d.ts +71 -0
- package/dist/launch/wethPlan.d.ts.map +1 -0
- package/dist/launch/wethPlan.js +146 -0
- package/dist/launch/wethPlan.js.map +1 -0
- package/dist/perps/abi.generated.d.ts +794 -0
- package/dist/perps/abi.generated.d.ts.map +1 -0
- package/dist/perps/abi.generated.js +738 -0
- package/dist/perps/abi.generated.js.map +1 -0
- package/dist/perps/index.d.ts +160 -0
- package/dist/perps/index.d.ts.map +1 -0
- package/dist/perps/index.js +147 -0
- package/dist/perps/index.js.map +1 -0
- package/package.json +105 -0
- package/src/abis.generated.ts +2738 -0
- package/src/abis.ts +35 -0
- package/src/artDex.ts +254 -0
- package/src/client.ts +10 -0
- package/src/clog.ts +171 -0
- package/src/clogInflow.ts +217 -0
- package/src/deployments.generated.ts +98 -0
- package/src/deployments.ts +60 -0
- package/src/feeSplit.ts +76 -0
- package/src/feeTerms.ts +137 -0
- package/src/index.ts +233 -0
- package/src/launch/abi.generated.ts +2481 -0
- package/src/launch/addresses.ts +58 -0
- package/src/launch/feeTerms.ts +93 -0
- package/src/launch/index.ts +93 -0
- package/src/launch/revert.ts +188 -0
- package/src/launch/salt.ts +216 -0
- package/src/launch/stockCall.ts +223 -0
- package/src/launch/tickMath.ts +139 -0
- package/src/launch/variants.ts +183 -0
- package/src/launch/wethCall.ts +240 -0
- package/src/launch/wethPlan.ts +257 -0
- package/src/perps/abi.generated.ts +741 -0
- package/src/perps/index.ts +263 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// GENERATED FILE — DO NOT EDIT.
|
|
2
|
+
// Written by script/generate-deployments.mjs from contracts/deployments/<chainId>.json
|
|
3
|
+
// and <chainId>-perp.json.
|
|
4
|
+
// Re-run it after any deploy and commit the result.
|
|
5
|
+
|
|
6
|
+
/** Every Peddles contract address the SDK knows, by chain id. */
|
|
7
|
+
export const DEPLOYMENTS = {
|
|
8
|
+
4663: {
|
|
9
|
+
PeddlesClogVaultFactory: '0x1d2142699F1D818203738226240C605243a39b9e',
|
|
10
|
+
PeddlesFactoryV20: '0x51336A94B55Ad82D1099Dc82b27b151eDFfc1978',
|
|
11
|
+
PeddlesFeeForwarder: '0xc572A003d0Ab529da670a42177Fc583b03D5b026',
|
|
12
|
+
PeddlesFeeHook: '0xfe055282E3cD471A8b2cd922d31E75ECd3bBc0CC',
|
|
13
|
+
PeddlesHandleLauncher: '0x3720ca29D87E692000Ca71C56a56a3BA05B00CD1',
|
|
14
|
+
PeddlesHandlePotFactory: '0x2b5881c79a83C9EB17C67354F6737f3c9A120DE2',
|
|
15
|
+
PeddlesHolderRewardsFactory: '0xB22d6D7B1eC296AA79cbFf4ABE4992A726BE1978',
|
|
16
|
+
PeddlesLaunchOrchestratorV20: '0x81213e2cE27f03714143152aE5cf5c94094f1978',
|
|
17
|
+
PeddlesLiquidityActivationManager: '0xBE8d8999b53c5E5249feBafe5f505221F3e41978',
|
|
18
|
+
PeddlesNFTBondingGraduationOrchestratorV20: '0x6C5032DeDCc78b92b370C86a2aA0EA3AD0D71978',
|
|
19
|
+
PeddlesNFTFactory: '0x2e0029BeDCBb2f211Fd17EeBA8f40657cF6A1978',
|
|
20
|
+
PeddlesNftFeeDistributorFactory: '0x3081d5E69b7D12A330C9594C175F88EB77f31978',
|
|
21
|
+
PeddlesNftLiquidityReserve: '0x367F626775E22818C369BB3a276E3e9Fbc201978',
|
|
22
|
+
PeddlesNFTMarketplace: '0xC0374a0776449fE60dD1653C6421bEB41e911978',
|
|
23
|
+
PeddlesNftStockGraduationOrchestrator: '0x3DAd8D2B78C8a71f01600FDecA0b17af5F961978',
|
|
24
|
+
PeddlesPerpFactory: '0xCFA648281759C406E921F736c86ece2a9c4D1Fca',
|
|
25
|
+
PeddlesPerpHookDeployer: '0x3ab1848069cE7d19CD62ffB645292146cDd0139F',
|
|
26
|
+
PeddlesPerpTreasury: '0x2C322f48B2dD97f6c5050eDF5F349C7f70126dB5',
|
|
27
|
+
PeddlesRouteSwapRouter: '0xA4858047F9c7359CC0d9071598BFA74187739d8D',
|
|
28
|
+
PeddlesStockLaunchpad: '0x4b7Aa977eA4B95D9859D73e6bc922ecF2F881978',
|
|
29
|
+
PeddlesStockSwapRouter: '0x5A934c5eE47daa3b4562f9Dc9224bB00e9191978',
|
|
30
|
+
PeddlesStockVaultFactory: '0xa565E57046B51C4C1144b837A1871FF89c141978',
|
|
31
|
+
PeddlesTransferValidator: '0x2ADA0EA86925b5EbA1CF7B75e0d606C89b1B1978',
|
|
32
|
+
PeddlesV4LiquidityExecutor: '0xAB23BD1234AB1BCa48a97aB8e2baaFAaE7A21978',
|
|
33
|
+
PeddlesV4SwapRouter: '0xDd1F65380C9f7283A61FC489DCAceA352ee31978',
|
|
34
|
+
PeddlesVaultFactoryV20: '0xe6EE48916F87DD8E8Cab9D100a5E9717aE731978',
|
|
35
|
+
PeddlesVaultRegistry: '0xd322A44c4600dfDcF399Ceb7a32869e83d561978',
|
|
36
|
+
},
|
|
37
|
+
8453: {
|
|
38
|
+
PeddlesClogVaultFactory: '0x8563Aeec8C63d53BC8dfbE56FF9134a7b1e80503',
|
|
39
|
+
PeddlesFactoryV20: '0x90bD4d38F621529b4aD6480c221075B7317b1978',
|
|
40
|
+
PeddlesFeeForwarder: '0x77886951f19458B2FC27D4373553001A620264D3',
|
|
41
|
+
PeddlesFeeHook: '0xAB8E39207718f519865D6f0d52eFB31b231D40cc',
|
|
42
|
+
PeddlesFlywheel: '0x2E9366BF81d0D0ebA905927B55d3Fa3008891978',
|
|
43
|
+
PeddlesHandleLauncher: '0x265f0653aA62B30Fab11d0718D12CedE7965d7C4',
|
|
44
|
+
PeddlesHandlePotFactory: '0xC2575Cc3Eb3934C826Fa33FC9BA2e6DA8186D725',
|
|
45
|
+
PeddlesHolderRewardsFactory: '0xE4A89dCb02211409b50CEFd9B570E67dA2aD1978',
|
|
46
|
+
PeddlesLaunchOrchestratorV20: '0x7e7E62B41E447B6526E89D0AAAd7Cac9FB9f1978',
|
|
47
|
+
PeddlesLiquidityActivationManager: '0xa8bfd83d21eec43F9a984C6B13a31Ce1df191978',
|
|
48
|
+
PeddlesNFTBondingGraduationOrchestratorV20: '0xB7bB0f25B1AE59adcAa2787C68cd50296d2B1978',
|
|
49
|
+
PeddlesNFTFactory: '0xd111413B91b0E31865464658071099779e2C1978',
|
|
50
|
+
PeddlesNftFeeDistributorFactory: '0x9B0c155e7248706860c9707c2a8f41A0a2281978',
|
|
51
|
+
PeddlesNftLiquidityReserve: '0x444C34FF8503eA088a36792659D7c34D7F931978',
|
|
52
|
+
PeddlesNFTMarketplace: '0x5362a7DE96E8b3474Aef1a0fE6F6e2427Bd81978',
|
|
53
|
+
PeddlesNftStockGraduationOrchestrator: '0xD6d41a5029EC7F78886C135A71A9c85e96781978',
|
|
54
|
+
PeddlesPerpFactory: '0x71a8EF2288fFdB3b1d546C33a16F0d4104E11488',
|
|
55
|
+
PeddlesPerpHookDeployer: '0x9122dAd755B6F8c1DB5F2B7c494580169fB327F2',
|
|
56
|
+
PeddlesPerpTreasury: '0xDB77eA86006E530D25789F2cf2aeB7bd2C7c3745',
|
|
57
|
+
PeddlesRouteSwapRouter: '0x8f04A804Ba989d38A68D93eb8679d3a967f2b079',
|
|
58
|
+
PeddlesStockLaunchpad: '0x680805FBd0D6225d0bE4E12A2B73b2Ed83a31978',
|
|
59
|
+
PeddlesStockSwapRouter: '0xd150B1E102493e4F35A1e95DdA80948C0e581978',
|
|
60
|
+
PeddlesStockVaultFactory: '0x5EF74DFc6Dfd9e5dAb314Df5747AC4E117e41978',
|
|
61
|
+
PeddlesToken: '0xCF2Be52981b5BD563C268a0e2b17D50B53e21978',
|
|
62
|
+
PeddlesTransferValidator: '0xD1390f3E4741963A7eEd1E53163a1B09c97e1978',
|
|
63
|
+
PeddlesV4LiquidityExecutor: '0x4Ca94531420F0e4f5a612475c8c4E35E9c8d1978',
|
|
64
|
+
PeddlesV4SwapRouter: '0xbf7A709cBf71b303B856126D3aEb5051a07c1978',
|
|
65
|
+
PeddlesVaultFactoryV20: '0xD4fe62B8cd5a7e07fF09D6D1151fA5BC8A771978',
|
|
66
|
+
PeddlesVaultRegistry: '0xb390c4A83D8B308b378250a59F273A7c9aDF1978',
|
|
67
|
+
},
|
|
68
|
+
} as const;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The perp launchpad on each chain that has one (from contracts/deployments/<chainId>-perp.json).
|
|
72
|
+
* `baseCandidates` are the bases the deploy whitelisted — CANDIDATES only: whether a base is
|
|
73
|
+
* allowed, and on what curve, is read live from `PeddlesPerpFactory.bases(base)`.
|
|
74
|
+
*/
|
|
75
|
+
export const PERP_DEPLOYMENTS = {
|
|
76
|
+
4663: {
|
|
77
|
+
PeddlesPerpFactory: '0xCFA648281759C406E921F736c86ece2a9c4D1Fca',
|
|
78
|
+
PeddlesPerpHookDeployer: '0x3ab1848069cE7d19CD62ffB645292146cDd0139F',
|
|
79
|
+
PeddlesPerpTreasury: '0x2C322f48B2dD97f6c5050eDF5F349C7f70126dB5',
|
|
80
|
+
hookCodePointer: '0xf97907D6AA1Ae3D3Ec3247eEfA998C8D6BE78605',
|
|
81
|
+
baseCandidates: {
|
|
82
|
+
WETH: '0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73',
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
8453: {
|
|
86
|
+
PeddlesPerpFactory: '0x71a8EF2288fFdB3b1d546C33a16F0d4104E11488',
|
|
87
|
+
PeddlesPerpHookDeployer: '0x9122dAd755B6F8c1DB5F2B7c494580169fB327F2',
|
|
88
|
+
PeddlesPerpTreasury: '0xDB77eA86006E530D25789F2cf2aeB7bd2C7c3745',
|
|
89
|
+
hookCodePointer: '0xDAE55090fF0ffb81E3da33C79950DF7Ef78379aA',
|
|
90
|
+
baseCandidates: {
|
|
91
|
+
WETH: '0x4200000000000000000000000000000000000006',
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
} as const;
|
|
95
|
+
|
|
96
|
+
export type KnownChainId = keyof typeof DEPLOYMENTS;
|
|
97
|
+
export type PerpChainId = keyof typeof PERP_DEPLOYMENTS;
|
|
98
|
+
export type ContractName<C extends KnownChainId = KnownChainId> = keyof (typeof DEPLOYMENTS)[C];
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { DEPLOYMENTS, type KnownChainId, type ContractName } from './deployments.generated.js';
|
|
2
|
+
|
|
3
|
+
export { DEPLOYMENTS };
|
|
4
|
+
export type { KnownChainId, ContractName };
|
|
5
|
+
|
|
6
|
+
/** Thrown when the SDK is asked about a chain it ships no address book for. */
|
|
7
|
+
export class UnknownChainError extends Error {
|
|
8
|
+
readonly chainId: number;
|
|
9
|
+
constructor(chainId: number) {
|
|
10
|
+
super(
|
|
11
|
+
`Peddles SDK has no deployment for chain ${chainId}. ` +
|
|
12
|
+
`Known chains: ${Object.keys(DEPLOYMENTS).join(', ')}. ` +
|
|
13
|
+
`Pass an explicit address map if you are pointing at a private deployment.`,
|
|
14
|
+
);
|
|
15
|
+
this.name = 'UnknownChainError';
|
|
16
|
+
this.chainId = chainId;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Thrown when a chain is known but does not carry the contract being asked for. */
|
|
21
|
+
export class UnknownContractError extends Error {
|
|
22
|
+
constructor(chainId: number, name: string) {
|
|
23
|
+
super(
|
|
24
|
+
`Peddles SDK has no address for ${name} on chain ${chainId}. ` +
|
|
25
|
+
`That deployment may predate the contract.`,
|
|
26
|
+
);
|
|
27
|
+
this.name = 'UnknownContractError';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function isKnownChain(chainId: number): chainId is KnownChainId {
|
|
32
|
+
return Object.prototype.hasOwnProperty.call(DEPLOYMENTS, chainId);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The address book for one chain.
|
|
37
|
+
*
|
|
38
|
+
* FAILS CLOSED, ALWAYS. An unknown chain throws rather than returning a default
|
|
39
|
+
* or an empty map, and a missing contract throws rather than returning
|
|
40
|
+
* `undefined` or the zero address. Both of those alternatives look like a
|
|
41
|
+
* working call right up until a transaction is signed against nothing.
|
|
42
|
+
*
|
|
43
|
+
* This is not hypothetical in this codebase: every address changed in a single
|
|
44
|
+
* redeploy, and one stale copy of this list had the API quoting a launchpad that
|
|
45
|
+
* no longer existed — an error that surfaced as "neither leg is a launched coin"
|
|
46
|
+
* rather than as a missing address.
|
|
47
|
+
*/
|
|
48
|
+
export function deploymentFor(chainId: number): Readonly<Record<string, `0x${string}`>> {
|
|
49
|
+
if (!isKnownChain(chainId)) throw new UnknownChainError(chainId);
|
|
50
|
+
return DEPLOYMENTS[chainId] as Readonly<Record<string, `0x${string}`>>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** One address, or a throw. Never a default, never the zero address. */
|
|
54
|
+
export function addressOf(chainId: number, name: string): `0x${string}` {
|
|
55
|
+
const book = deploymentFor(chainId);
|
|
56
|
+
// Own properties only: `book['constructor']` would otherwise return a function.
|
|
57
|
+
const found = Object.prototype.hasOwnProperty.call(book, name) ? book[name] : undefined;
|
|
58
|
+
if (!found || /^0x0{40}$/.test(found)) throw new UnknownContractError(chainId, name);
|
|
59
|
+
return found;
|
|
60
|
+
}
|
package/src/feeSplit.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How one charged fee divides — the exact arithmetic `PeddlesFeeHook._book` runs.
|
|
3
|
+
*
|
|
4
|
+
* IMPORT-FREE on purpose, like `clogInflow.ts`: it is pure bigint maths with no
|
|
5
|
+
* runtime, so a UI, an indexer or a test can use it without pulling in viem.
|
|
6
|
+
*
|
|
7
|
+
* THE MODEL (fixed at launch, never changed afterwards):
|
|
8
|
+
*
|
|
9
|
+
* creatorTaxBps the ALL-IN rate a trader pays, 100..1000 (1%..10%)
|
|
10
|
+
* excessToCreatorBps the creator's share of everything ABOVE the two fixed
|
|
11
|
+
* 0.50% legs, 0..10000; the rest of that excess is holders'
|
|
12
|
+
*
|
|
13
|
+
* platform = fee × 50 / creatorTaxBps (floored)
|
|
14
|
+
* creatorBase = fee × 50 / creatorTaxBps (floored)
|
|
15
|
+
* excess = fee − platform − creatorBase
|
|
16
|
+
* creator = creatorBase + excess × excessToCreatorBps / 10000 (floored)
|
|
17
|
+
* holders = fee − platform − creator (the remainder)
|
|
18
|
+
*
|
|
19
|
+
* Every floor lands its dust with holders, so `platform + creator + holders`
|
|
20
|
+
* equals `fee` exactly; at `excessToCreatorBps == 10000` the holder leg is 0.
|
|
21
|
+
*
|
|
22
|
+
* `fee` is what the hook CHARGED, in the pool's quote asset. It is not the trade
|
|
23
|
+
* size: the hook charges `gross × creatorTaxBps / 10000` (floored) on an exact-in
|
|
24
|
+
* trade and grosses up an exact-out one. During a pool's opening window the hook
|
|
25
|
+
* uses a different, fixed split (½ liquidity / ¼ creator / ¼ platform) that this
|
|
26
|
+
* function does not model.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/** `PeddlesFeeHook.PLATFORM_FEE_BPS`. */
|
|
30
|
+
export const PLATFORM_FEE_BPS = 50;
|
|
31
|
+
/** `PeddlesFeeHook.CREATOR_FEE_BPS`. */
|
|
32
|
+
export const CREATOR_FEE_BPS = 50;
|
|
33
|
+
/** `PeddlesFeeHook.MIN_CREATOR_TAX_BPS` — the two fixed legs added together. */
|
|
34
|
+
export const MIN_CREATOR_TAX_BPS = PLATFORM_FEE_BPS + CREATOR_FEE_BPS;
|
|
35
|
+
/** `PeddlesFeeHook.MAX_CREATOR_TAX_BPS`. */
|
|
36
|
+
export const MAX_CREATOR_TAX_BPS = 1000;
|
|
37
|
+
/** Basis-point denominator. */
|
|
38
|
+
export const BPS = 10_000;
|
|
39
|
+
/** `PeddlesFeeHook.MAX_FEE_AMOUNT` — the largest fee one accrual can represent (int128 max). */
|
|
40
|
+
export const MAX_FEE_AMOUNT = (1n << 127n) - 1n;
|
|
41
|
+
|
|
42
|
+
/** One fee, divided. Base units of the pool's quote asset. */
|
|
43
|
+
export interface FeeSplit {
|
|
44
|
+
readonly platform: bigint;
|
|
45
|
+
readonly creator: bigint;
|
|
46
|
+
readonly holders: bigint;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function assertBps(label: string, value: number, min: number, max: number): void {
|
|
50
|
+
if (!Number.isInteger(value) || value < min || value > max) {
|
|
51
|
+
throw new RangeError(`${label} must be an integer in [${min}, ${max}], got ${String(value)}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Divide a charged fee between platform, creator and holders.
|
|
57
|
+
*
|
|
58
|
+
* Throws `RangeError` for terms `registerPool` would refuse, and for a fee the
|
|
59
|
+
* hook would not charge at all (negative, or above `MAX_FEE_AMOUNT`). Returning a
|
|
60
|
+
* split for either would describe a charge that cannot happen.
|
|
61
|
+
*/
|
|
62
|
+
export function feeSplit(creatorTaxBps: number, excessToCreatorBps: number, fee: bigint): FeeSplit {
|
|
63
|
+
assertBps('creatorTaxBps', creatorTaxBps, MIN_CREATOR_TAX_BPS, MAX_CREATOR_TAX_BPS);
|
|
64
|
+
assertBps('excessToCreatorBps', excessToCreatorBps, 0, BPS);
|
|
65
|
+
if (typeof fee !== 'bigint') throw new TypeError('fee must be a bigint in base units');
|
|
66
|
+
if (fee < 0n) throw new RangeError(`fee must not be negative, got ${fee}`);
|
|
67
|
+
if (fee > MAX_FEE_AMOUNT) throw new RangeError(`fee ${fee} exceeds MAX_FEE_AMOUNT; the hook charges nothing`);
|
|
68
|
+
|
|
69
|
+
const total = BigInt(creatorTaxBps);
|
|
70
|
+
const platform = (fee * BigInt(PLATFORM_FEE_BPS)) / total;
|
|
71
|
+
const creatorBase = (fee * BigInt(CREATOR_FEE_BPS)) / total;
|
|
72
|
+
const excess = fee - platform - creatorBase;
|
|
73
|
+
const creator = creatorBase + (excess * BigInt(excessToCreatorBps)) / BigInt(BPS);
|
|
74
|
+
const holders = fee - platform - creator;
|
|
75
|
+
return { platform, creator, holders };
|
|
76
|
+
}
|
package/src/feeTerms.ts
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import type { ReadClient } from './client.js';
|
|
2
|
+
import type { Address, Hex } from 'viem';
|
|
3
|
+
import { feeHookAbi, liquidityExecutorAbi, stockLaunchpadAbi } from './abis.js';
|
|
4
|
+
import { addressOf } from './deployments.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A launch pool's permanent terms, read from `PeddlesFeeHook`.
|
|
8
|
+
*
|
|
9
|
+
* Every launch pool — stock-paired, WETH-paired, Clog, and both NFT graduations —
|
|
10
|
+
* is registered with the hook once, at launch, and nothing can change these
|
|
11
|
+
* afterwards. What a buyer pays is `creatorTaxBps`; `excessToCreatorBps` decides
|
|
12
|
+
* who receives the part above the fixed 1% (creator vs holders).
|
|
13
|
+
*/
|
|
14
|
+
export interface PoolTerms {
|
|
15
|
+
readonly poolId: Hex;
|
|
16
|
+
readonly token: Address;
|
|
17
|
+
/** The asset the tax is charged in, and the asset holder rewards pay out in. */
|
|
18
|
+
readonly quote: Address;
|
|
19
|
+
readonly creator: Address;
|
|
20
|
+
/** All-in tax a trader pays, in bps (100..1000). Excludes any opening-window premium. */
|
|
21
|
+
readonly creatorTaxBps: number;
|
|
22
|
+
/** Creator's share of the tax above the fixed 1%, in bps (0..10000). The rest goes to holders. */
|
|
23
|
+
readonly excessToCreatorBps: number;
|
|
24
|
+
/** The pool's `PeddlesHolderRewards` distributor. */
|
|
25
|
+
readonly rewards: Address;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Why `getPoolTerms` could not answer. It never substitutes a default. */
|
|
29
|
+
export type PoolTermsFailure = 'legacy-fee-hook' | 'not-registered' | 'ambiguous';
|
|
30
|
+
|
|
31
|
+
export class PoolTermsUnavailableError extends Error {
|
|
32
|
+
readonly reason: PoolTermsFailure;
|
|
33
|
+
constructor(reason: PoolTermsFailure, message: string) {
|
|
34
|
+
super(message);
|
|
35
|
+
this.name = 'PoolTermsUnavailableError';
|
|
36
|
+
this.reason = reason;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const ZERO_ID = /^0x0{64}$/i;
|
|
41
|
+
|
|
42
|
+
interface RawPoolConfig {
|
|
43
|
+
quote: Address;
|
|
44
|
+
creatorTaxBps: number;
|
|
45
|
+
excessToCreatorBps: number;
|
|
46
|
+
registered: boolean;
|
|
47
|
+
token: Address;
|
|
48
|
+
creator: Address;
|
|
49
|
+
rewards: Address;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The launch terms of `token`'s pool on `chainId`.
|
|
54
|
+
*
|
|
55
|
+
* FINDING THE POOL. The hook is keyed by pool id, not by token, so the id comes
|
|
56
|
+
* from the contract that created the pool: `PeddlesStockLaunchpad.coins(token)`
|
|
57
|
+
* for stock-paired coins (including NFT stock graduations), and
|
|
58
|
+
* `PeddlesV4LiquidityExecutor.poolIdFor(token)` for WETH-paired ones. The hook's
|
|
59
|
+
* own record must then name this token and be registered — anything else throws.
|
|
60
|
+
*
|
|
61
|
+
* FAILS CLOSED, the same as the address book:
|
|
62
|
+
* - a deployment whose hook predates the fixed-terms model throws
|
|
63
|
+
* (`legacy-fee-hook`) — its third config field meant something else, so
|
|
64
|
+
* decoding it with this ABI would return a plausible, wrong number;
|
|
65
|
+
* - a token with no registered launch pool throws (`not-registered`).
|
|
66
|
+
*/
|
|
67
|
+
export async function getPoolTerms(client: ReadClient, chainId: number, token: Address): Promise<PoolTerms> {
|
|
68
|
+
const hook = addressOf(chainId, 'PeddlesFeeHook');
|
|
69
|
+
const launchpad = addressOf(chainId, 'PeddlesStockLaunchpad');
|
|
70
|
+
const executor = addressOf(chainId, 'PeddlesV4LiquidityExecutor');
|
|
71
|
+
|
|
72
|
+
const [factory, coin, wethPoolId] = await client.multicall({
|
|
73
|
+
allowFailure: true,
|
|
74
|
+
contracts: [
|
|
75
|
+
{ address: hook, abi: feeHookAbi, functionName: 'holderRewardsFactory' },
|
|
76
|
+
{ address: launchpad, abi: stockLaunchpadAbi, functionName: 'coins', args: [token] },
|
|
77
|
+
{ address: executor, abi: liquidityExecutorAbi, functionName: 'poolIdFor', args: [token] },
|
|
78
|
+
],
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// `holderRewardsFactory` exists only on the fixed-terms hook. A hook that does not
|
|
82
|
+
// answer it is the previous model, not a transient failure to be guessed around.
|
|
83
|
+
if (factory.status !== 'success' || /^0x0{40}$/i.test(factory.result as string)) {
|
|
84
|
+
throw new PoolTermsUnavailableError(
|
|
85
|
+
'legacy-fee-hook',
|
|
86
|
+
`PeddlesFeeHook ${hook} on chain ${chainId} does not expose holderRewardsFactory(); ` +
|
|
87
|
+
`this deployment predates fixed launch terms, so they cannot be read from it.`,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const candidates: Hex[] = [];
|
|
92
|
+
if (coin.status === 'success') {
|
|
93
|
+
const poolId = (coin.result as readonly unknown[])[2] as Hex;
|
|
94
|
+
if (!ZERO_ID.test(poolId)) candidates.push(poolId);
|
|
95
|
+
}
|
|
96
|
+
if (wethPoolId.status === 'success' && !ZERO_ID.test(wethPoolId.result as Hex)) {
|
|
97
|
+
candidates.push(wethPoolId.result as Hex);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const configs =
|
|
101
|
+
candidates.length === 0
|
|
102
|
+
? []
|
|
103
|
+
: await client.multicall({
|
|
104
|
+
allowFailure: false,
|
|
105
|
+
contracts: candidates.map(
|
|
106
|
+
(poolId) => ({ address: hook, abi: feeHookAbi, functionName: 'poolConfig', args: [poolId] }) as const,
|
|
107
|
+
),
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const matches = candidates
|
|
111
|
+
.map((poolId, i) => ({ poolId, config: configs[i] as unknown as RawPoolConfig }))
|
|
112
|
+
.filter(({ config }) => config.registered && config.token.toLowerCase() === token.toLowerCase());
|
|
113
|
+
|
|
114
|
+
if (matches.length === 0) {
|
|
115
|
+
throw new PoolTermsUnavailableError(
|
|
116
|
+
'not-registered',
|
|
117
|
+
`No registered Peddles launch pool for ${token} on chain ${chainId}.`,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
if (matches.length > 1) {
|
|
121
|
+
throw new PoolTermsUnavailableError(
|
|
122
|
+
'ambiguous',
|
|
123
|
+
`${token} has more than one registered launch pool on chain ${chainId}; refusing to pick one.`,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const { poolId, config } = matches[0] as { poolId: Hex; config: RawPoolConfig };
|
|
128
|
+
return {
|
|
129
|
+
poolId,
|
|
130
|
+
token: config.token,
|
|
131
|
+
quote: config.quote,
|
|
132
|
+
creator: config.creator,
|
|
133
|
+
creatorTaxBps: Number(config.creatorTaxBps),
|
|
134
|
+
excessToCreatorBps: Number(config.excessToCreatorBps),
|
|
135
|
+
rewards: config.rewards,
|
|
136
|
+
};
|
|
137
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import type { ReadClient } from './client.js';
|
|
2
|
+
/**
|
|
3
|
+
* @peddles/sdk — read and build against Peddles launches.
|
|
4
|
+
*
|
|
5
|
+
* ONE PACKAGE, SIX ENTRY POINTS. Take only what you import:
|
|
6
|
+
*
|
|
7
|
+
* @peddles/sdk this file — chain resolution, launch reads, pool terms
|
|
8
|
+
* @peddles/sdk/abis ABI fragments alone, no runtime
|
|
9
|
+
* @peddles/sdk/clog the Clog launch type
|
|
10
|
+
* @peddles/sdk/deployments the address book
|
|
11
|
+
* @peddles/sdk/launch launch calldata builders, salts, revert decoding
|
|
12
|
+
* @peddles/sdk/perps the perp launchpad (PeddlesPerpFactory)
|
|
13
|
+
*
|
|
14
|
+
* `viem` is a PEER dependency, not a bundled one: an integrator already has a
|
|
15
|
+
* client, and two copies of viem in a bundle is both bloat and a source of
|
|
16
|
+
* "instance is not a PublicClient" errors that are miserable to diagnose.
|
|
17
|
+
*
|
|
18
|
+
* WHAT IS DELIBERATELY ABSENT. This package exposes reads and calldata, not
|
|
19
|
+
* infrastructure. `ContractService`, `WalletService` and the log-source used by
|
|
20
|
+
* the indexer stay internal to the monorepo: publishing them would freeze this
|
|
21
|
+
* project's internals as public API and commit it to supporting them.
|
|
22
|
+
*/
|
|
23
|
+
import type { Address, Hex } from 'viem';
|
|
24
|
+
import { encodeAbiParameters, getContractAddress, keccak256 } from 'viem';
|
|
25
|
+
import { erc20Abi, factoryAbi } from './abis.js';
|
|
26
|
+
import { addressOf, deploymentFor, isKnownChain, UnknownChainError } from './deployments.js';
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
DEPLOYMENTS,
|
|
30
|
+
addressOf,
|
|
31
|
+
deploymentFor,
|
|
32
|
+
isKnownChain,
|
|
33
|
+
UnknownChainError,
|
|
34
|
+
UnknownContractError,
|
|
35
|
+
} from './deployments.js';
|
|
36
|
+
export type { KnownChainId, ContractName } from './deployments.js';
|
|
37
|
+
export type { ReadClient } from './client.js';
|
|
38
|
+
|
|
39
|
+
export {
|
|
40
|
+
getVariantAllocation,
|
|
41
|
+
getClogVault,
|
|
42
|
+
getClogState,
|
|
43
|
+
inflowShareBps,
|
|
44
|
+
isReleaseReady,
|
|
45
|
+
CLOG_MODEL_FLOW_MULTIPLE,
|
|
46
|
+
CLOG_TABLE_SLICE_BPS,
|
|
47
|
+
clogShareOfInflows,
|
|
48
|
+
clogShareOfInflowsFromBps,
|
|
49
|
+
clogInflowShareBps,
|
|
50
|
+
} from './clog.js';
|
|
51
|
+
export type { VariantAllocation, ClogState, ShareOfInflows, ClogModelInput } from './clog.js';
|
|
52
|
+
|
|
53
|
+
export {
|
|
54
|
+
factoryAbi,
|
|
55
|
+
clogVaultAbi,
|
|
56
|
+
clogVaultFactoryAbi,
|
|
57
|
+
erc20Abi,
|
|
58
|
+
tokenV20Abi,
|
|
59
|
+
feeHookAbi,
|
|
60
|
+
stockLaunchpadAbi,
|
|
61
|
+
holderRewardsAbi,
|
|
62
|
+
nftFactoryAbi,
|
|
63
|
+
nftCollectionAbi,
|
|
64
|
+
nftFeeDistributorFactoryAbi,
|
|
65
|
+
nftFeeDistributorAbi,
|
|
66
|
+
nftBondingGraduationOrchestratorAbi,
|
|
67
|
+
nftStockGraduationOrchestratorAbi,
|
|
68
|
+
liquidityExecutorAbi,
|
|
69
|
+
} from './abis.js';
|
|
70
|
+
|
|
71
|
+
export {
|
|
72
|
+
relayParamsHash,
|
|
73
|
+
stockGraduationOptInRequest,
|
|
74
|
+
graduateToStockWithTermsRequest,
|
|
75
|
+
graduateAndBuyRequest,
|
|
76
|
+
predictDistributor,
|
|
77
|
+
getHolderCommitment,
|
|
78
|
+
getDistributorState,
|
|
79
|
+
holderClaimable,
|
|
80
|
+
} from './artDex.js';
|
|
81
|
+
export type { RelayParams, StockTermsLaunch, HolderCommitment, DistributorState, HolderClaimable } from './artDex.js';
|
|
82
|
+
|
|
83
|
+
export { getPoolTerms, PoolTermsUnavailableError } from './feeTerms.js';
|
|
84
|
+
export type { PoolTerms, PoolTermsFailure } from './feeTerms.js';
|
|
85
|
+
|
|
86
|
+
export {
|
|
87
|
+
feeSplit,
|
|
88
|
+
PLATFORM_FEE_BPS,
|
|
89
|
+
CREATOR_FEE_BPS,
|
|
90
|
+
MIN_CREATOR_TAX_BPS,
|
|
91
|
+
MAX_CREATOR_TAX_BPS,
|
|
92
|
+
MAX_FEE_AMOUNT,
|
|
93
|
+
} from './feeSplit.js';
|
|
94
|
+
export type { FeeSplit } from './feeSplit.js';
|
|
95
|
+
|
|
96
|
+
/** A launched token, as the chain describes it. */
|
|
97
|
+
export interface TokenInfo {
|
|
98
|
+
readonly address: Address;
|
|
99
|
+
readonly name: string;
|
|
100
|
+
readonly symbol: string;
|
|
101
|
+
readonly decimals: number;
|
|
102
|
+
readonly totalSupply: bigint;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Identity and supply, read from the token itself.
|
|
107
|
+
*
|
|
108
|
+
* `decimals` travels with `totalSupply` on purpose. Every amount this SDK
|
|
109
|
+
* returns is a raw `bigint` in base units and MUST be formatted against its own
|
|
110
|
+
* decimals — never assume 18, and never route a token amount through a JS
|
|
111
|
+
* `number`, which loses precision above 2^53 and will silently mis-price a
|
|
112
|
+
* large-supply token.
|
|
113
|
+
*/
|
|
114
|
+
export async function getTokenInfo(client: ReadClient, token: Address): Promise<TokenInfo> {
|
|
115
|
+
const base = { address: token, abi: erc20Abi } as const;
|
|
116
|
+
const [name, symbol, decimals, totalSupply] = await client.multicall({
|
|
117
|
+
allowFailure: false,
|
|
118
|
+
contracts: [
|
|
119
|
+
{ ...base, functionName: 'name' },
|
|
120
|
+
{ ...base, functionName: 'symbol' },
|
|
121
|
+
{ ...base, functionName: 'decimals' },
|
|
122
|
+
{ ...base, functionName: 'totalSupply' },
|
|
123
|
+
],
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
address: token,
|
|
127
|
+
name: name as string,
|
|
128
|
+
symbol: symbol as string,
|
|
129
|
+
decimals: decimals as number,
|
|
130
|
+
totalSupply: totalSupply as bigint,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Whether a launch type can be launched on this chain right now.
|
|
136
|
+
*
|
|
137
|
+
* Ask before offering one. A variant with no code registered, or one an owner
|
|
138
|
+
* has retired, reports false — and a UI that offers it anyway sends a creator
|
|
139
|
+
* into a transaction that cannot succeed.
|
|
140
|
+
*/
|
|
141
|
+
export async function isVariantSupported(
|
|
142
|
+
client: ReadClient,
|
|
143
|
+
chainId: number,
|
|
144
|
+
variant: number,
|
|
145
|
+
): Promise<boolean> {
|
|
146
|
+
return (await client.readContract({
|
|
147
|
+
address: addressOf(chainId, 'PeddlesFactoryV20'),
|
|
148
|
+
abi: factoryAbi,
|
|
149
|
+
functionName: 'isVariantSupported',
|
|
150
|
+
args: [variant],
|
|
151
|
+
})) as boolean;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Everything `PeddlesFactoryV20` hashes into a token's CREATE2 address. */
|
|
155
|
+
export interface TokenAddressInput {
|
|
156
|
+
readonly factory: Address;
|
|
157
|
+
readonly variant: number;
|
|
158
|
+
/** The launcher that calls the factory (e.g. the orchestrator), not the end user. */
|
|
159
|
+
readonly sender: Address;
|
|
160
|
+
readonly salt: Hex;
|
|
161
|
+
/** keccak256 of the variant's creation code, as the factory reports it. */
|
|
162
|
+
readonly creationCodeHash: Hex;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* The CREATE2 address, computed locally — the exact formula the factory uses:
|
|
167
|
+
*
|
|
168
|
+
* boundSalt = keccak256(abi.encode(uint8 variant, address sender, bytes32 salt))
|
|
169
|
+
* address = keccak256(0xff ‖ factory ‖ boundSalt ‖ creationCodeHash)[12:]
|
|
170
|
+
*
|
|
171
|
+
* `creationCodeHash` is deliberately an input, not a constant shipped here. For
|
|
172
|
+
* the plain token it is the factory's immutable `TOKEN_CREATION_CODE_HASH`
|
|
173
|
+
* (keccak256 of `type(PeddlesTokenV20).creationCode` in the build that
|
|
174
|
+
* deployed it), and for a registered variant the hash stored at registration.
|
|
175
|
+
* Any recompile moves it, so the deployed factory is the only reliable source —
|
|
176
|
+
* read it with `predictTokenAddress`, or pass one you read yourself.
|
|
177
|
+
*/
|
|
178
|
+
export function computeTokenAddress(input: TokenAddressInput): Address {
|
|
179
|
+
const boundSalt = keccak256(
|
|
180
|
+
encodeAbiParameters(
|
|
181
|
+
[{ type: 'uint8' }, { type: 'address' }, { type: 'bytes32' }],
|
|
182
|
+
[input.variant, input.sender, input.salt],
|
|
183
|
+
),
|
|
184
|
+
);
|
|
185
|
+
return getContractAddress({
|
|
186
|
+
opcode: 'CREATE2',
|
|
187
|
+
from: input.factory,
|
|
188
|
+
salt: boundSalt,
|
|
189
|
+
bytecodeHash: input.creationCodeHash,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* The CREATE2 address a launch will deploy to, before it is launched.
|
|
195
|
+
*
|
|
196
|
+
* THE VARIANT IS PART OF THE ADDRESS: the factory binds
|
|
197
|
+
* `keccak256(abi.encode(variant, sender, salt))`, so the same salt under two
|
|
198
|
+
* launch types produces two different addresses. Passing the wrong variant here
|
|
199
|
+
* yields an address the factory will never deploy to.
|
|
200
|
+
*
|
|
201
|
+
* The creation-code hash is read from the deployed factory (`variantInfo`, which
|
|
202
|
+
* returns `TOKEN_CREATION_CODE_HASH` for the plain token), never assumed. An
|
|
203
|
+
* unknown variant throws, as `getPeddlesAddress` does; a retired one still
|
|
204
|
+
* resolves, so an address quoted before retirement stays recoverable.
|
|
205
|
+
*/
|
|
206
|
+
export async function predictTokenAddress(
|
|
207
|
+
client: ReadClient,
|
|
208
|
+
chainId: number,
|
|
209
|
+
variant: number,
|
|
210
|
+
sender: Address,
|
|
211
|
+
salt: Hex,
|
|
212
|
+
): Promise<Address> {
|
|
213
|
+
const factory = addressOf(chainId, 'PeddlesFactoryV20');
|
|
214
|
+
const [, creationCodeHash] = (await client.readContract({
|
|
215
|
+
address: factory,
|
|
216
|
+
abi: factoryAbi,
|
|
217
|
+
functionName: 'variantInfo',
|
|
218
|
+
args: [variant],
|
|
219
|
+
})) as readonly [Address, Hex, boolean];
|
|
220
|
+
if (/^0x0{64}$/i.test(creationCodeHash)) {
|
|
221
|
+
throw new Error(`PeddlesFactoryV20 on chain ${chainId} has no launch type ${variant}.`);
|
|
222
|
+
}
|
|
223
|
+
return computeTokenAddress({ factory, variant, sender, salt, creationCodeHash });
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** Chains this build ships an address book for. */
|
|
227
|
+
export function supportedChains(): number[] {
|
|
228
|
+
return Object.keys(DEPLOYMENTS_KEYS).map(Number);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Imported lazily-shaped to keep the public surface honest: `supportedChains`
|
|
232
|
+
// reads the same generated map every other entry point does.
|
|
233
|
+
import { DEPLOYMENTS as DEPLOYMENTS_KEYS } from './deployments.generated.js';
|