SDK

API 客户端

Last updated October 12, 2026

Genesis API 客户端提供了用于创建和注册代币发行的高级函数。它通过基于 Umi 构建的简洁接口,处理交易构建、签名和链上注册。

我们建议使用 SDK 以编程方式创建发行,因为 metaplex.com 尚未支持 Genesis 程序的全部功能。通过 API 创建的主网发行在注册后将显示在 metaplex.com 上。

安装

npm install @metaplex-foundation/genesis @metaplex-foundation/umi \
@metaplex-foundation/umi-bundle-defaults

设置

import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
import { genesis } from '@metaplex-foundation/genesis';
import { keypairIdentity } from '@metaplex-foundation/umi';
const umi = createUmi('https://api.mainnet-beta.solana.com')
.use(genesis());
// For server-side or scripts, load a keypair
umi.use(keypairIdentity(myKeypair));

函数概览

函数用途
createAndRegisterLaunch一次调用完成代币发行——创建、签名、发送并注册
createLaunch为自定义签名流程构建未签名的发行交易
registerLaunch在创建交易于链上确认后注册发行
claimCreatorRewards为某个钱包认领所有存储桶中累积的创作者奖励

三种集成模式

SDK 提供三种创建发行的模式,从全自动到完全手动。

简易模式 — createAndRegisterLaunch

最简单的方式。一次函数调用即可处理所有事项:创建链上账户、通过 Umi 签名并发送交易,以及注册发行。

createAndRegisterLaunch.ts
1import {
2 createAndRegisterLaunch,
3 CreateLaunchInput,
4 genesis,
5} from '@metaplex-foundation/genesis'
6import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
7import { keypairIdentity } from '@metaplex-foundation/umi'
8
9const umi = createUmi('https://api.mainnet-beta.solana.com')
10 .use(genesis())
11
12// Use keypairIdentity to set a wallet when running server-side:
13// umi.use(keypairIdentity(myKeypair))
14
15const input: CreateLaunchInput = {
16 wallet: umi.identity.publicKey,
17 token: {
18 name: 'My Token',
19 symbol: 'MTK',
20 image: 'https://gateway.irys.xyz/...',
21 },
22 launchType: 'launchpool',
23 launch: {
24 launchpool: {
25 tokenAllocation: 500_000_000,
26 depositStartTime: new Date(Date.now() + 48 * 60 * 60 * 1000),
27 raiseGoal: 250,
28 raydiumLiquidityBps: 5000,
29 fundsRecipient: umi.identity.publicKey,
30 },
31 },
32}
33
34const result = await createAndRegisterLaunch(umi, {}, input)
35console.log(`Launch live at: ${result.launch.link}`)

返回 CreateAndRegisterLaunchResult:

字段类型描述
signaturesUint8Array[]交易签名
mintAddressstring创建的代币铸造地址
genesisAccountstringGenesis 账户 PDA 地址
launch.idstring发行 ID
launch.linkstring发行页面 URL
token.idstring代币 ID
token.mintAddressstring代币铸造地址

中级模式 — 自定义交易发送器

使用 createAndRegisterLaunch 配合自定义 txSender 回调,适用于多签钱包或自定义重试逻辑等场景。

customTxSender.ts
1import {
2 createAndRegisterLaunch,
3 SignAndSendOptions,
4} from '@metaplex-foundation/genesis'
5
6// Assumes umi and input from the Easy Mode example.
7
8const options: SignAndSendOptions = {
9 txSender: async (transactions) => {
10 // Replace myCustomSign / myCustomSend with your own
11 // signing and sending implementation.
12 const signatures: Uint8Array[] = []
13 for (const tx of transactions) {
14 const signed = await myCustomSign(tx) // your signer
15 const sig = await myCustomSend(signed) // your sender
16 signatures.push(sig)
17 }
18 return signatures
19 },
20}
21
22const result = await createAndRegisterLaunch(umi, {}, input, options)
23console.log(`Launch live at: ${result.launch.link}`)

txSender 回调接收未签名交易数组,必须返回签名数组。回调完成后 SDK 会处理注册。

完全控制 — createLaunch + registerLaunch

完全控制交易生命周期。调用 createLaunch 获取未签名交易,自行处理签名和发送,然后调用 registerLaunch。

fullControl.ts
1import {
2 createLaunch,
3 registerLaunch,
4} from '@metaplex-foundation/genesis'
5
6// Assumes umi and input from the Easy Mode example.
7
8// Step 1: Get unsigned transactions from the API
9const createResult = await createLaunch(umi, {}, input)
10
11// Step 2: Sign and send each transaction
12for (const tx of createResult.transactions) {
13 const signedTx = await umi.identity.signTransaction(tx)
14 const signature = await umi.rpc.sendTransaction(signedTx, {
15 commitment: 'confirmed',
16 preflightCommitment: 'confirmed',
17 })
18 await umi.rpc.confirmTransaction(signature, {
19 commitment: 'confirmed',
20 strategy: {
21 type: 'blockhash',
22 ...createResult.blockhash,
23 },
24 })
25}
26
27// Step 3: Register the launch
28const registerResult = await registerLaunch(umi, {}, {
29 genesisAccount: createResult.genesisAccount,
30 createLaunchInput: input,
31})
32console.log(`Launch live at: ${registerResult.launch.link}`)

createLaunch 返回 CreateLaunchResponse:

字段类型描述
transactionsTransaction[]需要签名并发送的未签名 Umi 交易
blockhashBlockhashWithExpiryBlockHeight用于确认交易的区块哈希
mintAddressstring创建的代币铸造地址
genesisAccountstringGenesis 账户 PDA 地址

registerLaunch 返回 RegisterLaunchResponse:

字段类型描述
existingboolean?如果发行已注册则为 true
launch.idstring发行 ID
launch.linkstring发行页面 URL
token.idstring代币 ID
token.mintAddressstring代币铸造地址

在调用 registerLaunch 之前,交易必须在链上确认。注册端点会验证 Genesis 账户是否存在以及是否与预期配置匹配。


认领创作者奖励

claimCreatorRewards 调用 POST /v1/creator-rewards/claim,并返回每个符合条件的联合曲线和 Raydium 存储桶的 base64 编码认领交易,这些交易已反序列化为 Umi Transaction。请使用 Umi identity 对每个交易签名并发送。有关完整的认领流程——包括创作者费配置、累积检查以及更底层的逐桶指令——请参阅 Genesis 联合曲线上的创作者费。

claimCreatorRewards.ts
1import { claimCreatorRewards } from '@metaplex-foundation/genesis'
2import { base58 } from '@metaplex-foundation/umi/serializers'
3import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
4import { keypairIdentity } from '@metaplex-foundation/umi'
5
6const umi = createUmi('https://api.mainnet-beta.solana.com')
7const keypair = umi.eddsa.createKeypairFromSecretKey(mySecretKeyBytes)
8umi.use(keypairIdentity(keypair))
9
10const result = await claimCreatorRewards(umi, {}, {
11 wallet: umi.identity.publicKey,
12 network: 'solana-mainnet',
13 // payer is optional — defaults to `wallet` on the server.
14 // Set it to have a different wallet cover rent and transaction fees.
15 // payer: umi.identity.publicKey,
16})
17
18for (const tx of result.transactions) {
19 const signed = await umi.identity.signTransaction(tx)
20 const signature = await umi.rpc.sendTransaction(signed, {
21 preflightCommitment: 'confirmed',
22 })
23 await umi.rpc.confirmTransaction(signature, {
24 strategy: { type: 'blockhash', ...result.blockhash },
25 commitment: 'confirmed',
26 })
27 console.log('Claimed:', base58.deserialize(signature)[0])
28}
29
30// Claimed: 5uGGYEMmjP2HpyFCvLPNpVDSQEBtUE3LR6ZQFqhJxQSh5FbKacSyN8nQmAJowuFs6BTCdwzoFyyJz8Y2hQx8kPxo
31// Claimed: 3TAroVovEap1ZEAJYq3WiDZoMK3GU3soCdrhvZJNg6b9EANqvWrVcDGNffm7mD8wvtpR7ynWQBcbrmz8AK6nrhfy

ClaimCreatorRewardsInput

字段类型必填描述
walletPublicKey | string是要认领的创作者费钱包。
networkSvmNetwork否'solana-mainnet'(默认)或 'solana-devnet'。必须与 API 基础 URL 一致——https://api.metaplex.com
payerPublicKey | string否支付返回交易的费用和租金的钱包。默认为 wallet。当创作者费钱包没有 SOL 时使用(例如 agent PDA)。payer 必须对返回的交易签名;认领到的 SOL 仍由创作者费钱包接收。

ClaimCreatorRewardsResponse

字段类型描述
transactionsTransaction[]已反序列化的 Umi 交易,每个被认领的存储桶一个。请使用 payer identity 对每个交易签名并发送。
blockhashBlockhashWithExpiryBlockHeight构建交易时使用的区块哈希。请用它来确认交易——不要替换为新获取的区块哈希。

无奖励是类型化错误,而不是空数组

当钱包没有可认领的内容时,API 返回 HTTP 400 和 {"error":{"message":"No rewards available to claim"}},SDK 会抛出 GenesisApiError。调用方必须捕获该错误并根据 err.message(或 err.statusCode === 400)进行分支——不能依赖 result.transactions.length === 0。请参阅处理无奖励情况。

errorHandling.ts
1import {
2 claimCreatorRewards,
3 isGenesisApiError,
4 isGenesisApiNetworkError,
5} from '@metaplex-foundation/genesis'
6
7// Assumes umi is configured with a keypair identity.
8
9try {
10 const result = await claimCreatorRewards(umi, {}, {
11 wallet: umi.identity.publicKey,
12 network: 'solana-mainnet',
13 })
14 console.log(`Claimable transactions: ${result.transactions.length}`)
15} catch (err) {
16 if (isGenesisApiError(err)) {
17 // The API returns HTTP 400 with
18 // { "error": { "message": "No rewards available to claim" } }
19 // when the wallet has no unclaimed creator rewards. Match on the message
20 // (or statusCode === 400) to handle this as a success case rather than a
21 // failure.
22 if (err.message === 'No rewards available to claim') {
23 console.log('Nothing to claim right now.')
24 } else {
25 console.error('API error:', err.statusCode, err.message)
26 }
27 } else if (isGenesisApiNetworkError(err)) {
28 console.error('Network error:', err.cause.message)
29 } else {
30 throw err
31 }
32}
33
34// Nothing to claim right now.

配置

CreateLaunchInput

字段类型必填描述
walletPublicKey | string是创建者钱包(签署交易)
tokenTokenMetadata是代币元数据
networkSvmNetwork否'solana-mainnet'(默认)或 'solana-devnet'
quoteMintQuoteMintInput否'SOL'(默认)或 'USDC'
launchTypeCreateLaunchType是'launchpool' — 底层发行机制
launchLaunchpoolLaunchInput是发行配置

TokenMetadata

字段类型必填描述
namestring是代币名称,1–32 个字符
symbolstring是代币符号,1–10 个字符
imagestring是图片 URL(有效的 HTTPS URL)
descriptionstring否最多 250 个字符
externalLinksExternalLinks否网站、Twitter、Telegram 链接
字段类型描述
websitestring?网站 URL
twitterstring?Twitter/X 用户名(@mytoken)或完整 URL
telegramstring?Telegram 用户名或完整 URL

LaunchpoolConfig

字段类型描述
tokenAllocationnumber销售代币数量(总供应量 10 亿中的一部分)
depositStartTimeDate | string存款期开始时间(持续 48 小时)
raiseGoalnumber最低募集报价代币数量,以整数单位表示(例如 250 SOL)。最低 250 SOL 或 5,000 USDC
raydiumLiquidityBpsnumber用于 Raydium LP 的募集资金百分比,以基点表示(2000–10000)
fundsRecipientPublicKey | string接收解锁部分募集资金的地址
softCapnumber?募集报价代币的可选上限,以整数单位表示。超出上限的存款将按比例退还。必须大于 0;省略则表示无上限

ClaimScheduleAllocation(归属锁仓)

ClaimScheduleAllocation 为部分代币供应量定义 Genesis 原生的归属(vesting)锁仓,通过 launch.claimScheduleAllocations 传入。每个分配都会成为链上的 ClaimScheduleBucketV2:代币沿带有可选悬崖期的线性曲线累积,SDK 会将下面易读的计划转换为链上的秒数和基点值。

字段类型必填描述
namestring是锁仓名称,最多 64 个字符(例如 "Team"、"Advisors")
recipientPublicKey | string是可领取已归属代币的钱包
tokenAmountnumber是归属计划中的代币总量
vestingStartTimeDate | string是线性归属曲线开始累积的时间
vestingDuration{ value: number, unit: TimeUnit }是线性归属周期的长度
unlockScheduleTimeUnit是代币解锁的频率
cliff{ duration, unlockAmount? }否可选悬崖期。duration 从 vestingStartTime 起算;unlockAmount 为代币数量,或表示按比例份额的 'linear'(默认值)
cancelableboolean否发行创建者可以取消该锁仓。默认 false
cancelableByRecipientboolean否接收方可以取消该锁仓。默认 false
transferableboolean否发行创建者可以转让该锁仓。默认 false
transferableByRecipientboolean否接收方可以转让该锁仓。默认 false
pausableboolean否Genesis 权限方可以暂停和恢复归属。默认 false

对于 Launch Pool,即使 vestingStartTime 更早且代币已经累积,在发行完成毕业(graduate)之前也无法领取已归属的代币。解析后的悬崖期份额必须大于零。

lockedAllocations 已弃用

launch.lockedAllocations(原 Streamflow 锁仓)已弃用。SDK 仍然接受它,并将每个条目映射为领取计划(claim schedule),因此现有集成仍可正常编译。新的集成应使用 claimScheduleAllocations。迁移时,请将 cancelableBySender 重命名为 cancelable,将 transferableBySender 重命名为 transferable。

TimeUnit 值: 'SECOND'、'MINUTE'、'HOUR'、'DAY'、'WEEK'、'TWO_WEEKS'、'MONTH'、'QUARTER'、'YEAR'

归属锁仓示例:

claimScheduleAllocations.ts
1import {
2 createAndRegisterLaunch,
3 CreateLaunchInput,
4 genesis,
5} from '@metaplex-foundation/genesis'
6import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
7import { keypairIdentity } from '@metaplex-foundation/umi'
8
9const umi = createUmi('https://api.mainnet-beta.solana.com')
10 .use(genesis())
11
12// Use keypairIdentity to set a wallet when running server-side:
13// umi.use(keypairIdentity(myKeypair))
14
15const input: CreateLaunchInput = {
16 wallet: umi.identity.publicKey,
17 token: {
18 name: 'My Token',
19 symbol: 'MTK',
20 image: 'https://gateway.irys.xyz/...',
21 description: 'A project token with vesting locks.',
22 externalLinks: {
23 website: 'https://example.com',
24 twitter: '@mytoken',
25 },
26 },
27 launchType: 'launchpool',
28 launch: {
29 launchpool: {
30 tokenAllocation: 500_000_000,
31 depositStartTime: new Date('2026-04-01T00:00:00Z'),
32 raiseGoal: 250,
33 raydiumLiquidityBps: 5000,
34 fundsRecipient: 'FundsRecipientWallet...',
35 },
36 claimScheduleAllocations: [
37 {
38 name: 'Team',
39 recipient: 'TeamWallet...',
40 tokenAmount: 100_000_000,
41 vestingStartTime: new Date('2026-04-05T00:00:00Z'),
42 vestingDuration: { value: 1, unit: 'YEAR' },
43 unlockSchedule: 'MONTH',
44 cliff: {
45 duration: { value: 3, unit: 'MONTH' },
46 unlockAmount: 10_000_000,
47 },
48 cancelable: false,
49 pausable: false,
50 },
51 ],
52 },
53}
54
55const result = await createAndRegisterLaunch(umi, {}, input)
56console.log(`Launch live at: ${result.launch.link}`)

SignAndSendOptions

createAndRegisterLaunch 的选项(扩展自 RpcSendTransactionOptions):

字段类型默认值描述
txSender(txs: Transaction[]) => Promise<Uint8Array[]>—自定义交易发送器回调
commitmentstring'confirmed'确认的承诺级别
preflightCommitmentstring'confirmed'预检承诺级别
skipPreflightbooleanfalse跳过预检检查

错误处理

SDK 提供三种错误类型及对应的类型守卫函数。

GenesisApiError

当 API 返回非成功响应时抛出。

import { isGenesisApiError } from '@metaplex-foundation/genesis';
try {
await createLaunch(umi, {}, input);
} catch (err) {
if (isGenesisApiError(err)) {
console.error('API error:', err.statusCode, err.responseBody);
}
}
属性类型描述
statusCodenumberHTTP 状态码
responseBodyunknownAPI 返回的完整响应体

GenesisApiNetworkError

当 fetch 调用失败时抛出(网络问题、DNS 故障等)。

import { isGenesisApiNetworkError } from '@metaplex-foundation/genesis';
if (isGenesisApiNetworkError(err)) {
console.error('Network error:', err.cause.message);
}
属性类型描述
causeError底层 fetch 错误

GenesisValidationError

当输入验证在发起 API 调用之前失败时抛出。

import { isGenesisValidationError } from '@metaplex-foundation/genesis';
if (isGenesisValidationError(err)) {
console.error(`Validation failed on field "${err.field}":`, err.message);
}
属性类型描述
fieldstring验证失败的输入字段

综合错误处理

errorHandling.ts
1import {
2 createAndRegisterLaunch,
3 isGenesisApiError,
4 isGenesisApiNetworkError,
5 isGenesisValidationError,
6} from '@metaplex-foundation/genesis'
7
8// Assumes umi and input from the Easy Mode example.
9
10try {
11 const result = await createAndRegisterLaunch(umi, {}, input)
12 console.log(`Launch live at: ${result.launch.link}`)
13} catch (err) {
14 if (isGenesisValidationError(err)) {
15 console.error(`Invalid input "${err.field}":`, err.message)
16 } else if (isGenesisApiError(err)) {
17 console.error('API error:', err.statusCode, err.responseBody)
18 } else if (isGenesisApiNetworkError(err)) {
19 console.error('Network error:', err.cause.message)
20 } else {
21 throw err
22 }
23}

验证规则

SDK 在将输入发送到 API 之前会进行验证:

规则约束条件
代币名称1–32 个字符
代币符号1–10 个字符
代币图片有效的 HTTPS URL
代币描述最多 250 个字符
代币分配量大于 0
募集目标大于 0
Raydium 流动性 BPS2000–10000(20%–100%)
总供应量固定为 10 亿枚代币
锁仓分配名称最多 64 个字符

总供应量始终为 10 亿枚代币。SDK 会自动计算创建者分配额,即总供应量减去 Launch Pool、Raydium LP 和所有锁仓分配后的剩余部分。


辅助函数

signAndSendLaunchTransactions

如果您希望将默认的签名并发送行为作为独立函数使用(适用于重试或部分流程):

import {
createLaunch,
signAndSendLaunchTransactions,
} from '@metaplex-foundation/genesis';
const createResult = await createLaunch(umi, {}, input);
const signatures = await signAndSendLaunchTransactions(umi, createResult, {
commitment: 'confirmed',
});

交易按顺序签名并发送——每笔交易在下一笔发送之前完成确认。

buildCreateLaunchPayload

验证输入并构建原始 API 载荷。为高级用例导出:

import { buildCreateLaunchPayload } from '@metaplex-foundation/genesis';
const payload = buildCreateLaunchPayload(input);
// Use payload with your own HTTP client