Skip to main content
Cette page est traduite automatiquement par IA. La version anglaise fait foi.Voir la version anglaise →
Une règle de courbe est la réponse d’une plateforme à la question « quels lancements suis-je prêt à héberger ? ». GlobalConfig définit le plancher du protocole — au moins 10M de supply, au moins 20% de la supply vendue sur la courbe, et ainsi de suite — et ces planchers sont volontairement larges pour que chaque type de plateforme s’y inscrive. Une règle de courbe est l’endroit où votre plateforme les affine à la forme que votre produit supporte réellement.Les règles vivent dans leur propre compte PlatformCurveRule, un par paire (plateforme, GlobalConfig). Elles ne peuvent que restreindre ce que la config autorise déjà ; une règle ne peut jamais élargir une limite du protocole.

Le modèle mental

Trois niveaux, de l’extérieur vers l’intérieur :
Les deux niveaux d’imbrication sont ce qui rend cela expressif :
  • Les contraintes à l’intérieur d’un groupe sont combinées avec ET. Toutes doivent être satisfaites.
  • Les groupes à l’intérieur d’une règle sont combinés avec OU. Un lancement est autorisé dès qu’il satisfait un seul groupe.
Donc un groupe est une forme autorisée, et la règle est le menu des formes que vous proposez. Une règle peut contenir jusqu’à 10 groupes, et un groupe jusqu’à 25 contraintes. Deux cas limites méritent d’être mémorisés : Une règle ne prend effet que si PlatformConfig.restrict_curve_param est 1. À 0, le programme ne lit pas du tout les règles, ce qui est aussi le commutateur que vous utilisez pour déployer une règle et pour la revenir en arrière.

Contraintes

Une contrainte est un triplet (champ, opérateur, valeur). Rien d’autre — pas d’expressions, pas d’imbrication.
Une plage est deux contraintes sur le même champ à l’intérieur d’un groupe : une Gte pour le plancher et une Lte pour le plafond. La même paire (champ, opérateur) ne peut pas apparaître deux fois dans un groupe, ce qui vous empêche d’écrire deux minimums contradictoires.

Champs

Les identifiants de champ sont permanents. Les nouveaux champs ne sont jamais qu’ajoutés, donc un identifiant ne change jamais de signification une fois qu’un compte de règle le contient.
Les champs dérivés sont ceux qui rendent les règles portables. Épingler Supply et TotalFundRaisingB à des nombres exacts fixe une forme de lancement ; contraindre FundRaisingRateB fixe la relation entre eux et permet à un créateur de choisir n’importe quelle supply qui la maintient.
Les champs de taux ne sont comparables que dans une GlobalConfig, car leurs dénominateurs dépendent du mint de quote de cette config et de ses décimales. Ce n’est pas une limitation en pratique : une règle est délimitée à une config par construction.

Les neuf modèles

Chaque modèle ci-dessous est une règle. Les contraintes sont écrites comme (champ, opérateur, valeur).

1. Un palier standard

La règle la plus simple, et le comportement exact que la liste blanche de paramètres de courbe retirée offrait : une forme, épinglée. Tout lancement qui s’écarte dans l’un des trois est rejeté avec CurveParamNotMatchPlatformRule.

2. Une bande au lieu d’un nombre

La raison pour laquelle les bandes existent : un créateur choisit une cible de levée de fonds avec laquelle vous êtes à l’aise, sans que vous énumériez chaque valeur. Un groupe, quatre contraintes, et le créateur a un corridor de 50–200 SOL. Sous l’ancienne liste blanche, cela nécessitait une entrée par valeur autorisée, et le plafond de dix entrées le rendait impossible.

3. Paliers côte à côte

Les groupes sont combinés avec OU, donc chaque palier est un groupe. L’ordre importe pour le calcul, pas pour la sémantique : l’évaluation s’arrête au premier groupe qui correspond, donc mettez votre palier le plus utilisé en premier.

4. Une bande de valorisation à la graduation

FundRaisingRateB est TotalFundRaisingB / Supply en millionièmes. La contraindre plafonne la richesse avec laquelle un token peut se diplômer indépendamment de la supply que le créateur a choisie. Avec une supply de 1e12 et un mint de quote à 9 décimales, 85e9 / 1e12 × 1e6 = 85_000 se situe à l’intérieur de cette bande. Un créateur qui double la supply doit à peu près doubler la cible pour rester dedans — c’est le point. Deux contraintes remplacent ce qui serait autrement un tableau de paires (supply, cible).

5. Un plancher de migration

MigrateRateA est la part de la supply qui atterrit réellement dans le pool CPMM à la graduation : Supply − TotalSellA − TotalLockedAmount, divisé par la supply. C’est la profondeur du pool diplômé, et c’est le seul bouton du protocole sans équivalent côté plateforme avant que les règles n’existent. Au moins 15% de la supply atteint le pool. Un créateur ne peut pas vendre 95% sur la courbe et laisser un carnet peu profond derrière.
Si les paramètres ne s’additionnent pas — un montant verrouillé plus grand que ce qui reste après la vente de courbe — la valeur dérivée ne peut pas être calculée et la contrainte échoue fermée, donc le lancement est rejeté plutôt que silencieusement autorisé.

6. Vesting que vous appliquez réellement

GlobalConfig.max_lock_rate plafonne le vesting par le haut. Une règle peut mettre un plancher dessous, et exiger une vraie falaise. Entre 5% et 20% de la supply verrouillée, avec au moins une falaise de 30 jours. Utile pour une plateforme dont le pitch est « pas de lancements avec déverrouillage instantané ».

7. Gating par type de token

BaseTokenProgram et TransferFeeEnabled sont indépendants, ce qui importe : un mint Token-2022 sans TransferFeeConfig rapporte TransferFeeEnabled = 0 tout comme un mint SPL Token le fait.

8. Un plafond de frais de transfert conditionnel

Il n’y a pas d’opérateur « si », et aucun n’est nécessaire — deux groupes expriment la condition. Une TransferFeeConfig à taux zéro ne glisse pas à travers le groupe 0 : l’extension est présente, donc TransferFeeEnabled est 1 et seul le groupe 1 peut l’accepter.

9. Une promo limitée dans le temps, programmée à l’avance

UnixTimestamp est l’heure de bloc du lancement, donc un groupe peut porter sa propre fenêtre de validité. Vous écrivez les deux groupes aujourd’hui et le basculement se fait tout seul. Aucune transaction n’est nécessaire à la limite. Le coût est deux emplacements de groupe au lieu d’un.

Restrictions de type de courbe

Quatre champs lisent TotalSellA : TotalSellA, SellRateA, MigrateAmountA, et MigrateRateA. Sur une config de produit constant, le créateur fournit ce nombre. Sur une config de prix fixe ou de prix linéaire, la courbe le dérive à la place, et la valeur que le programme compare est 0, ce qui rejetterait chaque lancement. Plutôt que de vous laisser écrire une règle qui bloque silencieusement votre propre config, le programme refuse ces quatre champs au moment de l’écriture sur une config non-produit-constant, avec CurveRuleFieldNotSupportedByCurve. Seules les configs de produit constant existent aujourd’hui, donc en pratique vous ne rencontrerez pas cette erreur.

Vérifier avant d’envoyer

Les deux directions de la vérification on-chain sont disponibles hors-chain, donc ni un créateur ni une plateforme n’a besoin d’apprendre une règle en regardant les transactions revenir.
Banneau de version.
  • SDK : @raydium-io/raydium-sdk-v2@0.2.42-alpha est la version à laquelle tous les autres démos de code sur ce site sont épinglés. Les deux assistants ci-dessous arrivent avec la version du SDK qui expédie le support des règles de courbe ; jusqu’à alors, portez-les depuis le platform_curve_rule.rs du programme ou appelez le programme et lisez le code d’erreur.
  • Cluster : testez d’abord sur Solana devnet — voir Testez d’abord sur devnet.
  • ID du programme : voir reference/program-addresses
Les deux assistants sont des fonctions pures. Ils ne touchent aucun RPC, donc ils sont sûrs à exécuter à chaque frappe dans un formulaire.

Avant un lancement : ces paramètres passeront-ils ?

checkLaunchAgainstCurveRule reflète exactement la vérification du programme au moment du lancement, y compris son comportement d’échec fermé. Exécutez-le dans votre formulaire de lancement et vous pouvez désactiver le bouton d’envoi avec une raison au lieu de laisser le créateur payer pour une transaction annulée.
Trois choses que l’assistant reproduit plutôt que d’approximer :
  • Un compte de règle manquant, et une règle sans groupe, passent tous les deux. Tout comme un groupe sans contrainte. Passez rule: undefined pour un compte inexistant ; ne le traitez pas comme un rejet.
  • Les valeurs non calculables échouent fermées. Une supply zéro n’a pas de taux, et un montant verrouillé plus grand que ce que la vente de courbe laisse n’a pas de montant de migration. actual revient undefined et la contrainte compte comme non satisfaite, exactement comme on-chain.
  • Toutes les contraintes défaillantes sont rapportées, pas seulement la première. Le programme court-circuite parce qu’il n’a besoin que d’un verdict ; l’assistant collecte tout pour que votre formulaire puisse lister chaque problème à la fois.
La seule chose qu’il ne peut pas savoir est l’heure de bloc à laquelle votre transaction atterrira réellement. Si une règle utilise UnixTimestamp près d’une limite, traitez un passage comme provisoire.

Avant d’écrire une règle : ce groupe est-il valide ?

checkCurveRuleGroupWritable reflète la validation au moment de l’écriture de UpdatePlatformCurveRule — les ids de contrainte, la règle de (champ, opérateur) dupliqué, les deux limites de comptage, et la restriction de type de courbe. Exécutez-le dans votre outil d’administration de plateforme avant de signer.
Passer cette vérification signifie que la transaction ne sera pas rejetée pour être mal formée. Cela ne dit rien sur le fait que la règle est ce que vous aviez l’intention — un groupe peut être parfaitement valide et toujours rejeter chaque lancement que votre UI peut produire. C’est ce que la vérification du côté du lancement ci-dessus est pour : après avoir écrit un groupe, exécutez chaque forme que votre produit offre à travers checkLaunchAgainstCurveRule et confirmez que chacune trouve toujours un groupe.

Testez d’abord sur devnet

Activer restrict_curve_param sur mainnet change ce que vos créateurs peuvent faire, immédiatement, pour chaque lancement. Répétez la séquence entière sur devnet avant de toucher mainnet :
  1. Créez une config de plateforme et une règle sur devnet, et écrivez les mêmes groupes que vous avez l’intention d’expédier.
  2. Exécutez chaque forme de lancement que votre UI peut produire à travers checkLaunchAgainstCurveRule, et confirmez que les verdicts sont ceux que vous attendez — à la fois les formes qui devraient passer et les formes qui devraient être rejetées.
  3. Activez restrict_curve_param, puis lancez réellement un token qui devrait passer et un qui devrait être rejeté. Le second devrait échouer avec CurveParamNotMatchPlatformRule (6025), pas avec NotEnoughRemainingAccounts (6018) — ce dernier signifie que votre builder n’ajoute pas le PDA de règle et la vérification n’est pas réellement exercée.
  4. Seulement alors répétez sur mainnet, dans le même ordre.
Pointez le SDK sur devnet avec cluster: "devnet" quand vous le chargez, et prenez l’ID du programme devnet depuis reference/program-addresses. L’étape 3 est celle sur laquelle il vaut la peine d’insister. L’assistant hors-chain et le programme on-chain sont deux implémentations des mêmes règles, et un lancement devnet est ce qui prouve qu’ils s’accordent pour votre règle — y compris que votre constructeur de lancement passe le compte du tout.

Exploiter une règle

Le gestionnaire délégué

Éditer les règles est un travail de routine ; une clé d’administrateur de plateforme est généralement un multisig. PlatformConfig.curve_rule_manager existe exactement pour cela : définissez-le une fois via UpdatePlatformConfig::CurveRuleManager, et ce portefeuille chaud peut alors créer, mettre à jour, supprimer et fermer les comptes de règle par lui-même. L’administrateur de la plateforme conserve le même pouvoir en parallèle, donc une clé de gestionnaire perdue est récupérable — faites-la tourner avec un autre appel d’administrateur. Portée d’une clé de gestionnaire compromise : elle peut assouplir ou supprimer vos règles de paramètres, et elle peut réclamer le loyer d’un compte de règle. Elle ne peut pas toucher aux portefeuilles de frais, au vesting, à la config CPMM, ne peut pas basculer restrict_curve_param, et ne peut pas casser une limite GlobalConfig. Traitez-la comme une clé de configuration, pas une clé de trésorerie.

Le loyer suit le contenu

Un compte de règle est créé sans groupe et redimensionné à chaque changement, donc vous payez pour les règles que vous avez réellement écrites. Supprimer un groupe rembourse la différence au signataire.

Ordre de déploiement

  1. Répétez la séquence entière sur devnet — voir Testez d’abord sur devnet.
  2. Créez le compte de règle et écrivez ses groupes. Rien ne change encore — avec restrict_curve_param toujours à 0, le programme ne les lit pas.
  3. Vérifiez la règle hors-chain avec checkLaunchAgainstCurveRule : pour chaque forme de lancement que votre UI peut produire, confirmez qu’un groupe l’accepte.
  4. Définissez restrict_curve_param à 1. À partir de ce moment, les lancements de vos créateurs sont vérifiés.
  5. Pour revenir en arrière, définissez-le à 0 à nouveau. Le compte de règle est laissé intact.
Votre constructeur de lancement doit ajouter le PDA de règle à remaining_accounts tandis que restrict_curve_param est 1. Le programme exige que le compte soit présent même quand il n’existe pas encore, donc qu’un créateur ne peut pas ignorer la vérification en l’omettant — un compte manquant est NotEnoughRemainingAccounts, pas un passage. La dérivation est [b"platform_curve_rule", platform_config, global_config].

Coût au moment du lancement

La vérification s’exécute à chaque lancement tandis qu’elle est activée, donc son coût de calcul est une taxe par lancement. Mesuré de bout en bout — dérivation PDA, l’analyse remaining_accounts, désérialisation, et évaluation : « Bon marché » est une lecture de champ direct comme Supply ; « dérivé » est un champ calculé comme MigrateRateA, qui coûte environ 223 CU par contrainte contre environ 48. Même une règle complètement chargée de contraintes dérivées reste à l’intérieur d’un tiers du budget par instruction par défaut de 200 000 CU, et une règle réaliste de trois groupes est sous 6 000. Les groupes sont évalués jusqu’à ce qu’un corresponde, donc mettre votre palier commun en premier est une économie gratuite.

Où aller ensuite

Sources :
  • raydium-launch/programs/launchpad/src/states/platform_curve_rule.rsPlatformCurveRule, CurveRuleGroup, ParamConstraint, les espaces d’id de champ et d’opérateur, et CurveRuleContext::value_of.
  • raydium-launch/programs/launchpad/src/utils/platform_curve_rule.rs — la vérification au moment du lancement.
  • raydium-launch/programs/launchpad/src/instructions/platform/create, update, remove, et close_platform_curve_rule.