Ripplesのローンチ一覧を開いています
Ripplesのローンチ一覧を開いています
すべてのアドレス
1か所にまとめて掲載
ローンチが固定するものに、Ripplesがデプロイした、または保有するアドレスをすべて載せています。Ripplesのものとして送られてきたアドレスがこの一覧にない場合、それはRipplesから送られたものではありません。
すべての鍵
できることの範囲を明記
同じ一覧に、両方の目標額、手数料、クリエイターの収益、プールのロック、そしてRipplesの各鍵にできることとできないことを記載しています。
フィードとインデクサー
自動生成される1つのドキュメント
連携ページに、すべてのアドレス、イベントトピック、呼び出しと、DEX ScreenerとGeckoTerminalに応答するマーケットフィードを載せています。
はじめに
Ripplesが作成するのは、誕生した瞬間から誰でも取引できるマーケット付きのトークン、購入者が直接買えるNFTコレクション、またはその両方です。Ripplesのすべては、次の4つのステップに収まります。それ以降は、すべてリファレンスです。
供給量、目標額、手数料、クリエイターの収益、確保されたトークンのアンロックは、ローンチの作成時に書き込まれます。ローンチが固定するものには、それらの数値、Ripplesがデプロイしたすべてのアドレス、Ripplesが持つすべての鍵と、それぞれの鍵にできることとできないことを記載しています。同じ流れは、Ripplesについてのページでより詳しく説明しています。
保護の仕組み
ローンチや取引で起こりうるケースと、それぞれで何が起きるかをまとめています。どの回答にも、詳細を記したセクションへのリンクがあります。
すべての取引には、0.5%、1%、2%から選ぶ許容スリッページがあります。取引が確定する前に価格がそれ以上動いた場合、取引は拒否され、支払おうとした金額はウォレットに残ります。
NFT購入者向けに確保されたトークンは、マーケットが目標額に達して流動性が稼働するまでロックされたままです。その後、各購入者の分はローンチのクリフ期間中ロックされ、そのあと段階的にアンロックされます。各ローンチは作成時に2つのルールのどちらかを設定し、そのルールは後から変更できません。受け取り権がNFTをミントしたウォレットに残るか、NFTとともに移り、NFTの保有者がまだ受け取られていないトークンを受け取れるかのどちらかです。どちらのルールが適用されるかは、コレクションのページに表示されます。クリエイターには、マーケットが目標額に達するのを待たずにNFTの売上が支払われます。マーケットが目標額に達したときに起きること
決済にRipplesの鍵は必要ありません。どのウォレットからでも呼び出せ、Ripples自身のボットも、ほかのウォレットと同じ権限で呼び出します。ネットワーク手数料は送信した人が支払います。
Wakeのコントラクトは支払いの時点でNFTの保有者を読み取るため、次の支払いは、売却前に積み上がった収益も含めて新しい保有者に届きます。売り手がその収益を受け取るには、売却前に回収しておく必要があります。Wakeの支払先は誰にも変えられません。支払いは誰でも実行でき、支払われる先は常に保有者だけです。ローンチの元になるエージェントのNFTが別のチェーンにある場合、クリエイター収益はローンチしたウォレットに残り、署名の前に確認画面でそのことが示されます。Wakeを残す
エージェントにできるのは、自らのトークンの購入、設定された上限内での売却、自らのコレクションからのミント、指定されたアドレスへの支払い、実行ログへの書き込みです。それ以外は何もできません。コントラクトにはオーナーも、設定を変更する関数も、引き出しの機能もないため、上限はローンチが続く限り有効で、ローンチした本人も含め、誰も引き上げられません。NFTを移転できないため、ミントした作品はエージェントの手元に残ります。エージェントのローンチ
ポストへの返信に、画像がない、ティッカーがないか長すぎるなど、直すべき箇所が示されます。各アカウントがローンチできるのは1日10件までで、次にローンチできる時刻も返信に示されます。ポストから作成したローンチは、クリエイターが1回の署名で受け取るまで保留されます。Xのポストからローンチ
資産によっては、発行体が送金を一時停止したり、アドレスをブロックしたりできます。その保留が有効な間、その資産建てのマーケットでは購入、売却、ミント、プールへの移行がすべて止まり、発行体が保留を解除すると再開します。この権限は発行体だけのものです。ローンチの費用
クリエイター向け
必要なのは、ブラウザウォレット、名前、ティッカー、そしてローンチ費用をまかなえるだけのそのチェーン自体の資産です。トークンを作成するローンチでは、画像と説明も必要です。Robinhoodでは、これらをトークン自体が保持します。画像のリンク、説明、各種リンクは作成時にトークンへ書き込まれ、その後は誰も変更できません。RobinhoodでのNFTのみのローンチでは、どちらも必要ありません。コレクション自体がアートワークを持つからです。それ以外はすべてローンチフォームで決めます。フォームは、ウォレットに承認を求める前に、トランザクションで支払う金額をすべて合計し、その内訳を示します。
クリエイター手数料(任意)
クリエイターは、今後のクリエイター手数料収益の0.01%〜100%を、トークンの保有者に振り向けられます。コレクション付きのローンチなら、作品の保有者に振り向けることもできます。この決定は永続的です。一度振り向けた手数料は、誰も元に戻したり別の宛先に変えたりできないため、署名する前に最終決定として扱ってください。Robinhoodでは、現在クリエイター手数料を受け取っているウォレットが、ローンチの分配コントラクトを準備するときに分配率と分配先を設定します。残りの手数料は引き続きクリエイター、またはクリエイターが指定した受取先に支払われます。そこで始めたコレクション付きのローンチは、作品の保有者に手数料を分配する設定で始まります。確認画面に分配率が表示され、その行の横のスイッチでオフにできます。Foam付きのローンチでは、対象となる購入でミントされた作品も、ほかの作品と同じように数えられます。分配コントラクトを準備するだけでは何も変わらず、手数料を動かすのは2つ目のトランザクションです。
これは、マーケットを株式トークン建てにすることや、StockLinkを記録することとは別の仕組みです。NFTの売上と、振り向ける前に得たクリエイター残高も、これとは別に扱われます。報酬は実際に得た資産そのもので、ペアの資産、ローンチしたトークン、またはその両方です。対象となる残高は、各期間中に保有していた長さで重み付けされます。固定の利回りはなく、分配できる手数料がない期間には何も支払われません。
Ripplesは公開されている保有履歴を読み取り、期間ごとに資金の裏付けがある配分を公開します。これについてRipplesができることとできないことは、利用規約に定めています。
期間の配分が公開されると、Ripplesが各ホルダーの分をそれぞれのウォレットに送ります。ネットワーク手数料はRipplesが負担します。ホルダー側で必要な操作はありません。ネットワーク手数料に見合わないほど少額の分はアカウントページに残り、そこで受け取れます。
アカウントページには、公開済みの各期間と2つの金額が、それぞれの資産の単位で表示されます。まだ届いていない期間の分は、そこで受け取れます。Ripplesはウォレットに署名を求める前にチェーン上で受け取り内容を確認し、支払い済みのものは受け付けません。2つの資産はまとめて受け取ります。必要な送金に発行体の保留がかかっている場合、保留が解除されるまで受け取りは実行されません。自分で受け取る場合は、ネットワーク手数料を、そのネットワーク自体の資産で支払います。表示倍率が変えるのはウォレット上の表示だけで、送金される数量は変わりません。
公開されている配分は、GET /v1/holder-rewardsに rail、account、任意のcursorを指定して取得できます。エポックごとの完全な証明はGET /v1/holder-rewards/epochs/{distributor}/{epoch}?rail=...で取得します。報酬は、そのチェーンのファクトリーまたはプログラムが対応している場合にのみ提供されます。このサイトの設定で有効にすることはできません。
ローンチの種類
トークンローンチでは、供給量が固定されたトークンと、誰でも売買できるそのマーケットを作成します。NFTコレクションのローンチでは、購入者が直接買えるドロップを作成します。同時ローンチは両方を作成してつなげるもので、Ripplesはこのローンチの種類のために作られました。
同時ローンチでは、NFTの支払いのたびにその一部がクリエイターではなくトークンのマーケットに入り、NFTを購入したウォレットにはトークン供給量の一部が割り当てとして記録されます。この割り当てはすぐには支払われません。マーケットが目標額に達するのを待ち、その後段階的にアンロックされます。各支払いの残りはクリエイターのもので、アカウントページからいつでも引き出せます。
ローンチフォームには、そのための設定が3つあります。違いは、NFTの各支払いのうちマーケットに入る割合、購入者向けに確保されるトークン供給量の割合、そして購入者が受け取るまでの期間です。
どのコレクションも、作成された時点でこの4つの数値を自分のものとして保持します。そのため、これらはネットワークが全員に適用するルールではなく、今日作成するローンチについての数値です。あとから変更することはできません。
はい、Robinhoodで作成できます。どちらのチェーンでも、すべてのウォレットがトークンまたはNFTコレクションをローンチでき、Robinhoodではすべてのウォレットが同時ローンチ(専用のNFTコレクションを持つトークン)を作成できます。
ローンチの種類は、フォームの入力中なら変更できます。ローンチを作成すると固定されます。
RobinhoodでETHで支払う場合、ウォレットはまずローンチ費用分のラップ、次にファクトリーによる使用の許可、最後にローンチの作成を求めます。別の資産建てのローンチでは、その資産の使用許可が1つ加わります。そこでの同時ローンチは、初回購入のない1回のトランザクションのあと、アートワークのアップロード用の署名と、アートワークをコレクションに結びつけるトランザクションが1回ずつ続きます。
Wake
RobinhoodでNFTを保有している人は、そのNFTからマーケットをローンチでき、NFTはWakeを残します。Wakeとは、そのマーケットのクリエイター収益のことで、NFTの保有者に支払われます。NFTが売れると、Wakeも一緒に移ります。売り手も買い手も、支払先をほかに変えることはできません。
その過程で、作品はERC-8004エージェントとして登録されます。作品そのものがエージェントの顔になり、8004scanとOpenSeaのページへのリンクが付きます。保有者は名前を変えたり、自分のエンドポイントを指定したりできます。エージェントに記録されたフィードバックはエージェントに残るため、作品にも残ります。
エージェントを保有するウォレットは、そのエージェントからローンチを始めることもでき、ローンチにはエージェントの名前と顔が入ります。エージェントのNFTがRobinhoodにある場合、WakeはそのNFTとともに移ります。別のチェーンにある場合、クリエイター収益はローンチしたウォレットに残ります。
1つのマーケットのWakeを、元になったコレクションの最大256点の作品で分け合うこともできます。「確認」ステップの「詳細設定」で「Wakeを分け合う」をオンにし、1-20, 25のようにトークンIDで作品を指定します。各作品の取り分は均等です。取り分はそれぞれの作品に支払われ、作品とともに持ち主が変わります。すべての作品への支払いは、誰でも一度に実行できます。
Wakeを支払うのは、オーナーも設定項目もないコントラクトで、支払いの時点でNFTの保有者を読み取ります。支払いは誰でも実行できますが、支払先は常に保有者です。Wakeを残したすべての作品はWakeに一覧があり、未払い額と支払い済みの額を確認できます。
Upwell
Upwellは、RobinhoodのPonsですでに取引されているコインのためのNFTコレクションです。ミントのたびに、同じトランザクションの中でミントした人のためにそのコインを購入するので、作品とコインが一緒にウォレットに届きます。
ミント価格の80%で、Ponsのカーブ上で、またはコインが卒業した後はそのUniswap v4プールで、コインを購入します。コレクションのクリエイターが15%、Ripplesが5%を得ます。Ponsの1%の手数料と、コインのクリエイターが設定した税は購入額から差し引かれるため、署名の前に、そのミントで買えるコインの数が表示されます。ミンターは最低数量に署名し、価格がそれを超えて動いた場合はミントされず、資金も使われません。
コインにUpwellを作るのは誰でもでき、1つのコインにいくつでも作れます。Upwellから始めます。コインのアドレスを貼り付け、アートワークを追加し、0.001 ETH以上の価格と発行数を設定します。ローンチにはほかのコレクションと同じ0.0005 ETHの手数料がかかり、クリエイターは自分の取り分をいつでもコレクションのページから引き出せます。同じページのUpwell一覧では、Upwellによる購入額の順にコインが並びます。
Upwellが購入するのは、Ponsでローンチされ、ETH建てで価格が付くコインです。ローンチ直後のコインの最初の購入にPonsが税をかけている間、ミントは待機します。カーブからプールへの移行途中にあるコインは、ミントの一部としてプールが開かれます。
ファクトリーは0x48Fb81eEaDC1A8Af9037abD7B23DCc2aF8554e33、コインを購入するコントラクトは0x61E8c408750FB6b608f58F6AF8Df30DE2F792b32で、どちらもSourcifyでソースコードが検証済みです。購入用のコントラクトにはオーナーがなく、ミントとミントの間は何も保有しません。
ローンチの種類
エージェントローンチは、エージェントが自分自身のために開く同時ローンチです。エージェント用のコントラクトが作成され、同じトランザクションの中でそのコントラクトがローンチを開きます。そのため、記録上のクリエイターはこのコントラクトになります。すべての取引手数料の70%がこのコントラクトに支払われ、ローンチした本人の収益ではなく、エージェントの運転資金になります。
その後は、エージェントが自分のローンチを運用します。できるのは、自分のマーケットで自分のトークンを購入すること、設定された上限の範囲内で売却すること、自分のコレクションからミントすること、指定されたアドレスに支払うこと、公開の実行ログに書き込むことです。それ以外は何もできません。オーナーも、設定を変える関数も、引き出しの機能もないため、設定した上限はローンチが続く限りそのまま有効です。あとから引き上げることは、ほかの誰にもできないのと同じく、ローンチした本人にもできません。
設定するのは次の7つです。エージェントを動かすウォレット、ローンチ時にトレジャリーに入れる運用資金、1日に使える金額、1回のトランザクションで使える金額、1日に売却できる自分のトークンの量、1日にミントできる作品の数、支払い先にできるアドレス。1日の支出上限はすべての支出をまとめて対象とするため、購入、ミント、支払いは同じ予算から引かれます。売却上限が0なら、エージェントは自分のトークンを一切売却できません。フォームの初期値はこの設定です。
エージェントローンチで必要なウォレットの署名は、Robinhoodのほかのローンチと同じ2回です。ETHの使用許可が1回、ローンチ本体が1回です。コントラクト、その初期資金、ローンチはすべて1つのトランザクションで成立するため、エージェントが資金を持っているのにマーケットがない、という状態は生じません。
コントラクトがすべてを保有することから、2つの結果が生まれます。まず、エージェントが自分のコレクションからミントした作品は、エージェントの手元に残ります。公開されたアドレスへの支払いはできても、NFTを転送することはできないからです。次に、アートワークの設定は引き続きローンチした本人が管理します。ほかのローンチと同じように、エージェントを通じてコレクションの画像を指定し、凍結します。
ポストからローンチ
launchと書いたポストで@ripplesdotrunをメンションし、ティッカーを入れて画像を1枚添付します。フォームに相当するのはこれだけです。Robinhoodでトークンとコレクションが作成され、画像がアートワークになり、ポストへの返信にリンクが届きます。以下のほかの項目はすべて任意で、どんな順番でも、普段どおりの言葉で書けます。ローンチについて話しているだけでティッカーのないポストには、反応しません。
ローンチはRipplesが作成し、本人が受け取るまで預かります。受け取るには、スレッドにリンクされた受け取りページで1回署名するだけです。それでクリエイター手数料と作品#1が手に入ります。ポストにfor @someoneと書くと、代わりにその人のためにローンチされ、受け取るのはその人になります。
$KOI。英字と数字で10文字まで、英字を1文字以上含めます。そのため、数字で始まるティッカーも使えます。数字だけのものはティッカーではなく数値として読まれるので、ポストに書いた価格は価格のまま扱われます。ポストで最初に出てくるティッカーが、そのローンチのティッカーです。for @alice。返信はその人宛てになり、受け取るのもその人です。33 piecesのほか、7 editions、12 nfts、5 items、9 pcs、x1000でも指定できます。1以上の任意の数を使えます。何も書かなければ100点です。数字だけの場合は名前の一部として扱われるため、50 Centは名前であり、数量にはなりません。paired with GLD、またはpair with、pair it with、pair it up with。ドル記号は付けても付けなくてもかまいません。するとマーケットは、ETHではなくその株式トークンで価格を付け、取引し、手数料を徴収します。Ripplesがローンチの価格付けに使っている株式トークンである必要があり、対象外のティッカーには理由を添えて返信します。その時点でマーケットがない株式トークンの場合はローンチせずに返信するので、取引が始まってからもう一度ポストできます。silly modeまたはdark mode。sillyでは、すべての作品がばかばかしく、意外で、突き抜けたものとして生成されます。darkでは、流血表現なしに怖い、不気味で禍々しい作品が生成されます。何も書かなければ、通常どおりに生成されます。指示として扱われるのはmodeという語があるときだけなので、Dark Pepeという名前のローンチも作れます。2% creator feeまたはcreator fee of 2%。1%〜10%の範囲で、全員が支払う1%に上乗せして、すべての取引にかかります。何も書かなければ2%です。half of that to holders、またはall、everything、あるいは30% of that to holdersのような数値。自分のクリエイター手数料の1%〜100%を、ローンチの取引相手となる資産、つまりペアの資産またはETHで、ホルダーに支払います。hey @ripplesdotrun launch $JPMEME JPEG Morgan for @alice, 33 pieces,
paired with GLD, 2% creator fee, with half of that going to holdersこれで、JPEG Morganという名前の33点のコレクションができます。金建てで取引され、すべての取引に2%のクリエイター手数料がかかり、その半分が保有している人に金で支払われます。受け取るのはAliceです。
分配を約束したローンチには、作成と同時に専用のコントラクトが2つ用意されます。1つは、すべてのクリエイター手数料をポストで指定された割合で分け、クリエイターの分をそのまま送ります。もう1つは、ホルダーへの未払い分を保管し、支払います。分配率はローンチの作成時に決まり、あとから変更する関数はどこにもありません。
各ウォレットへの支払額は、ある時点の保有量ではなく保有していた期間から計算されるため、支払いの直前に購入してもほとんど何も得られません。期間はおよそ1日に1回締まります。期限はなく、未払い分は「アカウント」の「株式トークン報酬」で受け取れるほか、誰でもホルダーのために送金を実行できます。
ローンチできないポストには、どこを直せばよいかを返信します。画像がない、ティッカーがないか長すぎる、ここで価格付けに使っていない株式トークン、範囲外の手数料や分配率、支払いに使うペアがない分配、作品数が0のコレクション、のいずれかです。ローンチは自分の分もほかの人の分も合わせて、1アカウントにつき1日10件までです。次にローンチできる時刻は返信に書かれています。
費用
ローンチ手数料はチェーン上で設定され、フォームを開いた時点で読み取られます。そのため、フォームにはここに書かれた数字ではなく、実際にかかる手数料が表示されます。その下の「承認時の合計」は、トランザクションで支払うほかのすべての金額を合計し、内訳を下に示します。ウォレットの残高で足りない場合、フォームは送信できません。Robinhoodでは、ネットワーク手数料はこの合計に含まれません。ETHで別にかかり、その行にもそう書かれています。
ローンチ、ミント、取引はETHで支払い、トランザクションごとにETHの少額のネットワーク手数料もかかります。使う分のほかに、ETHを少し残しておいてください。目標額への到達前も到達後も、すべての取引で1%の取引手数料が徴収されます。そのうち30%はRipplesに入り、クリエイターはローンチ時に選んだ最大70%までの取り分を受け取り、残りはそのマーケット自身のロック済みの流動性に加わります。Ripplesに入る30%のうち、70%は$RIPPLESの購入に使われて恒久的な流動性としてロックされ、30%はRipplesのトレジャリーに入ります。ここでのマーケットからは、ローンチ時に設定したクリエイター手数料と、NFTの売上も受け取ります。クリエイター手数料はすべての取引でETHで徴収され、全額が支払われます。$RIPPLESはRipplesのプロトコルトークンです。マーケットはローンチ時の手数料体系をそのまま保つため、以前からあるマーケットでは手数料、分配率、目標額が異なる場合があります。
マーケットは、許可リストにある別の資産建てにすることもでき、フォームには選べる資産が並びます。現在、Robinhoodの一覧はETH、RIPPLES、PENGUと、レジストリが承認したトークン化株式です。最新の一覧はローンチが固定するものに掲載しています。この選択はローンチの作成時に固定されます。マーケットが何建てでも、Robinhoodでのローンチ手数料はETHのままです。そのため、別の資産を選んだクリエイターは、使用許可を1回ではなく2回承認します。それぞれに、何のための許可かが表示されます。目標額と初期リザーブはその資産自身の行から、その資産の小数点以下の桁数で決まり、上の目標額は既定の資産だけに当てはまります。発行体が資産を一時停止したり、アドレスをブロックしたりできる場合、その制限がかかっている間は、その資産建てのマーケットで購入、売却、ミント、プールへの移動がすべて止まります。どちらの制御もRipplesのものではなく、ここで解除することもできません。
アートワーク
NFTは、名前と画像へのリンクを記した小さなテキストファイルを参照しています。このファイルは誰かが公開する必要があり、ここで作成したローンチではRipplesが公開します。
「アートワーク」ステップで画像ファイルを選ぶと、RipplesがNFT1点につき1つの番号付きファイルとして公開し、コレクションの参照先をそれらに設定します。大きなコレクションは分割してアップロードされます。フォームに進捗が表示され、途中で失敗した場合は残りの分がわかります。
ローンチ自体の画像は、何をローンチするかで決まります。トークンだけのローンチでは「トークン」ステップで画像を設定し、コレクション付きのローンチでは最初の作品が表示されます。トークンを作成するローンチでは、入力した画像、説明、リンクをRipplesが公開し、公開アドレスで配信します。
正方形版の画像は鮮明にしておいてください。マーケットプレイスはこれをコレクションのアイコンとして使い、正方形でない部分は切り取られます。
アートワークのリンクは後から凍結できます。凍結すると、各NFTが詳細を読み取るアドレスが確定し、クリエイター自身もそのアドレスを変更できなくなります。Ripplesでホストしている作品は、クリエイターが差し替えたり削除したりできなくなります。規約に違反するホスト作品は、Ripplesが削除できます。
流動性
マーケットには目標額があります。目標額に届くまでは、購入者はマーケット自体を相手に取引し、トークンが売れるにつれて価格が上がります。マーケットが保有する額は双方向に動きます。購入で増え、売却で減るため、マーケットページの数字は上がることも下がることもあります。目標額に達したマーケットは、誰でも決済できます。Ripplesはこれを自動で実行するボットを動かしていますが、その権限はほかのどのウォレットとも変わりません。
決済で何が起きるかは、チェーンによって異なります。Solanaでは、今日作成するローンチはMeteoraのボンディングカーブで始まります。カーブに85 SOLが集まると、リザーブと残りの供給量が、カーブが到達した価格で公開のMeteoraプールに移り、流動性は永久ロックされます。カーブが満たされるとすぐにRipplesが移行させ、取引はプールで続きます。Ripplesプログラムで作成したローンチは、代わりに公開のRaydiumプールに移り、ロックの条件は同じです。目標額にはそのプールの作成費用が含まれるため、表示される目標額はマーケットのしきい値より少し高くなっています。プールの準備中は、取引が少しの間止まります。
Robinhoodでは、ローンチは最初のブロックから公開のUniswap v4プールなので、移る先はありません。決済では、目標額に向かう間のマーケットの価格を決めていたポジションを閉じ、同じプールに永続的なポジションを開きます。これは1つのトランザクションで完了し、ローンチが作成時に宣言したロックが適用されます。ネットワーク手数料は、実行した人が支払います。
マーケットが目標額に達して流動性が稼働すると、同時ローンチがNFT購入者向けに確保しているトークンの期間も始まります。それまでは何もアンロックされません。その後、各購入者の取り分はローンチのクリフ期間中ロックされ、そこから段階的にアンロックされて、最終的に全量を受け取れるようになります。各ローンチは作成時に2つのルールのどちらかを設定し、そのルールは後から変更できません。受け取り権がNFTをミントしたウォレットに残るか、NFTとともに移り、NFTの保有者がまだ受け取られていないトークンを受け取れるかのどちらかです。どちらのルールが適用されるかは、コレクションのページに表示されます。
流動性が稼働しても、マーケットからの支払いは変わりません。Solanaでは、取引手数料の取り分は最初の取引から受け取れ、目標額に達した後は、ロックされたプールが回収する手数料のうち同じ割合を受け取れます。受け取り待ちの額はアカウントページに表示され、そこで回収できます。Robinhoodでは、目標額に達しても何も変わりません。1%の取引手数料はローンチ時の配分のまま分配され、クリエイター手数料もそのままかかり続けます。そこのプールには、分配するための独自の手数料がありません。マーケットはローンチ時の手数料体系をそのまま保つため、以前からあるマーケットでは手数料、分配率、目標額が異なる場合があります。
NFTの売上は、これらとは別です。目標額を待つこともなく、目標額で止まることもありません。購入された時点でその金額はクリエイターのものになり、アカウントページですぐに回収できます。
収益
ローンチで得た収益は、すべてアカウントページの1か所で回収します。ローンチを作成したウォレットを接続すると、最初のカードにそのチェーンで回収できる額が表示され、1つのボタンですべてを回収できます。
クリエイター手数料は取引のたびに積み上がり、NFTの売上はNFTが売れた時点でクリエイターのものになります。カードにはそれぞれ別の行があり、得た資産の単位で表示されます。期限はないため、都合のよいときに回収できます。
Robinhoodでは、エージェントもSDKのcollect、またはMCPサーバー経由のripples_collectで同じものを回収できます。
ローンチの後
大事なリンクは2つです。コレクションページではNFTを購入でき、マーケットページではトークンが取引されます。どちらもウォレットを接続せずに見られるため、誰でも何かを決める前に、価格、供給量、マーケットが目標額のどこまで達しているかを確認できます。
次の4点は、画像を見ても購入者にはわからないため、はっきり伝えてください。
価格やリターン、誰かが売却できることを約束しないでください。どれもクリエイターが左右できるものではありません。約束できるのはコントラクトの動作です。両方のページの数字はすべてチェーンから読み取られるので、誰でも確かめられます。
マーケットが目標額に達すると、ページにプールへのリンクが表示されます。もう一度ポストするのによいタイミングです。その時点で流動性はロックされており、慎重な購入者がまず確かめるのがその点だからです。
Robinhoodのコレクションは標準のERC-721とERC-7572のインターフェースを使っているため、このチェーンをインデックスしているマーケットプレイスに掲載できます。対応の現状はOpenSeaで確認してください。
Robinhood
以下の数値はすべてローンチの作成時に設定され、Robinhoodのコントラクトによって強制されます。運用上の方針で守っているものは1つもありません。Ripplesが鍵を持つ箇所では、その鍵にできることとできないことを正確に記載しています。
アドレス
Ripplesがデプロイした、または保有するアドレスを、省略せずにすべて載せています。Ripplesが自らのアドレスを公表するのはここだけです。Ripplesのものとして送られてきたアドレスがこの一覧にない場合、それはRipplesから送られたものではありません。Ripplesで作成したローンチにはそれぞれ固有のアドレスがあり、各ローンチのページに表示されます。Robinhoodでの取引は、Uniswapのプールマネージャーとユニバーサルルーターも経由して決済されます。この2つはRipplesがデプロイしたものではありません。ルーターのアドレスは下のRobinhoodリファレンスに記載しており、プールマネージャーのアドレスは上の卒業フックに問い合わせると返されます。目標額に達したマーケットを完了させるのにRipplesの鍵は必要ないため、そのためのウォレットは載せていません。この呼び出しは誰でも行えます。
連携ページには、同じアドレスが機械で読み取れる形式で、イベントトピックやマーケットを読み取る呼び出しとあわせて掲載されています。
許可済み資産
ローンチは1つの資産で決済されます。この資産は作成時に選ばれ、このセクションのほかの項目とともに固定されます。オーナーは、新しいローンチが指定できる資産の一覧を管理しています。この一覧はあくまで資産の一覧で、誰がマーケットを取引・保有・閲覧できるかについては何も定めていません。Ripplesは、利用資格や法域による制限をどこにも設けていません。以下の行はチェーンから読み取ったものではなく、デプロイ記録から写したものです。そのため、最初に記載したレジストリの内容が正となり、各行にはその資産が許可されたブロックを記載しています。
資産を指定しないローンチは、そのチェーン自体の資産と、フォームが入力する数値を使います。下の目標額もその数値の1つです。別の資産を指定するローンチは、レジストリにあるその資産の行をそのとおりに申告する必要があり、そうでなければファクトリーは作成を拒否します。それらの数値はここには再掲しません。資産を選ぶとフォームがその行を読み取り、読み取れなかった行をローンチが申告することは認めません。また、行ごとにそれぞれの資産の小数点以下の桁数で表されるため、2つの行を単純な数値として比べることもできません。
隣に記載しているStockLinkのレジストリが記録するのは、関連付けだけです。保存するのはアドレスで、発行体や金融商品については何も検証せず、資金も動かさず、一度書き込むと変更も削除もできません。関連付けと決済通貨は別々の選択です。ローンチはどちらか一方だけ、両方、またはどちらも持たないこともできます。
同時ローンチ
同時ローンチは、互いを必要とする1つのトークンと1つのNFTコレクションです。マーケットが目標額に達するまでの各ミントのうちトークンのマーケットでの購入に充てる割合、そのミンターに割り当てられる供給量の割合、そしてアンロックのスケジュールはローンチに書き込まれ、作成後はクリエイターもRipplesも変更できません。
マーケット
目標額に達するまで、購入者はマーケット自体を相手に取引します。目標額に達すると、ポジションはマーケットの直近の取引価格で、ローンチが作成時に宣言したロックの下に入ります。Robinhoodでは、ローンチは最初から公開のUniswap v4プールなので、決済によって目標額到達前のマーケットの価格を決めていたポジションが閉じられ、同じプールに永続的なポジションが開かれます。以下の数値は、現在ローンチフォームが設定するものです。以前に作成されたマーケットは作成時の目標額、手数料、分配率を保持し、それぞれのページに記載しています。完了したマーケットは誰でも決済できます。Ripplesはこれを自動で行うボットを運用していますが、その権限はほかのウォレットと変わりません。
totalBurned()が数えるのは、これまでのすべての買い戻しを含め、バーン用コントラクトを通じて流通から外れたトークンだけです。2つは別の合計です。バーンアドレスへの送金によって、トークンのtotalSupply()が減ることはありませんクリエイター収益
クリエイターの収益は、ミント、取引、またはその両方から支払われます。取引からの収益はローンチの最初の取引から発生し、目標額に達しても止まりません。以下の行がそのすべてです。各ローンチは作成時に自らの数値を固定するため、古いローンチは作成時の数値を保持し、そのページに表示しています。
鍵
Ripplesがウォレットの秘密鍵を受け取ることはなく、ローンチ、ミント、取引に署名することもありません。Ripplesが実際に持つ鍵はすべてここに記載し、それぞれにできることとできないことを示しています。
Robinhoodからオーナーを読み取っています。
現時点の制限
現在デプロイされているものの制限です。ロッカーを読んだ人が、このセクションにない約束を見いだすことのないよう記載しています。
これらの背後にあるコントラクト名は、下のRobinhoodリファレンスに記載しています。GenesisローンチはGenesisページにあります。
1つ目のチェーン · Solana Mainnet
ブラウザは、コードに同梱されたRipplesプログラムの記述、つまりAnchorのインターフェース定義(IDL)を使い、Solanaの@solana/kitライブラリでトランザクションを組み立てます。対応ウォレットは、Solana Wallet Standardを通じてそのトランザクションを確認し、署名します。公開APIは、プログラムアカウントをインデックスし、ローンチフォーム用にローンチの画像と詳細を公開し、署名済みのNFTリビール設定を保存します。これとは別のVirtualsエージェント連携は、専用のエージェントウォレット1つに対応しています。できるのは読み取りだけです。エージェントのトランザクション、有料サービスの提供、メインネットへのアクセスは無効です。
デプロイ
engineがdbc、Ripplesプログラム上のローンチではripplesか、フィールド自体がありません。日付ではなく記録から判断してください。種類ごとに、読み取り、取引、受け取りの方法が異なりますQuoteAssetアカウントがあり、設定権限者が書き込みます。ローンチは作成時に、mint、そのトークンプログラム、小数点以下の桁数を記録するため、一覧ではなくローンチ自体を読み取ってくださいNEXT_PUBLIC_LAUNCHPAD_CHAIN=solana
NEXT_PUBLIC_LAUNCHPAD_CLUSTER=mainnet-beta
NEXT_PUBLIC_LAUNCHPAD_PROGRAM_ID=RippcKjgg9pkjHY7RKp6R1K8jPGwq2BRSJcgbErufjF
NEXT_PUBLIC_LAUNCHPAD_API_BASE=https://api.ripples.run
NEXT_PUBLIC_ALLOW_MAINNET=1ブラウザはhttps://api.ripples.run/v1/rpcにあるレジストリのリレーを通じてSolana Mainnetを読み取ります。プロバイダーのキーはこのリレーが保持しています。各トランザクションの確定も、同じリレーへのポーリングで確認します。ページがSolanaノードにWebSocketを開くことはありません。APIベースがない場合、ローカルビルドはクラスターの公開エンドポイントapi.mainnet-beta.solana.comを読み取ります。
NEXT_PUBLIC_LAUNCHPAD_RPC_URLには、ページが最初に読み取るエンドポイントを指定します。リレーはその後ろに控えます。NEXT_PUBLIC_の値はすべてページ内で読めるため、クエリ文字列、パス、ユーザー名とパスワードのいずれかにキーを含むエンドポイントは、ビルドが受け付けません。リレーは1リクエストにつき1つのJSON-RPC呼び出しを受け付け、ページに必要な読み取りメソッドとsendTransactionを許可し、getProgramAccountsにはRipplesのプログラムについてのみ応答します。NEXT_PUBLIC_LAUNCHPAD_RPC_FALLBACK_URLには、別の2つ目のエンドポイントを指定します。offにすると、1つ目のエンドポイントだけから読み取ります。
トランザクション
SOLの費用は接続中のウォレットが支払います。署名を求める前に、アプリは送金額、新しいアカウントを有効に保つために必要な最低限のSOL(レント免除)、ネットワーク手数料の余裕分を確認します。@solana/kitのプランナーは、最新のブロックハッシュを取得し、トランザクションをシミュレーションして、コンピュートユニットに安全マージンを加えます。署名後、アプリはトランザクションの署名を表示し、確定するかlastValidBlockHeightを過ぎるまでステータスを確認し続けます。
const message = await client.planTransaction(instructions);
const transaction = await signTransactionMessageWithSigners(message);
const signature = getSignatureFromTransaction(transaction);
await sendWithoutConfirming(transaction, {
commitment: "confirmed",
preflightCommitment: "confirmed",
skipPreflight: false,
});
// Poll signature status and block height using
// message.lifetimeConstraint.lastValidBlockHeight.プールが作成されるまで、トークンの価格はSOLで提示されます。購入時に購入者の標準のトークン保有アカウント(Associated Token Account)が作成されるのは、まだ存在しない場合だけです。Ripplesプログラムのローンチトークンは小数点以下9桁、Meteoraのカーブのものは6桁を使い、ウォレットは取引全体に署名します。
エージェント連携
VirtualsのAgent Commerce Protocol(ACP)は、エージェントがサービスを売買するための仕組みです。非公開のRipplesサービスは、登録済みのVirtualsエージェント1つと専用のSolanaウォレット1つを、選択されたネットワーク、設定済みのRipplesプログラム、同梱のAnchor IDLに接続します。受け付けるのは、定められたRipplesの操作の一覧だけです。任意の送金、ほかのプログラム向けの命令、Virtualsのトークンローンチツール、汎用の取引、プログラム権限やメンテナンスに関わる操作は拒否します。
トランザクションを送る前に、サービスは2つのSolanaエンドポイントがどちらも想定どおりのネットワークにあることを確認します。各命令をAnchor IDLからデコードし、プログラムアドレス、アカウントの順序、読み書きの権限、必要な署名者、Ripplesアカウントの現在の状態、1回の操作と1日あたりの支出上限を確認します。各リクエストには、同じリクエストを識別するためのダイジェストである固定のリクエストフィンガープリントと、Solanaのメモが付きます。結果がはっきりしない場合、サービスは再送せずにいったん停止し、既存のトランザクションを確認します。
任意の販売リスティング(ACPオファリング)は、ripples-action-v1という名前の、USDC建て固定価格のサービスです。購入者がジョブに資金を入れると、サービスは実行の直前に最新のリクエストをもう一度読み取り、確認します。Ripplesに送るSOL、Solanaのネットワーク手数料、新しいアカウントの資金は、エージェントのウォレットが支払います。Virtualsのスポンサー制度は、Ripplesプログラムのトランザクションには適用されません。
この連携は読み取り専用です。エージェントのトランザクション、有料の販売リスティング、メインネットへのアクセスは無効です。これが変わる場合は、このセクションに記載します。
独自のエージェント
すでにウォレットを持つエージェントは、上のVirtualsサービスを経由しません。公開APIからローンチを読み取り、レジストリのリレーか自前のノードでチェーンの状態を読み取り、ブラウザと同じ命令を組み立てて、自分の鍵で署名します。ネットワーク手数料も取引の代金も、すべてそのウォレットが支払います。このページが、エージェントの代わりに署名したり、支払ったり、承認したりすることはありません。
GET https://api.ripples.run/v1/launchesは、Solana Mainnetのトークンローンチを新しい順に、count、nextOffset、launches配列として返します。各行にはlaunch(マーケットのアドレス)、mint、creator、name、symbolが入り、同時ローンチではcollectionも入ります。/v1/collectionsはNFTコレクションを同じ形式で返し、/v1/launches/<address>は1つのローンチを現在のマーケットの状態とともに返します。一覧は既定でSolana Mainnetを返し、?chain=robinhoodを付けるとRobinhoodを返します。単一ローンチのルートは渡されたアドレスの形式に応じて読み取るため、どちらのチェーンにも応答します。/v1/collectionsがインデックスするのはSolana Mainnetだけです。Robinhoodのローンチは、ローンチ一覧と同じようにファクトリーから直接読み取ることもできます。
const base = process.env.RIPPLES_API ?? "https://api.ripples.run";
const page = await fetch(`${base}/v1/launches?offset=0`).then((r) => r.json());
for (const row of page.launches) {
// row.launch, row.mint, row.creator, row.name, row.symbol, row.collection
}
// An index with nothing in it is a valid answer, so read the first row only if there is one.
const [first] = page.launches;
const one = first && (await fetch(`${base}/v1/launches/${first.launch}`).then((r) => r.json()));チェーンごとに別々に数えられ、そのチェーンで最初のローンチが作られるまでは、どちらもcount: 0を返します。空のページでも止まらないようにループを書いてください。/v1/launchesのlimitは既定で50、上限は200です。/v1/collectionsでは常に25です。offsetは最新の行から数えます。読み取りはクライアントのアドレスごとに1分あたり120回までで、すべての応答のx-ratelimit-remainingに残り回数が入ります。
同じ呼び出しをテストネットワークで実行したい場合は、RIPPLES_APIを自分のビルドのAPIに向けてください。
チェーンの状態については、POST https://api.ripples.run/v1/rpcが、1リクエストにつき1つのJSON-RPC呼び出しを、サイトが読み取りに使うSolana Mainnetのエンドポイントへ中継します。受け付けるのは、クライアントに必要な読み取りメソッド(getAccountInfo、getMultipleAccounts、getLatestBlockhash、simulateTransaction、getSignatureStatusesなど)とsendTransactionです。getProgramAccountsはRipplesプログラムについてのみ応答し、バッチリクエストは拒否されます。リレーはクライアントのアドレスごとに1分あたり600回までに制限されているため、それより頻繁にポーリングするエージェントは、自前のエンドポイントを用意してください。
Solana Mainnetでは、公開されているIDL(solana/idl/ripples.json)と上の表のプログラムアドレスを使って、購入の命令を組み立てます。Codamaでこのファイルからクライアントを生成すると、ブラウザが使うものと同じgetBuyInstructionAsyncが得られます。ローンチアカウントにはmint、ボールト、ローンチがクリエイターに支払うアドレスが記録され、設定アカウントにはトレジャリーが記録されています。価格はローンチの仮想リザーブに対する定積型カーブで決まるため、何かに署名する前に最低受取額を計算できます。買い戻しボールトはクライアントが導出するプログラムアドレスで、それ以外のアカウントはすべてこの2つの記録のどちらかにあります。
ホルダーがクリエイター収益の一部を受け取るローンチでは、その割合がholderShareBpsとして記録されます。この値が0より大きく、ローンチがクリエイターへの支払いを自身の分配用アカウント以外に送っている場合、購入や売却の命令では、命令自体のアカウントの後に分配用アカウントを書き込み可能として指定します。トークン建てのローンチでは、さらにその後に分配用アカウントのエスクローを指定します。どちらもローンチから導出されるプログラムアドレスで、["holder_rewards", launch]と["escrow", quote_mint, distributor]です。こうしたローンチでは、これらがない取引をプログラムが拒否します。
import {
appendTransactionMessageInstructions, createKeyPairSignerFromBytes, createSolanaRpc,
createTransactionMessage, getBase64EncodedWireTransaction, pipe,
setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash,
signTransactionMessageWithSigners,
} from "@solana/kit";
import { findAssociatedTokenPda, TOKEN_PROGRAM_ADDRESS } from "@solana-program/token";
import { fetchConfig, fetchLaunch, findConfigPda, getBuyInstructionAsync } from "./generated/ripples";
const rpc = createSolanaRpc("https://api.ripples.run/v1/rpc");
const agent = await createKeyPairSignerFromBytes(agentSecretKey);
const launch = await fetchLaunch(rpc, launchAddress);
const [configAddress] = await findConfigPda();
const settings = await fetchConfig(rpc, configAddress);
const [buyerToken] = await findAssociatedTokenPda({
owner: agent.address,
mint: launch.data.mint,
tokenProgram: TOKEN_PROGRAM_ADDRESS,
});
// The platform's fee and the creator's own charge come off the same base, then the
// constant-product output. In the first 3 seconds the opening tax comes off
// before both, so a floor sized here fails until that window closes.
const quoteIn = 100_000_000n;
const fee = (quoteIn * BigInt(launch.data.tradeFeeBps)) / 10_000n;
const creatorTax = (quoteIn * BigInt(launch.data.creatorTaxBps)) / 10_000n;
const net = quoteIn - fee - creatorTax;
const expected = (launch.data.virtualToken * net) / (launch.data.virtualQuote + net);
const minTokensOut = (expected * 9_900n) / 10_000n;
// Where this launch pays its creator. The account is pinned to that address, so it
// cannot be derived, and a launch created before the field existed carries the default
// address there and is paid at launch.creator instead.
const creatorPayout =
launch.data.creatorPayout === "11111111111111111111111111111111"
? launch.data.creator
: launch.data.creatorPayout;
const buy = await getBuyInstructionAsync({
launch: launchAddress,
mint: launch.data.mint,
tokenVault: launch.data.tokenVault,
buyerToken,
buyer: agent,
treasury: settings.data.treasury,
creatorPayout,
quoteIn,
minTokensOut,
});
const { value: latest } = await rpc.getLatestBlockhash().send();
const message = pipe(
createTransactionMessage({ version: 0 }),
(m) => setTransactionMessageFeePayerSigner(agent, m),
(m) => setTransactionMessageLifetimeUsingBlockhash(latest, m),
(m) => appendTransactionMessageInstructions([buy], m),
);
const signed = await signTransactionMessageWithSigners(message);
const signature = await rpc
.sendTransaction(getBase64EncodedWireTransaction(signed), { encoding: "base64" })
.send();RobinhoodではマーケットはUniswap v4プールなので、エージェントはviemでフックから価格を求め、Universal Routerを通じてスワップします。使うアドレスはRobinhoodのデプロイ表にあります。ルーターはPermit2を経由して支出するため、決済トークンをPermit2に承認し、さらにPermit2からルーターに承認します。ネットワーク手数料は、同じウォレットからETHで支払います。
import { createPublicClient, createWalletClient, http, parseAbi } from "viem";
import { privateKeyToAccount } from "viem/accounts";
const chain = {
id: 4663,
name: "Robinhood",
nativeCurrency: { name: "ETH", symbol: "ETH", decimals: 18 },
rpcUrls: { default: { http: ["https://rpc.mainnet.chain.robinhood.com"] } },
};
const account = privateKeyToAccount(agentPrivateKey);
const client = createPublicClient({ chain, transport: http() });
const wallet = createWalletClient({ account, chain, transport: http() });
const QUOTE = parseAbi(["function approve(address spender,uint256 value) returns (bool)"]);
const PERMIT2_ABI = parseAbi([
"function approve(address token,address spender,uint160 amount,uint48 expiration)",
]);
const ROUTER = parseAbi([
"function execute(bytes commands,bytes[] inputs,uint256 deadline) payable",
]);
// Price the market off LaunchLens.launchesOf and take the hook's cut off the output
// before signing a floor. The Robinhood "Token trading" section shows that
// arithmetic in full, and where the lens is deployed.
const quoteIn = 10n ** 16n;
const amountOutMinimum = floorFor(quoteIn);
const expiry = Math.floor(Date.now() / 1000) + 3_600;
await wallet.writeContract({ address: quoteToken, abi: QUOTE, functionName: "approve", args: [permit2, quoteIn] });
await wallet.writeContract({ address: permit2, abi: PERMIT2_ABI, functionName: "approve", args: [quoteToken, universalRouter, quoteIn, expiry] });
const hash = await wallet.writeContract({
address: universalRouter,
abi: ROUTER,
functionName: "execute",
args: [
"0x10",
[encodeV4Swap({ poolKey, zeroForOne: quoteIsCurrency0, amountIn: quoteIn, amountOutMinimum })],
BigInt(expiry),
],
});
await client.waitForTransactionReceipt({ hash });トークンローンチ
標準のウェブの流れでは、クリエイターが名前、ティッカー、説明、画像と、任意でウェブサイト、X、Telegram、カスタムリンクを入力します。Ripplesはこれらをまとめて公開し、命令のuriフィールドに自動で入力します。独自の画面を作る開発者は、互換性のある公開メタデータURIを直接指定できます。
createLaunch命令は、3つの中核となるレコードを作成します。マーケットの状態(ローンチのプログラム派生アドレス、PDA)、標準のSolanaトークン(SPL Tokenのミント)、Ripplesが管理する取引用の在庫(プログラムのボールト)です。総供給量は一度だけ発行され、その後、トークンを追加でミントする権限と凍結する権限は取り除かれます。トークンの名前、シンボル、URIはMetaplex Token Metadataに保存されます。
import { getCreateLaunchInstructionAsync } from "./generated/ripples";
const instruction = await getCreateLaunchInstructionAsync({
launch,
tokenVault,
creatorToken,
creator: walletSigner,
treasury,
args: {
nonce,
name: "Ripple",
symbol: "RPL",
uri: "ipfs://…",
curveSupply: 800_000_000n * 1_000_000_000n,
lpSupply: 321_052_631n * 1_000_000_000n,
virtualQuote: 36_250_000_000n,
virtualToken: 1_073_000_000n * 1_000_000_000n,
graduationQuote: 29_000_000_000n,
lpUnlockAt: 9_223_372_036_854_775_807n,
linked: false,
nftAllocationBps: 0,
mintToCurveBps: 0,
vestingCliff: 0n,
vestingDuration: 0n,
initialBuy: 0n,
minTokensOut: 0n,
snipeExemptions: [],
creatorTaxBps: 0, // the creator's own charge, 0 to 1000 bps
},
});トークンだけのローンチは、目標額の0.4倍にあたる決済通貨側の仮想リザーブで始まり、NFTからの資金は受け付けません。連動ローンチは同じ目標額の1.25倍を使い、NFTの資金で卒業するために必要な、より大きな流動性の割り当てを保持します。承認済みの決済トークンについては、現在のQuoteAssetレコードを読み取ってください。保存されているリザーブは連動ローンチ用のものです。単独ローンチ用のリザーブは目標額の5分の2で、両方の比率が決済通貨の最小単位でちょうど表せる場合にのみ利用できます。プログラムは作成時にこれらの条件を確認します。後から承認内容が変わっても、それが適用されるのは今後のローンチで、既存のマーケットのリザーブや目標額は変わりません。
マーケットの手数料は引数ではありません。プログラムは作成するすべてのローンチに1%を書き込むため、呼び出し側が別の条件でマーケットを開くことはできません。creatorTaxBpsはその上に加わるクリエイター手数料で、全額がクリエイターに支払われ、上限はプログラムが定めています。この引数は、手数料が抜けた位置ではなく引数リストの最後にあります。そのため、古いリストに合わせて作られたクライアントは、誰も求めていない手数料を送るのではなく、デコードに失敗します。
Raydiumの流動性プールへの移行は、実際のSOLリザーブが目標額(graduationQuote)と、確保済みのセットアップ費用(graduationCostLamports)の合計に達すると可能になります。任意の初回購入(initialBuy)は、作成と同じトランザクションで完了します。
NFTコレクションと連動するトークンは、待機状態(LinkPending)から始まります。createLinkedCollectionが成功するまで、取引はできません。この2つ目のトランザクションが完了しないままの場合、クリエイターはcancelPendingLaunchを呼び出せます。
NFTコレクション
すべてのNFTは、そのコレクション専用のCoreコレクションに属するMetaplex Coreのアセットです。そのため、Metaplex Coreを読み取るウォレットやマーケットプレイスであれば、どこでも表示されます。Solanaのマーケットプレイスはコレクションを1つずつ掲載しますが、ここのコレクションページには、ミントされた瞬間からNFTが表示されます。
createCollectionは、Ripplesのコレクションの状態と、そのMetaplex Coreコレクションを作成します。mintAssetは1回のトランザクションでNFTを1点作成し、SOLの支払いを記録して購入者のミント上限を更新します。同時ローンチの場合は、トークンマーケットへの資金の投入も、最初のミンターの分として記録します。
| 命令 | 権限者 | 用途 |
|---|---|---|
mintAsset | ミンター | SOLを支払い、NFTを1点作成します。 |
revealAsset | リビール権限者 | 現在のプレースホルダーを、1回だけ置き換えます。 |
setBaseUri | クリエイター | 準備済みのコレクションがアートワークを読み込むリンクを、ロックされるまで変更します。 |
setPlaceholderUri | クリエイター | 凍結する前に、現在のプレースホルダーを変更します。 |
freezeMetadata | クリエイター | URIを変更する機能を永久に無効にします。 |
creatorWithdraw | クリエイター | クリエイターに帰属する売上だけを引き出します。 |
コレクションのプログラム派生アドレス(PDA)に直接送られたSOLは、NFTの支払いとして数えられません。誰でも呼び出せる別の回収用の命令で、その余分なSOLだけを、記録されたクリエイターに送ることができます。
株式との関連付け
トークンローンチ、NFTコレクション、同時ローンチは、任意で変更できないStockLinkの付随アカウントを作成できます。このアカウントには、対象、正確なミントアドレス、SPL TokenかToken-2022かのプログラムが記録されます。作成時には、ミントアカウントが存在し、初期化済みで、記録されたトークンプログラムが所有していることを確認します。付随アカウントは、対象のアカウントとローンチの経済条件を変えません。
| ローンチの種類 | StockLinkの対象 |
|---|---|
| トークンローンチ | Launch PDA |
| NFTコレクション | Collection PDA |
| 同時ローンチ | Launch PDA |
プログラムが確認するのはミントアカウントとの関係であり、発行体や、そのミントが何に連動しているかは確認しません。Ripplesのメインネット用の選択画面は、発行体のカタログからトークン化トラッカーのミントを解決します。カタログを利用できない場合、リンクは作成できず、同梱のスナップショットは未検証であることが明示されたままです。公開ページは、リンクを発行体のカタログと一致すると表示する前に、ミントの照合と、必要なToken-2022プログラムの確認をもう一度行います。プログラムを直接呼び出す場合は、ほかの有効なSPL TokenまたはToken-2022のミントにもリンクできますが、Ripplesの画面ではそれを未検証と表示します。
株式リンクは参照を記録するものです。資産を動かすことも、権利を生むこともありません。リンクによってトラッカートークンを購入やスワップすることも、ローンチとペアにすることもなく、資産の保管、ホルダーへの報酬、配当を受ける権利の付与、いかなる資産の分配も行いません。経済条件も変わりません。リンクを記録したマーケットは、作成時の資産、目標額、手数料、アンロックの条件をそのまま保ち、付随アカウントはそのどれにも触れずに読み取れます。Ripplesの選択画面は、テストネットワークでは使えません。
ローンチは、作成時に指定した資産で決済されます。クリエイターが作成時に承認済みのミントを指定しない限り、それはSOLです。この選択は同じフォームで行いますが、それ以外にStockLinkとの関係はありません。ローンチは、トラッカーを参照してSOLで決済することも、承認済みのミントで決済して何も参照しないことも、その両方も、どちらもしないこともできます。関連付けを記録しても、取引の決済に使う資産が変わることはなく、あるミントでマーケットの価格を付けても、その発行体との関連付けが記録されることはありません。ローンチがどちらを持っているか、あるいはどちらも持たないかは、マーケットのページに記載されています。
参照するトークン化トラッカーや、ローンチの決済に使う資産を利用できるかどうかは、発行体の規約と利用者の法域によって決まります。これは資産とその発行体に関する開示です。Ripplesは、利用資格や法域による制限を設けていません。プログラムにも、このサイトにも、このリファレンスにも、読み手がどこにいるのか、誰なのかを確認する仕組みはなく、承認済みのリストは資産のリストです。保留となるのは発行体自身による一時停止とアカウントの凍結だけで、これらはRipplesが適用するものではなく読み取るものです。一時停止されたミント建てのマーケットは、発行体が解除するまで停止します。
トークンの取引
Meteoraのカーブ上のローンチ(engine: "dbc")は、Meteora自身のプログラムとSDK(@meteora-ag/dynamic-bonding-curve-sdk)を通じて取引します。プールのアドレスがマーケットです。プールとその設定をもとに見積もりを取り、見積もりの下限を付けてスワップを送信します。すべての取引は手数料をSOLで支払い、手数料はマーケット開始直後の短い間に下がっていくため、料率を決め打ちせず、取引ごとに見積もりを取ってください。カーブが85 SOLを保有すると、プールが開くまでプログラムはそれ以上のスワップを拒否し、それ以降はJupiterがこのトークンをルーティングします。
const client = new DynamicBondingCurveClient(connection, "confirmed");
const virtualPool = await client.state.getPool(pool);
const config = await client.state.getPoolConfig(virtualPool.poolState.config);
const quote = client.pool.swapQuote2({
virtualPool, config,
swapBaseForQuote: false, // true to sell
hasReferral: false, eligibleForFirstSwapWithMinFee: false,
currentPoint: await getCurrentPoint(connection, config.activationType),
slippageBps: 100, swapMode: SwapMode.ExactIn, amountIn: lamports,
});
const transaction = await client.pool.swap2({
owner, pool, swapBaseForQuote: false, referralTokenAccount: null,
swapMode: SwapMode.ExactIn, amountIn: lamports, minimumAmountOut: quote.minimumAmountOut,
});Ripplesのプログラム上のローンチは、以下の命令を使ってローンチアカウントと取引します。
// Amounts are base units: the quote's own smallest unit, and 9-decimal SPL units.
// A launch quoted in SOL takes lamports here; one quoted in a mint takes that mint's.
// creatorPayout is where this launch pays its creator, pinned by the launch record:
// launch.creatorPayout, or launch.creator where that field is the default address.
// A launch with launch.holderShareBps above zero that pays its creator anywhere other than
// its distributor also takes the distributor, writable, after the instruction's own
// accounts, and on a token-priced launch the distributor's escrow after that.
await getBuyInstructionAsync({
launch,
mint,
tokenVault,
buyerToken,
buyer: walletSigner,
treasury,
creatorPayout,
quoteIn: 100_000_000n,
minTokensOut,
});
await getSellInstructionAsync({
launch,
launchAuthority,
mint,
tokenVault,
sellerToken,
seller: walletSigner,
treasury,
creatorPayout,
tokensIn,
minQuoteOut,
});ローンチは作成時にtradeFeeBpsを固定し、マーケットが存続する限りその値を保つため、料率を決め打ちせず、ローンチアカウントから読み取ってください。quoteMint、quoteTokenProgram、quoteDecimalsも同じように固定されるので、資産もローンチから読み取ってください。ローンチごとの資産に対応する前に作成されたローンチは、決済通貨を「なし」と返し、これはSOLを意味します。ウェブのフォームはSolanaのマーケットを1%で作成し、手数料そのものを固定の割合で分けます。取引額の0.7%がクリエイターに、0.24%が$RIPPLESの買い戻し用に確保され、残りはプロトコルに入ります。クリエイターの取り分はローンチの最初の取引から支払われ、卒業後はcollectLockedFeesが、ロックされたプールのポジションが回収した手数料を、記録された同じ割合で分けます。この割合はローンチアカウントのcreatorFeeShareBpsとして読み取ってください。以前のスケジュールで作成されたマーケットは、そのマーケット自身の割合を返します。ローンチはクリエイター手数料(creatorTaxBps)を固定することもできます。これは購入でも売却でも手数料と同じ金額から差し引かれ、プロトコルの取り分なしで全額がクリエイターに届きます。上のエージェントの購入例と同じように、ローンチから読み取り、それを差し引いたうえで下限を決めてください。最初の3秒間は、免除されていない購入者に、99%からゼロまで下がるオープニングタックスもかかります。このタックスでトークンは購入されません。ローンチがsnipeTaxCollectedとして保持し、卒業時にプールへ投入します。意図的にrealQuote()の外に置かれているため、目標額には数えられません。署名者は必ず価格の制限を指定します。購入ではminTokensOut、売却ではminQuoteOutです。
マーケットは、ローンチアカウントのcreatorPayoutにクリエイターへの支払いを送ります。最初はクリエイター自身で、変更できるのはそこに記載されたアドレスだけです。変更にはsetCreatorPayoutを使い、次の取引から反映されます。そのアドレスが直接受け取れない収益は、プロトコルの収入にはならず、エスクローで待機します。claimEscrowは1つの資産の残高を1件支払い、claimNativeEscrowはSOLを支払います。どちらも誰でも実行でき、どちらも支払先を変えることはできません。受取先の残高がそのアカウント自身のレントの下限を超えないほど少額のSOLの受け取りは拒否されます。入金された残高をランタイムが回収してしまうためです。
同時ローンチ
Raydiumプールが作成されるまで、NFTの支払いのうち設定された割合が、仮想の価格リザーブを変えずにトークンマーケットのリザーブへ移ります。各ミンターの取り分はローンチの卒業時に確定し、そのミンターのミントがマーケットに入れた額に比例します。プールが完成すると、claimVestedが、設定されたクリフ期間とアンロックのスケジュールに従ってトークンをアンロックします。finalizeBeneficiaryは、全額を受け取ったミンターの記録を閉じます。これにより、最後に残る端数まで計上できます。NFTを売却または譲渡しても、このトークンの受け取り権は移りません。
MeteoraのDynamic Bonding Curve(DBC)上の同時ローンチは仕組みが異なり、そのための命令とエンドポイントには名前にdbcが含まれます。プールは、下に挙げる設定ごとに1つずつ用意された3つのRipplesの設定のいずれかから作成されます。各設定は、トークン供給量のうちコレクションのために確保する量と、そのアンロックの方法を固定します。その後、create_dbc_linked_collectionでコレクションをプールに連動させます。これにより、プールの作成から31秒後、カーブの開始直後の高い手数料が終わった時点で販売が始まります。それより前に送られたミントは拒否されます。カーブが完了するまで、各ミント(mint_asset_dbc)は価格のうち設定された部分を使い、Ripplesのプログラムを通じてカーブ上でトークンを購入します。カーブの完了後は、ミント価格の全額がコレクションの収益になります。カーブがMeteoraプールへ移行すると、誰でもsettle_dbc_linkを呼び出せます。これは、それらのミントで購入したトークンと確保済みの供給量を合わせるものです。Ripplesがこれを呼び出すサービスを動かしているため、連携する開発者が自分で呼び出す必要は通常ありません。各NFT(ここでは作品と呼びます)は、ミントの時点で購入されたトークンに加えて、そのミントがカーブ上で使ったSOLに比例した、コレクションの確保済み供給量の取り分を受け取れます。カーブの完了後にミントされた作品はカーブ上で何も使わないため、トークンも取り分も受け取りません。作品の取り分は、カーブが完了した時点、つまりMeteoraプールが開く直前から数えて、設定のスケジュールに従ってアンロックされます。取り分はミントしたウォレットではなく作品に属します。作品の保有者がclaim_dbc_pieceで受け取り、GET /v1/dbc/pieces/<collection>/<serial>は、その作品の取り分のうちアンロック済みの量と受け取り済みの量を返します。
流動性プール
Meteoraのカーブ上のローンチは、SOLリザーブが設定で決まった基準額(標準の設定では85 SOL)に達すると完了します。その後Ripplesがmigrate_damm_v2を実行し、カーブが到達した価格でローンチのMeteoraプール(DAMM v2)を開き、流動性を永久ロックします。これは誰でも実行でき、ほかの誰かが移行したプールはそのまま残されます。そのプールにあるクリエイターのロック済みポジションは、それ以降の手数料を回収し、cp-amm SDKのclaimPositionFeeで受け取れます。カーブ自身のクリエイター手数料は、claimCreatorTradingFee2でいつでも受け取れます。取引ターミナルは、最初の取引からこのプールを掲載します。
Ripplesのプログラム上のローンチは、次の手順でRaydiumプール(CPMM)に移行します。
beginGraduationがトークンの取引を一時停止し、流動性プールの開始に使う数量を記録します。createCpmmPoolが必要なSOLを用意し、ローンチに紐づくRaydiumの定数積プール(CPMM)を作成します。プールは、ローンチのトークンとローンチ自身の決済通貨のミントを、そのミントのトークンプログラムのもとでペアにします。Raydiumが審査する拡張機能を持つミントは、そのSupportMintAssociatedアカウントをremaining accountとして渡す必要があり、そのアカウントがないミントはプールに入れません。lockLpが流動性提供者のポジションを確保します。永久ロックにはRaydium Burn & Earnを使います。finalizeGraduationは、流動性ポジションの保管先をプログラムが確認したうえで、移行を完了として記録します。プールが作成される前であれば、ローンチのクリエイターまたは現在のRipplesプログラムの管理者がresumeTradingを呼び出し、用意した数量を取り消してトークンの取引を再開できます。この復旧手段は、プールのポジションが記録された時点で使えなくなります。
期限付きでロックされた流動性提供者のポジション(LPポジション)は、lpUnlockAtまで、プログラムが所有するアカウントに残ります。永久ロックではcollectLockedFeesを使います。これはプールの両側をローンチ自身のアカウントに集め、届いた分をローンチのcreatorFeeShareBpsで分けます。クリエイターの取り分はcreatorPayoutに、残りはトレジャリーに送られます。支払先は取引と同じcreatorPayoutなので、このアドレスを変えると両方の支払先が変わります。期限付きのポジションは、ロックの期限が切れた後にのみwithdrawUnlockedLpを使います。
公開レジストリAPI
このAPIは公開ウェブアプリを支えるものです。以下のルートはSolanaチェーンをインデックスします。/v1/launchesは?chain=robinhoodを指定するとRobinhoodにも応答し、/v1/launches/<address>はどちらのチェーンにも応答します。非公開のVirtualsエージェント連携は別のサービスで、このAPIがそのウォレットの認証情報やトランザクションのリクエストを受け取ることはありません。
| メソッド | パス | 用途 |
|---|---|---|
| GET | /v1/launches | インデックス済みのローンチPDAと、現在の公開状態。 |
| GET | /v1/collections | インデックス済みのコレクションPDAと供給量。 |
| POST | /v1/auth/solana/challenge | ウォレットが署名する1回限りのメッセージを作成します。 |
| POST | /v1/auth/solana/verify | ウォレットのEd25519署名を検証し、非公開のセッショントークンを発行します。 |
| POST | /v1/launch-media | ローンチ画像と、トークンまたはコレクションの公開情報を検証して公開します。Solanaウォレットのセッションが必要です。 |
| POST | /v1/collections/:collection/config/challenge | プロバイダー設定をクリエイターとコレクションに結びつけます。 |
| PUT | /v1/collections/:collection/config | 署名したばかりのプロバイダー設定を保存します。 |
/v1/launch-mediaは、name、symbol、descriptionと、MIMEタイプとbase64データを含むimageオブジェクトを受け付けます。任意のsocialsフィールドはウェブサイト、X、Telegramに対応し、customLinksにはラベル付きのHTTPSリンクを5件まで指定できます。画像はPNG、JPEG、GIF、WebP、AVIFのいずれかで、1 MB以下である必要があります。サービスは実際のファイル形式を確認し、画像とJSONを、それぞれの内容自体で指定される公開リンクに保存して、metadataUriとimageUrlを返します。同じアドレスは、ホストされている間は同じバイト列を返します。Ripplesは、利用規約に違反するコンテンツを削除できます。ブラウザはmetadataUriを自動で使うため、標準のフォームを使うクリエイターがこれを自分で入力したり、ホストしたりする必要はありません。
アドレスはbase58で、大文字と小文字を区別します。チャレンジのUTF-8バイト列をそのまま署名し、64バイトのEd25519署名をそのままbase58でエンコードして送信してください。これにより、パスワードもSolanaのトランザクションも使わずに、ウォレットを管理していることを証明できます。各チャレンジは1回だけ使え、期限が切れると無効になります。
リファレンス · Robinhood
Ripplesは公開されたコントラクトの集まりで、ソースコードはMITライセンスのもとgithub.com/ripples-dot-run/contractsで公開しています。すべてのローンチは2つのファクトリーがデプロイします。そのため、トークン、そのマーケット、コレクション、アンロック用コントラクトはどれも、使う前に読めるコードから作られます。ブラウザはviemでこれらを呼び出し、Robinhoodに対応するウォレットなら、その呼び出しを確認して署名できます。途中で資金を動かせるものはありません。ローンチとコレクションはファクトリーから直接読み取り、画面に表示される取引の下限が、そのままウォレットで署名する下限です。
デプロイ
approvedQuote(address)とquoteEconomics(address)。quoteCount()とquotes(offset, limit)でページ送りlinkOf(curve)。アドレスを1つ保存するだけで、発行体や金融商品については何も検証しませんcontractURIも備えています。マーケットの読み取りと取引には、そのマーケットが作成されたグループのコントラクトを使います。どのグループかはマーケットのページに記載されています。
NEXT_PUBLIC_LAUNCHPAD_CHAIN=rh-mainnet
NEXT_PUBLIC_LAUNCHPAD_RH_RPC_URL=https://rpc.mainnet.chain.robinhood.comエンドポイントの指定は任意で、既定では上のアドレスを使います。設定するのは、公開エンドポイントの前に自前のオリジンを置く場合だけです。チェーンとエンドポイントが別々のネットワークを指すビルドは起動時に拒否されるため、一方のネットワークを読みながらもう一方を名乗る状態にはなりません。
メインネットを指すビルドには、NEXT_PUBLIC_ALLOW_MAINNET=1も必要です。環境変数の入力ミスで、開発用のビルドが実際の資金に向いてしまうのを防ぐためです。
トランザクション
支払いはすべて承認額の範囲で引き出され、承認の対象はファクトリーの既定の資産ではなく、そのローンチ自身の決済通貨です。許可済み資産建てのマーケットでの購入やミントでは、その資産を承認します。資産はマーケットまたはコレクションのQUOTE()で読み取れます。通常の承認額を使わないのは取引だけです。取引はPermit2を通じて支出するルーターを経由するため、ある資産で初めて取引するときは、まずトークンをPermit2に一度だけ承認し、次にPermit2からルーターに金額と期限を指定して承認します。2回目の承認では、実際に取引を出すルーターを指定します。一方のルーターに与えた許可は、もう一方のルーターを通る取引には及びません。ローンチ手数料は例外で、常にfeeToken()が返すETHで支払います。そのため、別の資産建てのローンチを作成するには、手数料用と初回購入用の2回の承認が必要です。ローンチ手数料もミントも、支出する呼び出しの前にそれぞれapproveが必要で、どちらもネイティブ通貨での支払いは受け付けません。そのためETHで支払う場合は、同じ操作の中で、まず必要な額ちょうどをラップします。ETH建てのマーケットでの購入には、承認は一切必要ありません。後述の「トークンの取引」で説明するとおり、スワップと一緒にETHを送ります。アプリは承認と呼び出しを組にして計画し、ウォレットに何かを求める前に、1回のeth_simulateV1でまとめて実行します。そのため、ミント期間の終了、ウォレットごとの上限、ドロップの完売、価格の変動は、まだ何も署名しておらず、承認も残っていない段階でわかります。ノードがシミュレーションに対応していない場合は、シミュレーションなしでリクエストを進めます。
ガスは余裕を持たせて見積もり、価格はeth_feeHistoryから求めます。ノードがこれに応答しない場合はeth_gasPriceを使います。アプリは送信前にトランザクションハッシュとアカウントのnonceを記録するため、トランザクションの処理中にページを再読み込みしても、2回目の署名はせず、そのトランザクションと照合します。
このネットワークが現行のv4-peripheryと異なるのはスワップのパラメータだけですが、失敗しても理由が示されません。このネットワークのSWAP_EXACT_IN_SINGLE構造体には、amountOutMinimumとhookDataへのオフセットの間に、余分なヘッドワードが1つあります。ドキュメントどおりの5フィールドの構造体でエンコードすると、ルーターはその未使用スロットからフックデータのオフセットを読み取ってしまいます。プールの1つ目の通貨がチェーン自身の資産であれば、誤って読む位置がゼロのワードになり、スワップは成功します。ERC-20であれば同じスロットにトークンのアドレスが入っているため、calldataの境界チェックに失敗し、ルーターがPoolManagerに到達する前に、戻り値をまったく返さずにリバートします。ETH建てのマーケットはすべてERC-20を決済通貨にしているため、encodeV4Swapは6番目のフィールドを書き込みます。
// One read prices the market: config, position, state, raise, price, opening tax.
// launchesOf lives on the lens; the hook has no such function and reverts.
const [view] = await client.readContract({
address: launchLens,
abi: LAUNCH_LENS_ABI,
functionName: "launchesOf",
args: [[poolId]],
});
// Which side the hook charges depends on the hook the pool is keyed to, so read
// view.config.hook before sizing the floor.
//
// On QuoteFeeHook every leg comes off the WETH side whichever side the swap
// specifies: the opening tax first, then the trade fee and the creator's own
// charge on what is left. An exact-input buy is charged on its input and the
// curve sees the net, so the floor is the constant-product move on that net.
const taxQ = (quoteIn * view.snipeTaxBps) / 10_000n;
const feeQ = ((quoteIn - taxQ) * view.config.tradeFeeBps) / 10_000n;
const creatorQ = ((quoteIn - taxQ) * view.config.creatorTaxBps) / 10_000n;
const netOnQuoteFee = tokensOut(view, quoteIn - taxQ - feeQ - creatorQ);
// On LaunchHook the cut comes out of the swap's unspecified side, which on an
// exact-input buy is the launch token: the opening tax first, then the trade fee
// and the creator's own charge on what is left.
const gross = tokensOut(view, quoteIn);
const tax = (gross * view.snipeTaxBps) / 10_000n;
const fee = ((gross - tax) * view.config.tradeFeeBps) / 10_000n;
const creatorTax = ((gross - tax) * view.config.creatorTaxBps) / 10_000n;
const net = gross - tax - fee - creatorTax;
// Size the floor on what reaches the buyer, at the tolerance the app signs.
// quoteFeeHook is the address the deployment table above lists.
const isQuoteFeeHook = (hook) => hook.toLowerCase() === quoteFeeHook.toLowerCase();
const reaches = isQuoteFeeHook(view.config.hook) ? netOnQuoteFee : net;
const amountOutMinimum = (reaches * 9_900n) / 10_000n;
// V4_SWAP is the router's one command; the actions inside it are
// SWAP_EXACT_IN_SINGLE, SETTLE_ALL, TAKE_ALL. The deadline is a wall-clock
// second: the router compares it to block.timestamp.
const { request } = await client.simulateContract({
account: buyer,
address: universalRouter,
abi: UNIVERSAL_ROUTER_ABI,
functionName: "execute",
args: [
"0x10",
[encodeV4Swap({ poolKey, zeroForOne: quoteIsCurrency0, amountIn: quoteIn, amountOutMinimum })],
BigInt(Math.floor(Date.now() / 1000) + 600),
],
});
const hash = await wallet.writeContract(request);
await client.waitForTransactionReceipt({ hash });トークンローンチ
createLaunchは、1つのトランザクションでERC-20とそのローンチのロッカーをデプロイし、プールを開いてカーブのポジションに初期流動性を入れます。総供給量は作成時に一度だけミントされ、その後にミントできる権限はありません。createLinkedLaunchは、同じトランザクションにコレクションとアンロック用コントラクトを加えます。そのため同時ローンチも1つのトランザクションで完了し、作りかけで修復が必要な状態は残りません。どちらの呼び出しも呼び出し元の承認額からローンチ手数料を引き出すので、ウォレットはその前に、この支出を承認します。
どちらの呼び出しにも、任意で初回購入を含められます。ファクトリーはこの購入をそのローンチ自身のLPLockerを通して行い、LPLockerを免除対象として登録するため、この購入にかかるのは取引手数料だけです。免除対象に加わる経路は3つあります。登録時のクリエイター、snipeExemptで渡したすべてのアドレス、そして後からsetCreatorFeeRecipientで手数料の受取先を移した先のアドレスです。ただし、実際の免除範囲は字面より狭くなります。フックが免除対象と照合するのはPoolManagerを呼び出したアドレスで、通常の取引はすべてルーターを経由してPoolManagerに届きます。そのため、クリエイターが後から購入する場合も、他の購入者と同じくオープニングタックスを支払います。snipeExemptで渡したアドレスが免除されるのは、そのアドレス自身がPoolManagerに直接送った購入だけです。
2つの呼び出しは、2種類のローンチの形に対応しています。単独ローンチは、小さめの価格算出用リザーブで始まります。調達額は1件ずつの取引でしか入らず、取引のたびに価格が動くためです。同時ローンチは大きめのリザーブで始まります。NFTコレクションなら、価格をまったく動かさずに調達額をまとめて流し込めるためです。どちらも同じ目標額に到達し、レジストリの行には形ごとのリザーブが記録されています。
const params = {
name: "Ripple",
symbol: "RPL",
curveSupply: 800_000_000n * 10n ** 18n,
lpTokenSupply: 219_000_000n * 10n ** 18n,
vQuoteInit: 275_880_000_000_000_000n,
vTokenInit: 1_073_000_000n * 10n ** 18n,
graduationQuote: 689_700_000_000_000_000n,
lpUnlockAt: 0n,
// On quoteFeeFactory the charge below is the whole of what trading pays the
// creator: that factory stamps no creator share into the trade fee.
creatorTaxBps: 0n, // the creator's own charge, 0 to 1000 bps
// The asset this market settles in. The zero address means the factory's default.
// vQuoteInit and graduationQuote above are in THIS asset's own decimals and must
// equal the row in the registry that belongs to the factory you create on, so
// read them from quoteFeeRegistry.quoteEconomics(quote) rather than typing them.
// The two factories keep different rows for the same asset and each refuses the
// other's.
quote: approvedQuote,
};
// The launch fee is charged in feeToken(), so it is approved, not sent as value. A
// launch priced in another approved asset approves that asset for the opening buy as
// well, which is two permissions rather than one.
await wallet.writeContract({
address: feeToken,
abi: QUOTE_ABI,
functionName: "approve",
args: [quoteFeeFactory, launchFee],
});
const { request } = await client.simulateContract({
account: creator,
address: quoteFeeFactory,
abi: TOKEN_FACTORY_ABI,
functionName: "createLaunch",
args: [params],
});取引手数料はローンチのパラメータではありません。このファクトリーが開くマーケットはすべてTRADE_FEE_BPSを課し、プール手数料とティック間隔はフック自身の定数です。上のリザーブは単独ローンチ用の組です。createLinkedLaunchでは代わりに同時ローンチ用の組を指定し、どちらの形も、もう一方のリザーブで指定するとファクトリーが拒否します。
lpUnlockAtが0であれば、流動性のロックは永久に解除されないという意味です。供給量が自身の卒業をまかなえない設定、初期価格が許容範囲を外れる設定、追加クリエイター手数料がMAX_CREATOR_TAX_BPSを超える設定は、ファクトリーが拒否します。そのため、ローンチできないマーケットがチェーンに載ることはありません。
NFTコレクション
Robinhood上のコレクションは、標準のERC-721とERC-7572のインターフェースを使い、ERC-2981のロイヤリティに対応しています。そのため、このチェーンをインデックスしているマーケットプレイスなら掲載できます。対応の現状はOpenSeaで確認してください。そのチェーンにマーケットプレイスがある場合、コレクションページとここにあるNFTの各行は、そのコレクションのマーケットプレイス上のアドレスにリンクします。
上記のファクトリーがデプロイしたコレクションは、contractURI()(ERC-7572)に応答します。ファクトリーは作成時に、これをhttps://api.ripples.run/v1/collection-assets/<collection address>/contract.jsonに向けます。このドキュメントには、コレクション名、正方形のロゴ、このサイト上のコレクションページ、コントラクトから読み取ったロイヤリティ率と受取先アドレスが含まれるため、誰も申請しなくてもマーケットプレイスのページが自動で埋まります。マーケットプレイスに表示される説明は、このドキュメントではなく、クリエイターが公開したアートワークから取られます。コレクションは参照先のオリジンを一度だけ読み取って保持するため、ファクトリーがオリジンを公開する前に作成されたドロップは、ここでは何も返しません。どちらに当たるかは、そのコレクションページに記載されています。自分でドキュメントをホストするクリエイターは、setContractURIでいつでも向け先を変えられます。アートワークを凍結した後でも同じです。
createCollectionは、コレクション自身の決済通貨建ての固定価格でコレクションをデプロイします。決済通貨は、クリエイターが作成時に別の許可済み資産を指定しない限りETHです。資産を指定すると、その資産のレジストリの行が適用されます。価格はその行の下限であるminPriceQuote以上でなければならず、レジストリが許可していない資産は拒否されます。資産を指定しないコレクションはファクトリーの既定の資産を使い、どの行にも縛られません。価格ルールのもう半分、つまりミント1回のうちマーケットに回る分が、その資産の小数桁で丸めて0になってはならないという条件は、両方の数値がそろう場所で確認します。createLinkedLaunchが作成時に確認し、Collection721.linkToCurveが両者をつなぐときにもう一度確認します。同時ローンチではコレクションとカーブが1つの資産を共有するため、ミントのうちマーケットに回る分は、ミンターが支払ったのと同じ資産でマーケットに届きます。資産が一致しない2つをつなごうとすると、linkToCurveはつながずにQuoteMismatchでリバートします。事前生成のコレクションは、最初からbaseURIのアートワークを配信します。後から生成するコレクションは、プラットフォームの署名者が各NFTの最終URIを1回ずつ書き込むまで、placeholderURIを配信します。mintは、コレクションの1ウォレットあたりの上限の範囲内で、1つのトランザクションにつき最大20点のNFTを作成し、同じ呼び出しの中で支払いのうち連動分をトークンのマーケットへ回します。
| 呼び出し | 権限 | 用途 |
|---|---|---|
mint(qty, minRoutedTotal) | ミンター | コレクションの決済通貨で支払ってNFTを作成します。マーケットに回る分には下限を付けます。 |
setTokenURI | プラットフォームの署名者 | 後から生成するコレクションのプレースホルダーを、1回だけ置き換えます。 |
setBaseURI | クリエイター | 凍結されるまで、事前生成のコレクションがアートワークを読み込むリンクを変更します。 |
setPlaceholderURI | クリエイター | 凍結される前に、後から生成するコレクションのプレースホルダーを変更します。 |
setContractURI | クリエイター | マーケットプレイスが読み取るコレクションドキュメントの向け先を変えます。凍結の対象はアートワークのリンクなので、これは凍結の影響を受けません。 |
freezeMetadata | クリエイター | アートワークのリンクを永久に固定します。 |
creatorWithdraw | クリエイター | クリエイターに帰属する売上だけを引き出します。 |
ミント以外でコレクションに送られた決済トークンは、支払いとして扱われません。recoverUnattributedは誰でも呼び出せ、その余剰分だけを記録上のクリエイターに送ります。
トークンの取引
Robinhoodのマーケットは、Ripplesのフックが付いた通常のUniswap v4プールです。購入も売却も0x8876789976dEcBfCbBbe364623C63652db8C0904のUniversal Routerを通るv4スワップで、支出は0x000000000022D473030F116dDEE9F6B43aC78BA3のPermit2を経由します。そのため、v4のルーティングにすでに対応しているアグリゲーターなら、Ripples側のものを何も組み込まずにRipplesのマーケットで取引できます。この経路は、マーケットが全量を約定できる注文、つまり通常の規模の注文ならどれでも受け付けます。アプリがマーケットと呼んでいるアドレスは、そのローンチのLPLockerです。LPLockerはポジションを保有し、ローンチ自身の情報に応答しますが、注文は受け付けません。
マーケットに残っている売却可能量を超える注文には、もう一方の経路が必要です。ローンチの流動性は、上下限のある1つの価格帯にまたがる1つのポジションで、LaunchHookはその範囲の外で終わるスワップをすべて拒否します。Universal Routerは広い価格制限でスワップを出すため、大きすぎる購入は範囲の端を越え、その拒否に当たって全体がリバートします。0x22DB9B2c6C6DB56ABC3fc196E4bC1FBb9c4338a6にあるRipples独自のルーターは、範囲の端を価格制限にして同じスワップを出します。プールはカーブの終点まで約定して止まり、届かなかった分の決済通貨がウォレットから引き出されることはありません。購入をWETHで支払う場合や、マーケットが別の資産建ての場合、アプリはこの経路で取引します。マーケットが目標額に達する前の最後の購入にも、この経路を使ってください。QuoteFeeHookに紐づくマーケットは、0x0e558e1e65BD1850730A3B4a89654c066fC035B7で同じ呼び出しを受け付けます。v4プールのフックはプールキーの一部なので、一方のフック向けに作られたルーターは、もう一方のフックのプールをすべて拒否します。どちらを使うかはマーケットのページに記載されています。
swapExactIn(key, zeroForOne, amountIn, minAmountOut, recipient, deadline)は(spent, amountOut)を返し、Universal Routerの経路と同じPermit2の承認額から支出します。ただし承認先は、Universal Routerではなくこのルーターです。spentはプールが実際に消費した量で、最大でもamountInです。minAmountOutは数量ではなく価格を表します。ルーターはspent * minAmountOut <= amountIn * amountOutが成り立つことを求めます。全額を支出する注文ではこれはちょうどamountOut >= minAmountOutと同じで、一部だけ約定する注文では、約定した部分に同じレートを求めます。そのため、注文全体を基準に署名した下限は、注文がどう約定しても意味が変わりません。
結果はFilled(poolId, sender, recipient, zeroForOne, amountIn, spent, amountOut)から読み取ります。spentがamountInを下回っていれば一部約定で、その差額は送信者の手元に残った資金です。PoolManager自身のSwapは約定した部分だけを報告し、約定しなかった部分には何も触れません。そのため、一部約定を読み取れるのは、amountInとspentを並べたときだけです。売却できる残量がないマーケットでは、CurveEndReachedでリバートします。
ETH建てのマーケットはWETHで決済され、アプリはUniversal Routerを通じてETHで取引します。購入も売却も、呼び出しは1回です。購入では、ETHを呼び出しのvalueとして送ります。まずWRAP_ETHでルーター自身の残高に入れ、次にV4_SWAPを実行してルーターがSETTLEで支払い、TAKE_ALLで購入者に渡します。最後にUNWRAP_WETHでスワップの残りを返します。承認は必要ありません。売却では、V4_SWAPでWETHをルーターに受け取り(TAKEの宛先はルーター自身)、続いて下限を最小値としてUNWRAP_WETHで売り手に渡します。そのため下限は、実際に届くETHの量にかかります。どちらの方向でも、WETHがルーターや取引者の手元に残ることはありません。
プールキーはLaunchHook.poolKeyFor(token, quote)で得られ、そのIDはPoolManagerが受け取るのと同じハッシュです。そのため、マーケットから何も読み取らないうちから、そのマーケットを指定できます。逆にトークンからプールIDを求めるには、poolIdOfToken(token)を使います。
価格に必要なものはすべて、1回の読み取りでそろいます。上のデプロイ表にあるマーケット読み取りコントラクトのLaunchLens.launchesOf(bytes32[] poolIds)です。フック自体にはlaunchesOfがなく、フックに送った呼び出しはリバートします。各LaunchViewには、設定、初期配置の計画、現在の状態、これまでの調達額、プール価格、そのブロックで有効なオープニングタックスが含まれます。手数料はconfig.tradeFeeBps、目標額はconfig.graduationQuote、資産はconfig.quote、オープニングタックスはsnipeTaxBpsとして読み取ります。タックスはconfig.launchedAtから下がっていきます。これはローンチが登録された時刻ではなく、取引が始まった時刻です。決済通貨の小数桁数は、決済トークン自体から取得します。ローンチのどのコントラクトもこの値を公開し直していないため、誤った小数桁で目標額を読むと何桁もずれます。LaunchLensは状態も権限も持たず、返す値はすべてフック自身のビュー関数とPoolManagerから読み直したものです。そのため、LaunchLensを信頼しない呼び出し元でも、この2つのアドレスからすべてのフィールドを再現できます。
マーケットの価格はポジションとプール価格から求め、プールのアクティブな流動性は使いません。カーブのポジションが残っている間、アクティブな流動性は0と読み取られるためです。下限を計算する前にview.config.hookを読みます。フックがスワップのどちら側から差し引くかはフックごとに決まっていて、以下の2つのフックでは異なります。約定を再構成するには、PoolManagerのSwapにTradeFeeCharged、CreatorTaxCharged、SnipeTaxChargedを組み合わせます。Swapだけを見ると、取引者が受け取った量が、フックが差し引いた分だけ多く見えてしまいます。
ウェブのフォームは、Robinhoodのマーケットを取引手数料1%で作成します。これが手数料のすべてで、マーケットは登録時の手数料、取り分、フックをその後も保ちます。取引開始から最初の3秒は、購入にオープニングタックスが上乗せされます。タックスは99%から始まって0まで下がります。まずタックスが差し引かれ、残りの額に取引手数料と、そのローンチ自身のconfig.creatorTaxBpsがかかります。追加クリエイター手数料はクリエイターに全額計上され、どの分配にも回りません。そのため、これを設定したマーケットでは、1回の購入から差し引かれる項目が2つではなく3つになります。
QuoteFeeFactory 0x66112C2c987eD7A366715bD28CCc0DF629813206が作成したマーケットは、QuoteFeeHook 0x6172fD469E900F74eD04b1763daB4D2c2BFB6AcCに紐づきます。このフックは、すべての項目をスワップのETH側から差し引きます。その側が指定側ならbeforeSwapで、そうでなければafterSwapで差し引きます。そのため購入も売却もETHで支払い、オープニングタックスは、購入者が受け取るトークンではなく、購入者が支払う決済通貨から差し引かれます。フックは手数料とタックスを、ファクトリーが指定するトレジャリーに計上します。この宛先はtreasury(poolId)でその都度読み取られます。2026年11月2日からは買い戻し用手数料ルーター0xbc3940Ef143fb3C064a238805A31c18945be1C60で、70%をBuybackLiquidity 0x4B2B547688a7D39512937aD9583315619c3Ce711に、30%をRipplesのトレジャリーに送ります。買い戻しコントラクトは$RIPPLESを購入し、デッドアドレスが保有する流動性としてロックします。2026年11月1日まではQuoteFeeRouter 0xC8842e8196E96Cef0988Cffb144722fDe8a8a684で、70%を買い戻しバーン用コントラクトに送っていました。このファクトリーが作成するローンチでは、config.creatorFeeBpsはすべて0です。Founders Windowの期間中に作成されたマーケットは、意図して設けた例外です。そのファクトリー(0x1C4032F557621D3b77E11748706BEfd26F3b6ef4)はconfig.creatorFeeBpsに30%を書き込み、この分は最初の取引から、マーケットが続く限りクリエイターに支払われます。手数料の残りは同じ買い戻し用手数料ルーターに入ります。Founders Windowの期間中にRipplesでローンチし、7日以内に時価総額$30Kに届けば、DEX ScreenerのEnhanced Token Infoの費用をRipplesが負担します。対象は条件を満たした先着10件のプロジェクトです。既存のFounders Windowの特典とあわせて受けられます。config.creatorTaxBpsは同じ基準額にETHで課され、全額がクリエイターに計上されます。
TokenLaunchFactory 0xE89C1b33B6C2505414667bE7E5084774a72150e1が作成したマーケット($RIPPLESと$KOIを含む)は、LaunchHook 0xaF59944A7d03B914567cb0272b7E588A7aE7AAC4に紐づきます。このフックは、スワップの指定されていない側から差し引きます。入力額を指定する購入では、それはローンチのトークンです。そのため署名する下限は、プールが払い出す量ではなく、差し引き後の純額で計算してください。このフックはローンチの最初の取引から、afterSwapでconfig.creatorFeeBpsに従って手数料を分け、双方の分を、手数料が入った通貨のまま引き出し型のエスクロー台帳に計上します。購入の手数料はローンチのトークン、売却の手数料は決済通貨です。取り分は推測せず、ローンチから読み取ってください。通常の購入では、タックスの対象額はローンチのトークンで、フックはそれをBURN_SINKに計上します。購入者が支払った決済通貨はマーケットに残ります。
スワップ中にフックから資金が出ていくことはありません。フックがあるアドレスに支払うべき額はowed(token, account)で、claim(token)で受け取ります。claimFor(token, account)は同じ支払いを誰でも実行できるようにしたもので、account以外に支払うことはありません。クリエイターがこの収益の送り先を変えるには、setCreatorFeeRecipient(poolId, recipient)を使います。呼び出せるのは現在の受取先のアドレスで、最初はクリエイター自身です。
| 呼び出し | 用途 |
|---|---|
LaunchLens.launchesOf(poolIds) | プールIDごとの設定、ポジション、状態、調達額、価格、オープニングタックス。 |
LaunchHook.snipeTaxBps(poolId) | このブロックで有効なオープニングタックス(bps)。 |
LaunchHook.snipeExempt(poolId, account) | アドレスがオープニングタックスを免除されるかどうか。照合されるのはPoolManagerの呼び出し元なので、ルーターを経由すると取引者はルーターの陰に隠れます。 |
LPLocker.QUOTE() · LPLocker.TOKEN() | このマーケットがペアにしている2つの資産。 |
LPLocker.realQuote() · GRADUATION_QUOTE() | これまでの調達額と、卒業に必要な調達額(決済通貨自身の単位)。 |
LPLocker.graduated() | 調達が締め切られたかどうか。 |
LaunchHook.owed(token, account) | フックがあるアドレスに、1つの資産でまだ支払っていない額(その資産自身の単位)。 |
この世代より前に作成されたマーケットは、プールではなくボンディングカーブです。これらにはbuy、buyFor、sell、sellWithPermit、quoteBuyFor、TRADE_FEE_BPS、QUOTE_DECIMALS()、LAUNCH_STARTがあります。現在のファクトリーが作成するマーケットにはこれらのセレクタがどれもなく、呼び出すとリバートします。両者を見分けるには、マーケットのアドレスでpoolId()を呼び出します。プールを持つマーケットは応答し、カーブは応答しません。
同時ローンチ
カーブが開いている間、NFTの各支払いのうちmintToCurveBpsの分がカーブのリザーブに移り、AllocationVestingにミンターの分として記録されます。マーケットに回った決済通貨は目標額に算入されますが、価格を決める仮想リザーブは変わりません。
ミンターの取り分は、ローンチが卒業して確保分が引き渡されたときに確定します。allocationOf(minter)は、そのミンターがマーケットに回した額に比例します。claimable(minter)はアンロック済みの量を返し、claim()はローンチのクリフ期間とアンロック期間に従ってそれを渡します。受け取る権利はミントしたウォレットにあり、NFTを売却または譲渡しても移りません。finalizeBeneficiary(minter)は、全量を受け取り終えた受益者の記録を閉じます。これによりburnSurplusが、確保残高を超えて残った端数をバーンできます。
エージェントローンチ
AgentTreasuryFactory.createAndLaunch(charter, tp, np, lp, d, runway)はAgentTreasury(エージェントのトレジャリー)をデプロイし、呼び出し元からrunwayを移したうえで、同時ローンチと同じタプルを使ってTokenLaunchFactory.createLinkedLaunchを呼び出させます。そのため、このトレジャリーがローンチのクリエイターになり、すべての取引手数料のクリエイター分はトレジャリーに計上されます。先にRUNWAY_ASSET()をrunwayの額だけファクトリーに承認してください。これはローンチ用ファクトリーの手数料トークンで、それ以外の資産で決済するローンチは拒否されます。
チャーターは(address operator, uint128 dailySpend, uint128 perCallSpend, uint128 dailySell, uint32 dailyMint, address[] payees)で、コンストラクタで固定されます。決済通貨で表す3つの数値はRUNWAY_ASSET建てで、dailySellはローンチのトークン建てです。perCallSpendは0より大きく、dailySpend以下でなければなりません。受取先は最大8件で、同じアドレスは1回までです。
オペレーターの鍵は、buy(amountIn, minAmountOut, deadline)、sell(amountIn, minAmountOut, deadline)、mint(quantity, minRoutedTotal)、pay(to, amount)、note(digest, uri)を呼び出します。取引はこのローンチ自身のプールを通ります。トレジャリーはプールキーを自分のロッカーから読み取るので、取引所を指定する引数はありません。コントラクトから出ていく決済通貨は、購入、ミント、支払いを合算して、dailySpendとperCallSpendの両方で計量されます。売却は約定量ではなく注文量で計量されます。期間はUTCの1日です。remainingToday()は、各上限の残りを返します。
claimFees()とclaimVested()は誰でも呼び出せますが、支払い先はトレジャリーだけです。前者はフック、ロッカー、コレクションからクリエイター分を引き出し、後者はトレジャリー自身のミントで得た分を引き出します。クリエイターに残るのはsetCollectionBaseURI、setCollectionPlaceholderURI、freezeCollectionMetadataだけで、ほかの呼び出しはありません。オーナーも、一時停止も、引き出しもありません。
すべての呼び出しはイベントを発行します。Launched、Bought、Sold、Minted、Paid、FeesClaimed、VestedClaimed、Notedです。このファクトリーがデプロイしたトレジャリーを、同じ読み取りに応答する他のコントラクトと見分けるには、AgentTreasuryFactory.isFromFactory(address)を使います。
@ripplesdotrun/agent-sdkはエージェント自身の鍵でこれらの呼び出しに署名し、@ripplesdotrun/mcpはstdio経由でそれらをモデルに提供します。どちらもエージェントを動かすものではなく、モデルのループ、メモリー、ポリシーは自分で管理します。運用資金、各上限、作品の価格は、上と同じくRUNWAY_ASSET()建てです。
import { createRipplesAgent } from "@ripplesdotrun/agent-sdk";
import { privateKeyToAccount } from "viem/accounts";
const agent = createRipplesAgent({
network: "mainnet",
account: privateKeyToAccount(process.env.RIPPLES_PRIVATE_KEY),
allowMainnet: true,
});
const { treasury, token, market } = await agent.launch({
runway: "0.05",
charter: {
operator: process.env.OPERATOR_ADDRESS, // a key of its own, apart from this wallet
dailySpend: "0.03",
perCallSpend: "0.01",
dailySell: "100000",
dailyMint: 3,
payees: [process.env.PAYEE_ADDRESS],
},
token: { name: "Field Notes", symbol: "NOTES", image: "https://example.com/notes.png" },
collection: { name: "Field Notes Pieces", symbol: "NOTESP", price: "0.01", supply: 88, baseUri: "https://example.com/notes/" },
});
console.log(treasury, token, market);{
"mcpServers": {
"ripples": {
"command": "npx",
"args": ["-y", "@ripplesdotrun/mcp"],
"env": {
"RIPPLES_NETWORK": "mainnet",
"RIPPLES_ALLOW_MAINNET": "1",
"RIPPLES_MAX_PER_CALL": "0.02",
"RIPPLES_PRIVATE_KEY": "…"
}
}
}
}Wake
Wakeとは、クリエイター収益の送り先を、1つのNFTのために作られたコントラクトに向けたマーケットのことです。AgentPayeeFactory.payeeOf(nft, tokenId)は、そのコントラクトが存在する前からアドレスを返し、create(nft, tokenId)がそれをデプロイします。どちらも誰でも呼び出せます。2回目のcreateは同じアドレスを返し、チェーン上でそのトークンを保有するアドレスがない間、createはNoHolder()でリバートします。続いて、マーケットの現在のクリエイター手数料の受取先が、フックのsetCreatorFeeRecipient(poolId, payee)を呼び出します。送り先を変えられるのは現在の受取先だけで、受取用コントラクトにはそれをもう一度変える呼び出しがありません。そのため、この送り先は永続的です。
AgentPayee.release(hook, currency)が支払いを実行し、誰でも呼び出せます。フックが受取用コントラクトのために保管しているcurrency(ETHはゼロアドレス)を引き出し、受取用コントラクトの残高全額をholder()に送ります。holder()はその時点のownerOfを読み取ります。NFTが売れても受取用コントラクトは何も変わらず、次の支払いが新しい保有者に届きます。支払いのたびにReleased(currency, to, amount)が発行されます。
複数の作品で分け合うWakeは、代わりにWakeSplitに向けます。WakeSplitFactory.splitOf(pieces)とcreate(pieces)は、1〜256件の(address nft, uint256 tokenId, uint32 weight)エントリーを受け取ります。並び順はNFT、次にトークンIDの順で、重複と重み0は使えず、作成時にはすべての作品に保有者が必要です。各作品には、届いた額のweight / totalWeightが切り捨てで支払われます。release(hook, currency, index)は1つの作品に支払い、releaseMany(hook, currency, indexes)は1回の引き出しの後に複数の作品に支払います。支払えない作品があってもリバートせず、ReleaseFailedを発行してその作品を飛ばします。読み取りにはdue(currency, index)、holderOf(index)、pieces()を使います。ローンチのフォームはすべての作品に同じ重みを付けますが、コントラクトはどんな重みでも受け付けます。
ripples.runでは、Wakeを残すと、その作品がERC-8004エージェントとしても登録されます。チェーン上ではこれは別の呼び出しで、保有者がERC-8217の紐付けアダプターを通じてregister(0, nft, tokenId, agentURI)で作品を登録します(0はERC-721を表します)。以後、そのエージェントはNFTを保有している人に従います。これらのコントラクトにはどれも、オーナーも設定項目もありません。
APIも同じ読み取りに応答し、どのオリジンからでも利用できます。GET /v1/wake?chain=robinhoodは、最大500件のWakeと直近50件の支払いを返します。GET /v1/wake/{rail}/{collection}/{tokenId}は1つの作品を、GET /v1/wake/by-market/{rail}/{token}はマーケットが支払うWakeを、GET /v1/wake/split/{rail}/{split}は共有Wakeとそのすべての構成要素を返します。Wakeにはpayee、piece、現在のholder、そのWakeに支払うmarkets、owed(いま支払いを実行した場合に送られる額)、paid、lastPayoutが含まれ、共有Wakeの構成要素にはshareも含まれます。金額は最小単位の10進数文字列で、ETHはゼロアドレスで表します。
エージェントも同じように読み取れます。GET /v1/agents?wallet=0x…、GET /v1/agents/{chainId}/{agentId}、8004scanやOpenSeaのリンクを解決するGET /v1/agents/resolve?link=、作品のERC-8004登録ファイルを返すGET /v1/pieces/{rail}/{collection}/{tokenId}/agent.jsonです。railはrobinhoodです。
import { createWakeClient } from "@ripplesdotrun/agent-sdk";
import { privateKeyToAccount } from "viem/accounts";
const wake = createWakeClient({
chain: "robinhood",
account: privateKeyToAccount(process.env.RIPPLES_PRIVATE_KEY),
allowMainnet: true,
});
// Route a market you created to one piece, read it, and pay whoever holds it.
await wake.pinWake({ token, nft, tokenId });
console.log(await wake.wake({ nft, tokenId }));
await wake.releaseWake({ nft, tokenId });
// Or share it across pieces, then pay every part.
const { split } = await wake.shareWake({ token, pieces: [{ nft, tokenId: 1 }, { nft, tokenId: 2 }] });
await wake.releaseSplit({ split });bindTokenは保有している作品をエージェントとして登録し、splitOfとwakeOfSplitは共有Wakeを読み取ります。MCPサーバーは同じ呼び出しをripples_bind_token、ripples_pin_wake、ripples_wake、ripples_release_wake、ripples_share_wake、ripples_wake_split、ripples_release_splitとして提供します。署名する呼び出しにはRIPPLES_PRIVATE_KEYとRIPPLES_ALLOW_MAINNET=1が必要です。送り先の設定は取り消せないため、acknowledge: "permanent"の指定を求めます。
Uniswap v4の流動性プール
LPLocker.realQuote()がGRADUATION_QUOTE()に達し、オープニングタックス期間が終わると、LaunchHook.graduationReady(poolId)がtrueになります。そこから先はLPLocker.settleGraduation()を誰でも呼び出せ、実行されるのは1回だけです。この呼び出しはフック上で調達を締め切り、カーブのポジションを丸ごと閉じ、調達額と連動コレクションが回した分からフルレンジのポジションを開きます。さらに連動ローンチには確保分を引き渡し、残った供給量をすべてバーンします。締め切りから永続的なポジションができるまでの間はスワップが拒否されるため、2つではなく1つのトランザクションで行います。トークンの在庫でペアにできる量を超えた決済通貨は、初値を動かさずにトレジャリーに送られます。
マーケットのアドレスにgraduate()はありません。フックのgraduate(poolId)はそのローンチ自身のロッカーからの呼び出ししか受け付けないため、呼び出すのはsettleGraduation()です。権限は不要で、目標額に達したマーケットは誰でも仕上げられます。調達の締め切りとポジションの確定は1つのトランザクションで行われ、どちらかができなければ全体がリバートします。
ロッカーはUNLOCK_ATに従ってポジションを保有します。永久ロックがunlockに至ることはありません。プール自体は手数料を取らないため、collectFeesで回収されるものは通常ありません。それでもこの呼び出しが残っているのは、ポジションが実在し、プールが手数料を得たときにほかに分ける手段がないからです。この呼び出しは、ファクトリーがロッカーに書き込んだ取り分に従って、ポジションの手数料を分けます。QuoteFeeFactory 0x66112C2c987eD7A366715bD28CCc0DF629813206のロッカーではクリエイターの取り分はなく、ポジションの手数料はファクトリーが記録したトレジャリーに送られます。TokenLaunchFactory 0xE89C1b33B6C2505414667bE7E5084774a72150e1のロッカーでは、70%がクリエイターに、残りがトレジャリーに入ります。卒業したマーケットがクリエイターに支払うのは、フックが計上する取引手数料で、LaunchHook.claimで受け取ります。期限付きでロックされたポジションは、その時刻を過ぎると解消でき、その際は両方の数量に下限を指定します。
ローンチ一覧の読み取り
どのローンチも、それを作成したファクトリーから読み取ります。このチェーンでは2つのトークンローンチ用ファクトリーがマーケットを作成しているため、完全な一覧を作るには両方をページ送りで読み、それぞれに順にisFromFactoryを問い合わせます。トークンローンチのページ送りにはlaunchCount()とlaunches(offset, limit)を、ドロップにはcollectionCount()とcollections(offset, limit)を使います。isFromFactory(address)は、アドレスが似せて作られた別のデプロイではなく、Ripplesから来たものかを確かめる呼び出しです。トークン側ではローンチのロッカーを、NFT側ではコレクションを渡します。RobinhoodではMulticall3が標準のアドレスにあるため、1ページ分のローンチを1回のリクエストで読み取れます。
const count = await client.readContract({
address: tokenLaunchFactory,
abi: TOKEN_FACTORY_ABI,
functionName: "launchCount",
});
const page = await client.readContract({
address: tokenLaunchFactory,
abi: TOKEN_FACTORY_ABI,
functionName: "launches",
args: [0n, count],
});
// page: { token, locker, hook, creator }[]トークンの情報は、トークン自身が返します。name()とsymbol()はERC-20の標準の組で、logo()とdescription()は文字列を返し、socials()はtwitter、telegram、discord、website、farcasterの5つのリンクを返します。どのリンクも空の場合があります。この7つはすべてトークンのコンストラクタで書き込まれ、変更する関数がないため、一度読み取った内容がそのままローンチ時に固定された内容です。ローンチに関する情報は、このアプリが管理するインデックスに一切依存しません。
自分のエージェント
すでにウォレットを持っているエージェントは、公開APIからローンチを読み取り、デプロイ表のエンドポイントか自前のノードからチェーンの状態を読み取ります。そしてブラウザと同じ呼び出しを組み立て、自分の鍵で署名します。ネットワーク手数料も取引の代金も、すべてそのウォレットが支払います。このページのどの部分も、エージェントに代わって署名、支払い、承認をすることはありません。
GET https://api.ripples.run/v1/launches?chain=robinhoodは、Robinhoodのトークンローンチを新しい順に、count、nextOffset、launches配列として返します。各行にはaddress(トークン)、pair(マーケットの取引に使われるプールID)、creator、名前とティッカーを含むtoken、statusが含まれます。/v1/launches/<address>は、トークンアドレスまたはプールIDを指定すると、1件のローンチを現在のマーケットの状態とともに返します。ローンチ一覧と同じように、ファクトリーから直接ローンチを読み取ることもできます。
最初のローンチができるまで、一覧はcount: 0を返します。空のページでも止まらないようにループを書いてください。limitの既定値は50で、最大200まで指定できます。offsetは最新の行から数えます。読み取りはクライアントのアドレスごとに1分あたり120回までで、どの応答にも残り回数がx-ratelimit-remainingとして含まれます。同じ呼び出しをテストネットで試すときは、RIPPLES_APIを自分でビルドした環境のAPIに向けてください。
RobinhoodではマーケットはUniswap v4プールなので、エージェントはviemでフックから価格を求め、Universal Routerを通じてスワップします。使うアドレスはRobinhoodのデプロイ表にあります。ルーターはPermit2を経由して支出するため、決済トークンをPermit2に承認し、さらにPermit2からルーターに承認します。ネットワーク手数料は、同じウォレットからETHで支払います。
import { createPublicClient, createWalletClient, http, parseAbi } from "viem";
import { privateKeyToAccount } from "viem/accounts";
const chain = {
id: 4663,
name: "Robinhood",
nativeCurrency: { name: "ETH", symbol: "ETH", decimals: 18 },
rpcUrls: { default: { http: ["https://rpc.mainnet.chain.robinhood.com"] } },
};
const account = privateKeyToAccount(agentPrivateKey);
const client = createPublicClient({ chain, transport: http() });
const wallet = createWalletClient({ account, chain, transport: http() });
const QUOTE = parseAbi(["function approve(address spender,uint256 value) returns (bool)"]);
const PERMIT2_ABI = parseAbi([
"function approve(address token,address spender,uint160 amount,uint48 expiration)",
]);
const ROUTER = parseAbi([
"function execute(bytes commands,bytes[] inputs,uint256 deadline) payable",
]);
// Price the market off LaunchLens.launchesOf and take the hook's cut off the output
// before signing a floor. The Robinhood "Token trading" section shows that
// arithmetic in full, and where the lens is deployed.
const quoteIn = 10n ** 16n;
const amountOutMinimum = floorFor(quoteIn);
const expiry = Math.floor(Date.now() / 1000) + 3_600;
await wallet.writeContract({ address: quoteToken, abi: QUOTE, functionName: "approve", args: [permit2, quoteIn] });
await wallet.writeContract({ address: permit2, abi: PERMIT2_ABI, functionName: "approve", args: [quoteToken, universalRouter, quoteIn, expiry] });
const hash = await wallet.writeContract({
address: universalRouter,
abi: ROUTER,
functionName: "execute",
args: [
"0x10",
[encodeV4Swap({ poolKey, zeroForOne: quoteIsCurrency0, amountIn: quoteIn, amountOutMinimum })],
BigInt(expiry),
],
});
await client.waitForTransactionReceipt({ hash });