@floorlaunchpad / carpenter-sdk
TypeScript SDK for Carpenter, Solana上的集中流动性AMM,带有uniswasp -v4风格的钩子:由
(mint0, mint1, fee, tickSpacing, hook),外部lp,以及批准的钩程序允许列表。纯TypeScript结束
@solana/kit 8.3.0;这里的签收和发送都要带钱包。
它涵盖了阅读、报价和交易:
- 这个
carpenter_ammIDL及其 Codama 客户端(carpenterAmm); - 帐户布局和解码器(
AmmConfig,Pool,TickArray,Position(钩子允许列表,引用列表), pda和读取帮助程序,它们用其插槽标记每个读取(stateView); - 交易生成器:掉期、余额掉期、增加/删除流动性和收取费用(
buildModifyLiquidity), 关闭位置,标记数组,初始化池,以及planCreatePool(创建一个池和它的第一个位置); - 通过地址查找表发送V0消息,在钱包看到任何内容之前检查1232字节的数据包;
sendSolanaTransactions:一次操作每笔交易一个钱包提示;- 通过捆绑的wasm引号标记数学、协议费用算法、事件解码器和纯池引号
(
wasm/carpenter_quote_bg.wasm(程序自己的引用包编译为wasm)。
治理和管理说明(配置设置、钩子批准、暂停、费用提取)在IDL和 生成的客户端,但这里没有构建器。
安装
npm install @floorlaunchpad/carpenter-sdk @solana/[email protected]
@solana/kit 是一个对等依赖。
读池
import { createSolanaRpc } from '@solana/kit';
import { deploymentFor, fromKitRpc, MAINNET_CHAIN_KEY, programIdsFromDeployment, stateView } from '@floorlaunchpad/carpenter-sdk';
const rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');
const src = fromKitRpc(rpc);
const ids = programIdsFromDeployment(deploymentFor(MAINNET_CHAIN_KEY)); // DEVNET_CHAIN_KEY for devnet
const { slot, value: pool } = await stateView.getPool(src, poolAddress, ids);
程序id来自 deployments.json (deploymentFor),从不使用应用中的常量; DEFAULT_PROGRAM_IDS 存在
用于测试和脚本。
引用和交换一个普通池
import { readFileSync } from 'node:fs';
import { createRequire } from 'node:module';
import { buildSwap, compileSendable, decodePool, readSwapQuoteAccounts, sendSolanaTransactions, swapTickArrays } from '@floorlaunchpad/carpenter-sdk';
import { initQuote } from '@floorlaunchpad/carpenter-sdk/quote/wasm';
import { getBase64EncodedWireTransaction } from '@solana/kit';
// Node: read the bundled wasm; a browser fetches `@floorlaunchpad/carpenter-sdk/carpenter_quote_bg.wasm` and passes the bytes.
const quoter = await initQuote(readFileSync(createRequire(import.meta.url).resolve('@floorlaunchpad/carpenter-sdk/carpenter_quote_bg.wasm')));
const { value: v } = await readSwapQuoteAccounts(src, poolAddress, { radius: 4, ids });
const q = quoter.quoteSwap({ ...v, zeroForOne: true, amountSpecified: -1_000_000n }); // exact in: negative
const { starts } = await swapTickArrays(poolAddress, q, ids);
const tx = await buildSwap({
trader, pool: poolAddress, poolState: decodePool(v.pool), zeroForOne: true,
amountSpecified: -1_000_000n, amountLimit: (q.amountOut * 99n) / 100n, tickArrayStarts: starts, ids,
});
const { value: bh } = await rpc.getLatestBlockhash().send();
const { transaction } = compileSendable('swap', { feePayer: trader, instructions: tx.instructions, lifetime: bh });
await sendSolanaTransactions(wallet, [getBase64EncodedWireTransaction(transaction)], rpc, { lastValidBlockHeight: bh.lastValidBlockHeight });
引号是程序自己的引用箱(carpenter-quote)编译为wasm并在此包中发布
(wasm/, WASM_SHA256); 引用返回的每个数字都是箱子的。它引用了没有交换钩子的池
钩子参与交换被拒绝(QuoteHookNotQuotable),并被其钩子的SDK (@floorlaunchpad/sdk-solana 用于
地板池)。暂停的池被拒绝 PoolPaused;守护暂停(Pool.guardianPausedUntil(Unix时间)不是
从引号判断,上面写着没有时钟:检查 poolPausedAt(pool, nowUnix) 针对集群的时钟。
simulateTransaction 留下签字前的最后支票。
滑动:一种精确的交换 amountLimit 是否有最低限额 0 被拒绝,除非
allowNoMinimum: true); 从上面的引用(1%)中得出。 buildModifyLiquidity 都拒绝搬迁
amount0Min 也不 amount1Min,存款上限为 u64::MAX,在相同的选择退出下。
计算: CU_LIMITS (swapPlain 对于普通的交换,90,000)是估计值,而不是交易的度量。
首先进行模拟,然后根据模拟结果设置限制 unitsConsumed 重要的时候。
原生SOL:一个wSOL端通过一个临时的wSOL帐户移动,该帐户由构建器创建、资助并关闭
同一交易(tempWsol(交易员钥匙下的随机种子)。交易者自己的wSOL账户(如果有的话)两者都不是
未使用,未关闭,因此已保存的wSOL保持原样。指定一个帐户(traderTokenIn / traderTokenOut,
ownerToken0 / ownerToken1)来代替使用它(它为输入充值,永远不会关闭),或者传递 wrapSol: false
使用没有包装的wSOL ATA。
一个被钩住的池子需要它的钩子的账户(hookSlice)及计算(cuUnits):钩子自己的SDK提供了它们
(@floorlaunchpad/sdk-solana 游泳池)。 readSwapQuoteAccounts 拒绝一个钩形泳池的名字。
FLOOR池使用四帐户FLOOR钩子切片,这个发布列表中包含主网和开发网部署
(@floorlaunchpad/sdk-solana 的同一版本构建它)。固定的钩子HOOK_FLAGS.SETTLES)被称为下
["hook_settle", pool] Signer,这是它的切片。findHookSettle); 通过 writableMints 当它铸造成一个金库
在交换期间。
创建一个池并增加流动性
import { fromKitRpc, planCreatePool, sendSolanaTransactions, NO_HOOK } from '@floorlaunchpad/carpenter-sdk';
const plan = await planCreatePool({
payer, mintA, mintB, tokenProgramA, tokenProgramB, decimalsA: 6, decimalsB: 9,
fee: 3_000, tickSpacing: 60, hook: NO_HOOK, sqrtPriceX64,
position: { tickLower: -600, tickUpper: 600, liquidity, amountAMax, amountBMax },
lifetime: bh, ids,
});
await sendSolanaTransactions(wallet, plan.transactions, rpc, {
steps: plan.stepNames,
lastValidBlockHeight: bh.lastValidBlockHeight,
beforeSend: plan.beforeSend(fromKitRpc(rpc)), // stop if the new pool's price moved before the deposit
});
plan.transactions 是 [initializePool, addLiquidity]:两个交易(它们加在一起不适合一个包),签名
一次提示并按顺序发送。一个钱包签名两个,所以位置的所有者是付款人(owner / initializer
除了 payer 是拒绝)。
在这两个步骤之间,池存在并且是空的,任何人都可以免费移动空池的价格。
plan.beforeSend(src, { maxPriceMoveBps }) 在发送存款之前读取池并停止运行(原因)
guard, PoolPriceMovedError),当其价格从初始化价格移动超过 maxPriceMoveBps
(默认0:精确)。价格还可以在阅读和押金之间浮动,所以 amountAMax / amountBMax 是
存款是唯一的硬性限制:将它们设置为初始价格加上您接受的滑点所需要的头寸,
从来没有更大的。
每次操作的钱包提示
sendSolanaTransactions(wallet, steps, rpc, opts?) 将一个操作的每个交易签名在一起,然后发送它们
订单,等待每一个确认前的下一个;第一个失败停止运行并命名步骤
(SolanaSendError: index, step, reason(落地的签名)。
| 行动 | 交易 | 提示(批量签名钱包) |
|---|---|---|
| 交换 | 1 | 1 |
| 增加/移除流动性,收取费用 | 1 | 1 |
用它的第一个位置创建一个池(planCreatePool) | 2 | 1 |
钱包是如何被问到的,按照偏好顺序:钱包标准 solana:signTransaction 每一个特性
一次调用的事务(wallet.standard),遗产 signAllTransactions,否则 signTransaction 每一步一次。这个
结果表明哪个(path),以及需要多少提示。
警告:
- 逐个签署交易需要一个提示符;没有批处理调用的钱包就会退回到这种情况。
- 在发送任何东西之前,钱包返回的每笔交易都要进行检查:消息字节必须等于计划的字节
消息,以及每个所需的签名(预期签名者包括:
opts.signer,否则就是钱包标准 帐户,否则付款人)必须核实。其他任何事情都能合理地阻止逃亡sign. - 一个步骤不会发送两次。发送错误而没有预拒绝(超时,断开连接)可能有
交付事务,因此步骤继续等待其签名。一个步骤只有在其过期时才算作过期
签名不存在,区块高度已经超过了区块散列的高度
lastValidBlockHeight(传递它,或者发送者要求 节点),又通过了两个块,asearchTransactionHistory重新检查仍然一无所获,节点调用 blockhash无效。“未找到区块散列”本身并不过期。 - 失败后,从链状态重新计划:登陆的步骤改变了它。一个
timeout可能还会着陆:检查一下signature在再次行动之前。 - 这些交易共享一个最近的区块散列,有效期约为60-90秒。通过
opts.replan重建步骤 到期时还没有登陆的(再提示一次);没有它,奔跑会因理性而停止expired. - 在签名之前模拟每笔交易的钱包无法模拟依赖于之前帐户的步骤
步骤创建池
planCreatePool(查找表):它将该步骤显示为失败,并且它在 之前的步骤。
开发
idl/carpenter_amm.json 该程序的锚IDL与每个 docs 字段已删除,并出现一些错误 msg 字符串
缩短(错误名称和代码不变):发货的IDL,和
从它生成的客户端构建,不携带文档注释。 IDL_SHA256 是该文件的散列。
npm run generate # idl/ + deployments.json → src/generated/ (Codama client)
npx tsc -p tsconfig.json --noEmit
npx vitest run
npm run build # dist/
npm run sync-wasm # dist/wasm (from `bash wasm/build.sh` at the repository root) → wasm/ and WASM_SHA256
这个峡谷是由 wasm/carpenter-quote-wasm 由 wasm/build.sh 在存储库根目录:rustc 1.98.1
(wasm32-unknown-unknown), wasm-bindgen-cli 0.2.128, --target web,在固定路径上的源副本上
路径被重新映射,crate的文档注释被删除,所以相同的源给出相同的字节。 test/wasm.test.ts
回放 fixtures/quote-vectors/plain.json 和 parity-plain.json 通过运输的水路。
这些程序可以通过主网-测试版上的时间锁升级(10分钟的延迟只能由部署者提出),也可以通过部署者直接在devnet上升级,并且可以由部署者暂停;这个包没有声明它们是经过审计的或不可变的。
许可证
apache - 2.0。