pangu-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +217 -0
- package/dist/accounts-BuD-oCcr.d.mts +206 -0
- package/dist/accounts-BuD-oCcr.d.ts +206 -0
- package/dist/dbc.d.mts +505 -0
- package/dist/dbc.d.ts +505 -0
- package/dist/dbc.js +3003 -0
- package/dist/dbc.js.map +1 -0
- package/dist/dbc.mjs +3027 -0
- package/dist/dbc.mjs.map +1 -0
- package/dist/feed-CY-pcUU2.d.mts +269 -0
- package/dist/feed-Zwu1irEx.d.ts +269 -0
- package/dist/index.d.mts +415 -0
- package/dist/index.d.ts +415 -0
- package/dist/index.js +2809 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2803 -0
- package/dist/index.mjs.map +1 -0
- package/dist/price.d.mts +86 -0
- package/dist/price.d.ts +86 -0
- package/dist/price.js +1554 -0
- package/dist/price.js.map +1 -0
- package/dist/price.mjs +1533 -0
- package/dist/price.mjs.map +1 -0
- package/package.json +104 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { PublicKey, TransactionInstruction, Connection } from '@solana/web3.js';
|
|
2
|
+
import { F as FeedId, S as Sale } from './accounts-BuD-oCcr.mjs';
|
|
3
|
+
|
|
4
|
+
/** The ceiling a sale can put on the curve price, against the real stock's price. */
|
|
5
|
+
interface PriceBandInput {
|
|
6
|
+
/** How far above the live stock price a buy may leave the curve, in basis points. */
|
|
7
|
+
bps: number;
|
|
8
|
+
/** The Pyth feed carrying the stock price, as hex or 32 bytes. */
|
|
9
|
+
priceFeedId: FeedId;
|
|
10
|
+
/** Which Pyth shard to read. Defaults to Pangu's own, the one it refreshes. */
|
|
11
|
+
shard?: number;
|
|
12
|
+
/** How old the published price may be on a buy, in seconds. */
|
|
13
|
+
maxPriceAgeSecs: number;
|
|
14
|
+
/** The widest confidence interval this sale will buy against, in basis points. */
|
|
15
|
+
maxConfBps: number;
|
|
16
|
+
}
|
|
17
|
+
interface CreateSaleInput {
|
|
18
|
+
/** The pool's creator, who signs and pays. */
|
|
19
|
+
issuer: PublicKey;
|
|
20
|
+
/** The DBC hook pool, which must already exist. */
|
|
21
|
+
pool: PublicKey;
|
|
22
|
+
mint: PublicKey;
|
|
23
|
+
/** Most raw token units one wallet may hold, net of sells. */
|
|
24
|
+
cap: bigint;
|
|
25
|
+
accessMode: number;
|
|
26
|
+
/** Access mode 2 only: the verifier's credential and the schema that counts. */
|
|
27
|
+
credential?: PublicKey;
|
|
28
|
+
schema?: PublicKey;
|
|
29
|
+
band?: PriceBandInput;
|
|
30
|
+
/**
|
|
31
|
+
* The DBC launch template this pool was opened on. Needed in every mode: the
|
|
32
|
+
* program reads the fee mode off it before it will open a sale at all.
|
|
33
|
+
*/
|
|
34
|
+
dbcConfig: PublicKey;
|
|
35
|
+
/**
|
|
36
|
+
* The token buyers pay in, the one the launch template names. Needed in every
|
|
37
|
+
* mode: the program stores it, refuses one the issuer can freeze, and on a
|
|
38
|
+
* banded sale reads its decimals and refuses wrapped SOL.
|
|
39
|
+
*/
|
|
40
|
+
quoteMint: PublicKey;
|
|
41
|
+
/**
|
|
42
|
+
* Unix seconds at which the offering period ends and every rule lifts. Zero,
|
|
43
|
+
* the default, means no end: the rules hold until graduation.
|
|
44
|
+
*/
|
|
45
|
+
endsAt?: number;
|
|
46
|
+
}
|
|
47
|
+
interface OpenBuyerRecordInput {
|
|
48
|
+
wallet: PublicKey;
|
|
49
|
+
mint: PublicKey;
|
|
50
|
+
}
|
|
51
|
+
interface ApproveBuyerInput {
|
|
52
|
+
issuer: PublicKey;
|
|
53
|
+
mint: PublicKey;
|
|
54
|
+
wallet: PublicKey;
|
|
55
|
+
}
|
|
56
|
+
type RevokeBuyerInput = ApproveBuyerInput;
|
|
57
|
+
interface CloseBuyerRecordInput {
|
|
58
|
+
wallet: PublicKey;
|
|
59
|
+
mint: PublicKey;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Opens a sale: stores the rules for a mint and publishes the account list the
|
|
63
|
+
* transfer hook will be called with.
|
|
64
|
+
*
|
|
65
|
+
* The pool must already exist and the signer must be its creator. The pool's
|
|
66
|
+
* launch template and the token buyers pay in are needed in every mode,
|
|
67
|
+
* because the program reads the fee mode, the curve's supply and the paying
|
|
68
|
+
* token before it will open a sale at all. Access mode 2 needs a credential and
|
|
69
|
+
* a schema; a band needs the feed. Every input that could never pass on chain
|
|
70
|
+
* and can be judged without reading the chain is refused here, with the same
|
|
71
|
+
* limits the program holds.
|
|
72
|
+
*
|
|
73
|
+
* Throws PanguInputError. Sends nothing.
|
|
74
|
+
*/
|
|
75
|
+
declare function createSaleInstruction(input: CreateSaleInput): TransactionInstruction;
|
|
76
|
+
/**
|
|
77
|
+
* Opens a wallet's own record, so a later buy has somewhere to count against.
|
|
78
|
+
* A transfer hook cannot create accounts, which is why this comes first. Safe to
|
|
79
|
+
* send twice.
|
|
80
|
+
*/
|
|
81
|
+
declare function openBuyerRecordInstruction(input: OpenBuyerRecordInput): TransactionInstruction;
|
|
82
|
+
/**
|
|
83
|
+
* Puts a wallet on the issuer's approved list, in access mode 1. Creates the
|
|
84
|
+
* record if the wallet never opened one. Only the issuer named in the rules can
|
|
85
|
+
* send it, and it never resets what the wallet has already bought.
|
|
86
|
+
*/
|
|
87
|
+
declare function approveBuyerInstruction(input: ApproveBuyerInput): TransactionInstruction;
|
|
88
|
+
/**
|
|
89
|
+
* Takes a wallet off the approved list. The wallet can still sell what it holds,
|
|
90
|
+
* which is why the record is not closed.
|
|
91
|
+
*/
|
|
92
|
+
declare function revokeBuyerInstruction(input: RevokeBuyerInput): TransactionInstruction;
|
|
93
|
+
/**
|
|
94
|
+
* Closes a wallet's record and returns the rent.
|
|
95
|
+
*
|
|
96
|
+
* A record with nothing left in it closes at any time, mid-sale included, since
|
|
97
|
+
* it holds no count anybody could lose. A record that still counts tokens waits
|
|
98
|
+
* for the sale to finish, which is the moment the mint stops naming Pangu as its
|
|
99
|
+
* hook or the offering period in the rules ends; until then the program answers
|
|
100
|
+
* SaleStillRunning. Reopening later is safe, and in the issuer-list mode the
|
|
101
|
+
* wallet has to be approved again.
|
|
102
|
+
*/
|
|
103
|
+
declare function closeBuyerRecordInstruction(input: CloseBuyerRecordInput): TransactionInstruction;
|
|
104
|
+
|
|
105
|
+
/** One of the program's own refusals, matched out of a transaction's logs. */
|
|
106
|
+
interface PanguError {
|
|
107
|
+
code: number;
|
|
108
|
+
name: PanguErrorName;
|
|
109
|
+
/** The program's own message, as written in the IDL. */
|
|
110
|
+
message: string;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Every error the program can return, read straight out of the IDL so a new one
|
|
114
|
+
* cannot be missed here.
|
|
115
|
+
*/
|
|
116
|
+
declare const PANGU_ERRORS: readonly PanguError[];
|
|
117
|
+
/**
|
|
118
|
+
* Finds Pangu's own refusal in a transaction's logs.
|
|
119
|
+
*
|
|
120
|
+
* Covers the two shapes a refusal arrives in: the Anchor line that names the
|
|
121
|
+
* error, and the bare code the runtime prints when the program fails.
|
|
122
|
+
*
|
|
123
|
+
* A code only means something next to the program that raised it: DBC's 6002
|
|
124
|
+
* is its slippage refusal, and Pangu's 6002 is WrongMint. So the lines are
|
|
125
|
+
* walked as the runtime prints them, each "invoke" opening a frame and each
|
|
126
|
+
* "success" or "failed" closing one, and the first "failed" names the program
|
|
127
|
+
* the transaction really stopped in. The runtime aborts on the innermost
|
|
128
|
+
* failure, so every later "failed" is only a caller passing it up. A refusal is
|
|
129
|
+
* Pangu's only when that program is Pangu, and only lines printed inside
|
|
130
|
+
* Pangu's own frame are read for its name. Pangu having run earlier in the same
|
|
131
|
+
* transaction, which it does on every first buy when it opens the buyer
|
|
132
|
+
* record, counts for nothing.
|
|
133
|
+
*
|
|
134
|
+
* A fragment that names no program at all is read as Pangu's, because there is
|
|
135
|
+
* nobody else it could belong to.
|
|
136
|
+
*
|
|
137
|
+
* Does not cover: errors Pangu never raises, such as Anchor's own account checks
|
|
138
|
+
* or the token program's, and logs cut short before the failing line when the
|
|
139
|
+
* frame that failed is not Pangu's. Those come back as null and the caller should
|
|
140
|
+
* show the raw message.
|
|
141
|
+
*/
|
|
142
|
+
declare function panguErrorFromLogs(logs: string[]): PanguError | null;
|
|
143
|
+
/**
|
|
144
|
+
* One sentence a buyer can read for each refusal.
|
|
145
|
+
*
|
|
146
|
+
* Every name in the IDL has an entry. A name that is not Pangu's gets a sentence
|
|
147
|
+
* that says so, rather than a guess.
|
|
148
|
+
*/
|
|
149
|
+
declare const EXPLANATIONS: {
|
|
150
|
+
readonly NotTransferring: "This token only moves through a real transfer, and this was not one.";
|
|
151
|
+
readonly ReceivingAccountOwnerCanChange: "The account you are buying into could be handed to someone else later, so the sale will not send tokens to it. Use the wallet's ordinary holding account for this token, the one your wallet app makes by itself.";
|
|
152
|
+
readonly WrongMint: "That account belongs to a different token than this sale.";
|
|
153
|
+
readonly WrongBuyerRecord: "The buyer record sent with this transfer belongs to another wallet.";
|
|
154
|
+
readonly BuyerRecordMissing: "This wallet has no record in the sale yet. Open one first, then buy.";
|
|
155
|
+
readonly NotApproved: "The issuer has not approved this wallet to buy in this sale.";
|
|
156
|
+
readonly CredentialInvalid: "This wallet does not carry a valid approval from the verifier this sale trusts.";
|
|
157
|
+
readonly CredentialExpired: "The verifier's approval for this wallet has run out.";
|
|
158
|
+
readonly CredentialSignerNotAuthorized: "The key that signed this wallet's approval is no longer allowed to sign for that verifier.";
|
|
159
|
+
readonly OverCap: "This purchase would take your wallet past the limit for this sale.";
|
|
160
|
+
readonly WalletToWalletDuringSale: "This token cannot be sent from one wallet to another while the sale is running.";
|
|
161
|
+
readonly PriceStale: "The stock price this sale checks against is too old to use. Refresh it and try again. Outside market hours there is no fresh price to get, so the sale stays shut until the market opens.";
|
|
162
|
+
readonly PriceOutsideBand: "This purchase would push the price too far above the real stock price.";
|
|
163
|
+
readonly WrongPriceAccount: "The price account sent with this transfer is not the one this sale names.";
|
|
164
|
+
readonly PriceNotFullyVerified: "The price update has not been signed by two thirds of Pyth's guardians, and this sale will not price against a half signed number.";
|
|
165
|
+
readonly PriceTooUncertain: "Pyth's own publishers disagree about this stock's price by more than this sale allows, so there is no ceiling worth measuring against right now.";
|
|
166
|
+
readonly NotPoolCreator: "Only the wallet that created the pool can open its sale.";
|
|
167
|
+
readonly NotAHookPool: "That account is not a Meteora bonding curve pool of the kind Pangu works with.";
|
|
168
|
+
readonly HookProgramMismatch: "This token does not name Pangu as its transfer hook, so Pangu cannot hold its rules.";
|
|
169
|
+
readonly MintAuthorityStillSet: "This token can still be minted, so Pangu will not open a sale on it. Launch it with the minting power revoked.";
|
|
170
|
+
readonly WrongLaunchTemplate: "That launch template is not the one this pool was opened on.";
|
|
171
|
+
readonly FeesNotInQuoteToken: "This sale's template collects fees in the sale token, and Pangu only accepts templates that collect them in the paying token.";
|
|
172
|
+
readonly ZeroCap: "A sale needs a per-wallet limit above zero.";
|
|
173
|
+
readonly InvalidAccessMode: "That access mode does not exist, or the settings do not match the mode chosen.";
|
|
174
|
+
readonly InvalidBand: "The price band settings are incomplete or out of range.";
|
|
175
|
+
readonly SaleStillRunning: "The sale is still running and this record still counts tokens, so it cannot be closed yet. Sell them back or wait for the sale to finish.";
|
|
176
|
+
readonly NotIssuer: "Only the issuer of this sale can do that.";
|
|
177
|
+
readonly MathOverflow: "The sale's counters cannot go any higher.";
|
|
178
|
+
readonly WrongLayoutVersion: "These sale rules were written by an older build of the program, so this build will not act on them.";
|
|
179
|
+
readonly BandNeedsDollarQuote: "A price ceiling needs buyers to pay in a dollar token the program recognises (USDC, or on devnet the demo dollar). Turn the ceiling off, or pick a dollar paying token.";
|
|
180
|
+
readonly IssuerControlsPayingToken: "You hold the freeze authority of the token buyers pay in, which would let you stop sellers being paid. Pick a paying token whose freeze authority is not yours.";
|
|
181
|
+
readonly CapCoversWholeSale: "The per-wallet limit is as large as everything the curve sells, so one wallet could buy the whole sale. Set a limit below the curve's supply.";
|
|
182
|
+
readonly EndInThePast: "The end of the offering period has to be later than now. Pick a future time, or leave it at zero for no end.";
|
|
183
|
+
};
|
|
184
|
+
type PanguErrorName = keyof typeof EXPLANATIONS;
|
|
185
|
+
declare function explainPanguError(name: PanguErrorName | string): string;
|
|
186
|
+
|
|
187
|
+
/** What one Pyth price feed account carries. */
|
|
188
|
+
interface PriceUpdate {
|
|
189
|
+
/** The key Pyth's receiver program recorded as having written this update. */
|
|
190
|
+
writeAuthority: PublicKey;
|
|
191
|
+
/** True when two thirds of the Wormhole guardians signed it. */
|
|
192
|
+
fullyVerified: boolean;
|
|
193
|
+
/** Lowercase hex, no prefix, matching Sale.priceFeedId. */
|
|
194
|
+
feedId: string;
|
|
195
|
+
/** Pyth's whole number. The dollar price is this times ten to the exponent. */
|
|
196
|
+
price: bigint;
|
|
197
|
+
/** How wide the publishers' disagreement is, in the same units as the price. */
|
|
198
|
+
conf: bigint;
|
|
199
|
+
exponent: number;
|
|
200
|
+
/** Unix seconds Pyth's publishers agreed this price. */
|
|
201
|
+
publishTime: number;
|
|
202
|
+
prevPublishTime: number;
|
|
203
|
+
/** The Solana slot the update was posted in. */
|
|
204
|
+
postedSlot: bigint;
|
|
205
|
+
}
|
|
206
|
+
/** A sale's live stock price, and whether a buy could use it right now. */
|
|
207
|
+
interface PriceReading {
|
|
208
|
+
/** The Pyth price feed account this sale reads. */
|
|
209
|
+
address: PublicKey;
|
|
210
|
+
/** The stock price in dollars, scaled by 1e18. Zero when there is none. */
|
|
211
|
+
price: bigint;
|
|
212
|
+
/** The same price as an ordinary number, for display. */
|
|
213
|
+
priceDollars: number;
|
|
214
|
+
/** Unix seconds the price was published, or zero when unknown. */
|
|
215
|
+
publishTime: number;
|
|
216
|
+
/** How old it is, in seconds, against the chain's own clock. */
|
|
217
|
+
ageSecs: number;
|
|
218
|
+
/** Pyth's confidence interval as basis points of the price, rounded up. */
|
|
219
|
+
confBps: number;
|
|
220
|
+
/** True when two thirds of the Wormhole guardians signed the update. */
|
|
221
|
+
fullyVerified: boolean;
|
|
222
|
+
usable: boolean;
|
|
223
|
+
/** Why it cannot be used, as the program's own refusal. Null when usable. */
|
|
224
|
+
error: PanguErrorName | null;
|
|
225
|
+
/** One sentence for a buyer. Null when usable. */
|
|
226
|
+
reason: string | null;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Reads a Pyth price feed account without the Pyth package.
|
|
230
|
+
*
|
|
231
|
+
* Every offset is the one `programs/pangu/src/price.rs` reads on chain through
|
|
232
|
+
* Pyth's own Rust SDK, checked against the real account bytes in
|
|
233
|
+
* docs/measurements/price-band-pyth.md. Safe in a browser: it is arithmetic
|
|
234
|
+
* over bytes the caller already has, and it pulls in nothing.
|
|
235
|
+
*
|
|
236
|
+
* A partly verified update is decoded rather than refused, because the caller
|
|
237
|
+
* has to be able to say which of the two it is. `fullyVerified` is the answer,
|
|
238
|
+
* and `readPrice` refuses on it.
|
|
239
|
+
*
|
|
240
|
+
* Throws PanguInputError for anything that is not a well formed price update.
|
|
241
|
+
*/
|
|
242
|
+
declare function decodePriceUpdate(data: Uint8Array): PriceUpdate;
|
|
243
|
+
/**
|
|
244
|
+
* The live stock price a banded sale checks against, read straight off the
|
|
245
|
+
* chain, plus whether a buy could use it this moment.
|
|
246
|
+
*
|
|
247
|
+
* Runs the hook's own checks in the hook's own order: the account sits at the
|
|
248
|
+
* one address this sale's shard and feed id produce, it is owned by Pyth's
|
|
249
|
+
* receiver program, its bytes are a price update, two thirds of the guardians
|
|
250
|
+
* signed it, the feed inside it is this sale's feed, it is no older than the
|
|
251
|
+
* sale allows, it was not published in the future, the price is above zero, and
|
|
252
|
+
* Pyth's confidence interval is inside the sale's limit. Any doubt comes back
|
|
253
|
+
* as `usable: false` with the program's own error name, because that is what
|
|
254
|
+
* the buy would hit.
|
|
255
|
+
*
|
|
256
|
+
* The age is measured against the chain's own clock, the same clock the hook
|
|
257
|
+
* compares against, rather than this machine's. If the Clock sysvar cannot be
|
|
258
|
+
* read, this machine's clock stands in.
|
|
259
|
+
*
|
|
260
|
+
* Does not cover the Wormhole guardian signatures. Nothing can redo those from
|
|
261
|
+
* the account's bytes: what stands in for them is the owner check, because only
|
|
262
|
+
* Pyth's receiver program can write an account it owns and it checks them
|
|
263
|
+
* first.
|
|
264
|
+
*
|
|
265
|
+
* Throws PanguInputError when the sale has no price band.
|
|
266
|
+
*/
|
|
267
|
+
declare function readPrice(connection: Connection, sale: Sale): Promise<PriceReading>;
|
|
268
|
+
|
|
269
|
+
export { type ApproveBuyerInput as A, type CloseBuyerRecordInput as C, type OpenBuyerRecordInput as O, type PriceBandInput as P, type RevokeBuyerInput as R, type PanguErrorName as a, type PriceReading as b, type CreateSaleInput as c, PANGU_ERRORS as d, type PanguError as e, type PriceUpdate as f, approveBuyerInstruction as g, closeBuyerRecordInstruction as h, createSaleInstruction as i, decodePriceUpdate as j, explainPanguError as k, revokeBuyerInstruction as l, openBuyerRecordInstruction as o, panguErrorFromLogs as p, readPrice as r };
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { PublicKey, TransactionInstruction, Connection } from '@solana/web3.js';
|
|
2
|
+
import { F as FeedId, S as Sale } from './accounts-BuD-oCcr.js';
|
|
3
|
+
|
|
4
|
+
/** The ceiling a sale can put on the curve price, against the real stock's price. */
|
|
5
|
+
interface PriceBandInput {
|
|
6
|
+
/** How far above the live stock price a buy may leave the curve, in basis points. */
|
|
7
|
+
bps: number;
|
|
8
|
+
/** The Pyth feed carrying the stock price, as hex or 32 bytes. */
|
|
9
|
+
priceFeedId: FeedId;
|
|
10
|
+
/** Which Pyth shard to read. Defaults to Pangu's own, the one it refreshes. */
|
|
11
|
+
shard?: number;
|
|
12
|
+
/** How old the published price may be on a buy, in seconds. */
|
|
13
|
+
maxPriceAgeSecs: number;
|
|
14
|
+
/** The widest confidence interval this sale will buy against, in basis points. */
|
|
15
|
+
maxConfBps: number;
|
|
16
|
+
}
|
|
17
|
+
interface CreateSaleInput {
|
|
18
|
+
/** The pool's creator, who signs and pays. */
|
|
19
|
+
issuer: PublicKey;
|
|
20
|
+
/** The DBC hook pool, which must already exist. */
|
|
21
|
+
pool: PublicKey;
|
|
22
|
+
mint: PublicKey;
|
|
23
|
+
/** Most raw token units one wallet may hold, net of sells. */
|
|
24
|
+
cap: bigint;
|
|
25
|
+
accessMode: number;
|
|
26
|
+
/** Access mode 2 only: the verifier's credential and the schema that counts. */
|
|
27
|
+
credential?: PublicKey;
|
|
28
|
+
schema?: PublicKey;
|
|
29
|
+
band?: PriceBandInput;
|
|
30
|
+
/**
|
|
31
|
+
* The DBC launch template this pool was opened on. Needed in every mode: the
|
|
32
|
+
* program reads the fee mode off it before it will open a sale at all.
|
|
33
|
+
*/
|
|
34
|
+
dbcConfig: PublicKey;
|
|
35
|
+
/**
|
|
36
|
+
* The token buyers pay in, the one the launch template names. Needed in every
|
|
37
|
+
* mode: the program stores it, refuses one the issuer can freeze, and on a
|
|
38
|
+
* banded sale reads its decimals and refuses wrapped SOL.
|
|
39
|
+
*/
|
|
40
|
+
quoteMint: PublicKey;
|
|
41
|
+
/**
|
|
42
|
+
* Unix seconds at which the offering period ends and every rule lifts. Zero,
|
|
43
|
+
* the default, means no end: the rules hold until graduation.
|
|
44
|
+
*/
|
|
45
|
+
endsAt?: number;
|
|
46
|
+
}
|
|
47
|
+
interface OpenBuyerRecordInput {
|
|
48
|
+
wallet: PublicKey;
|
|
49
|
+
mint: PublicKey;
|
|
50
|
+
}
|
|
51
|
+
interface ApproveBuyerInput {
|
|
52
|
+
issuer: PublicKey;
|
|
53
|
+
mint: PublicKey;
|
|
54
|
+
wallet: PublicKey;
|
|
55
|
+
}
|
|
56
|
+
type RevokeBuyerInput = ApproveBuyerInput;
|
|
57
|
+
interface CloseBuyerRecordInput {
|
|
58
|
+
wallet: PublicKey;
|
|
59
|
+
mint: PublicKey;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Opens a sale: stores the rules for a mint and publishes the account list the
|
|
63
|
+
* transfer hook will be called with.
|
|
64
|
+
*
|
|
65
|
+
* The pool must already exist and the signer must be its creator. The pool's
|
|
66
|
+
* launch template and the token buyers pay in are needed in every mode,
|
|
67
|
+
* because the program reads the fee mode, the curve's supply and the paying
|
|
68
|
+
* token before it will open a sale at all. Access mode 2 needs a credential and
|
|
69
|
+
* a schema; a band needs the feed. Every input that could never pass on chain
|
|
70
|
+
* and can be judged without reading the chain is refused here, with the same
|
|
71
|
+
* limits the program holds.
|
|
72
|
+
*
|
|
73
|
+
* Throws PanguInputError. Sends nothing.
|
|
74
|
+
*/
|
|
75
|
+
declare function createSaleInstruction(input: CreateSaleInput): TransactionInstruction;
|
|
76
|
+
/**
|
|
77
|
+
* Opens a wallet's own record, so a later buy has somewhere to count against.
|
|
78
|
+
* A transfer hook cannot create accounts, which is why this comes first. Safe to
|
|
79
|
+
* send twice.
|
|
80
|
+
*/
|
|
81
|
+
declare function openBuyerRecordInstruction(input: OpenBuyerRecordInput): TransactionInstruction;
|
|
82
|
+
/**
|
|
83
|
+
* Puts a wallet on the issuer's approved list, in access mode 1. Creates the
|
|
84
|
+
* record if the wallet never opened one. Only the issuer named in the rules can
|
|
85
|
+
* send it, and it never resets what the wallet has already bought.
|
|
86
|
+
*/
|
|
87
|
+
declare function approveBuyerInstruction(input: ApproveBuyerInput): TransactionInstruction;
|
|
88
|
+
/**
|
|
89
|
+
* Takes a wallet off the approved list. The wallet can still sell what it holds,
|
|
90
|
+
* which is why the record is not closed.
|
|
91
|
+
*/
|
|
92
|
+
declare function revokeBuyerInstruction(input: RevokeBuyerInput): TransactionInstruction;
|
|
93
|
+
/**
|
|
94
|
+
* Closes a wallet's record and returns the rent.
|
|
95
|
+
*
|
|
96
|
+
* A record with nothing left in it closes at any time, mid-sale included, since
|
|
97
|
+
* it holds no count anybody could lose. A record that still counts tokens waits
|
|
98
|
+
* for the sale to finish, which is the moment the mint stops naming Pangu as its
|
|
99
|
+
* hook or the offering period in the rules ends; until then the program answers
|
|
100
|
+
* SaleStillRunning. Reopening later is safe, and in the issuer-list mode the
|
|
101
|
+
* wallet has to be approved again.
|
|
102
|
+
*/
|
|
103
|
+
declare function closeBuyerRecordInstruction(input: CloseBuyerRecordInput): TransactionInstruction;
|
|
104
|
+
|
|
105
|
+
/** One of the program's own refusals, matched out of a transaction's logs. */
|
|
106
|
+
interface PanguError {
|
|
107
|
+
code: number;
|
|
108
|
+
name: PanguErrorName;
|
|
109
|
+
/** The program's own message, as written in the IDL. */
|
|
110
|
+
message: string;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Every error the program can return, read straight out of the IDL so a new one
|
|
114
|
+
* cannot be missed here.
|
|
115
|
+
*/
|
|
116
|
+
declare const PANGU_ERRORS: readonly PanguError[];
|
|
117
|
+
/**
|
|
118
|
+
* Finds Pangu's own refusal in a transaction's logs.
|
|
119
|
+
*
|
|
120
|
+
* Covers the two shapes a refusal arrives in: the Anchor line that names the
|
|
121
|
+
* error, and the bare code the runtime prints when the program fails.
|
|
122
|
+
*
|
|
123
|
+
* A code only means something next to the program that raised it: DBC's 6002
|
|
124
|
+
* is its slippage refusal, and Pangu's 6002 is WrongMint. So the lines are
|
|
125
|
+
* walked as the runtime prints them, each "invoke" opening a frame and each
|
|
126
|
+
* "success" or "failed" closing one, and the first "failed" names the program
|
|
127
|
+
* the transaction really stopped in. The runtime aborts on the innermost
|
|
128
|
+
* failure, so every later "failed" is only a caller passing it up. A refusal is
|
|
129
|
+
* Pangu's only when that program is Pangu, and only lines printed inside
|
|
130
|
+
* Pangu's own frame are read for its name. Pangu having run earlier in the same
|
|
131
|
+
* transaction, which it does on every first buy when it opens the buyer
|
|
132
|
+
* record, counts for nothing.
|
|
133
|
+
*
|
|
134
|
+
* A fragment that names no program at all is read as Pangu's, because there is
|
|
135
|
+
* nobody else it could belong to.
|
|
136
|
+
*
|
|
137
|
+
* Does not cover: errors Pangu never raises, such as Anchor's own account checks
|
|
138
|
+
* or the token program's, and logs cut short before the failing line when the
|
|
139
|
+
* frame that failed is not Pangu's. Those come back as null and the caller should
|
|
140
|
+
* show the raw message.
|
|
141
|
+
*/
|
|
142
|
+
declare function panguErrorFromLogs(logs: string[]): PanguError | null;
|
|
143
|
+
/**
|
|
144
|
+
* One sentence a buyer can read for each refusal.
|
|
145
|
+
*
|
|
146
|
+
* Every name in the IDL has an entry. A name that is not Pangu's gets a sentence
|
|
147
|
+
* that says so, rather than a guess.
|
|
148
|
+
*/
|
|
149
|
+
declare const EXPLANATIONS: {
|
|
150
|
+
readonly NotTransferring: "This token only moves through a real transfer, and this was not one.";
|
|
151
|
+
readonly ReceivingAccountOwnerCanChange: "The account you are buying into could be handed to someone else later, so the sale will not send tokens to it. Use the wallet's ordinary holding account for this token, the one your wallet app makes by itself.";
|
|
152
|
+
readonly WrongMint: "That account belongs to a different token than this sale.";
|
|
153
|
+
readonly WrongBuyerRecord: "The buyer record sent with this transfer belongs to another wallet.";
|
|
154
|
+
readonly BuyerRecordMissing: "This wallet has no record in the sale yet. Open one first, then buy.";
|
|
155
|
+
readonly NotApproved: "The issuer has not approved this wallet to buy in this sale.";
|
|
156
|
+
readonly CredentialInvalid: "This wallet does not carry a valid approval from the verifier this sale trusts.";
|
|
157
|
+
readonly CredentialExpired: "The verifier's approval for this wallet has run out.";
|
|
158
|
+
readonly CredentialSignerNotAuthorized: "The key that signed this wallet's approval is no longer allowed to sign for that verifier.";
|
|
159
|
+
readonly OverCap: "This purchase would take your wallet past the limit for this sale.";
|
|
160
|
+
readonly WalletToWalletDuringSale: "This token cannot be sent from one wallet to another while the sale is running.";
|
|
161
|
+
readonly PriceStale: "The stock price this sale checks against is too old to use. Refresh it and try again. Outside market hours there is no fresh price to get, so the sale stays shut until the market opens.";
|
|
162
|
+
readonly PriceOutsideBand: "This purchase would push the price too far above the real stock price.";
|
|
163
|
+
readonly WrongPriceAccount: "The price account sent with this transfer is not the one this sale names.";
|
|
164
|
+
readonly PriceNotFullyVerified: "The price update has not been signed by two thirds of Pyth's guardians, and this sale will not price against a half signed number.";
|
|
165
|
+
readonly PriceTooUncertain: "Pyth's own publishers disagree about this stock's price by more than this sale allows, so there is no ceiling worth measuring against right now.";
|
|
166
|
+
readonly NotPoolCreator: "Only the wallet that created the pool can open its sale.";
|
|
167
|
+
readonly NotAHookPool: "That account is not a Meteora bonding curve pool of the kind Pangu works with.";
|
|
168
|
+
readonly HookProgramMismatch: "This token does not name Pangu as its transfer hook, so Pangu cannot hold its rules.";
|
|
169
|
+
readonly MintAuthorityStillSet: "This token can still be minted, so Pangu will not open a sale on it. Launch it with the minting power revoked.";
|
|
170
|
+
readonly WrongLaunchTemplate: "That launch template is not the one this pool was opened on.";
|
|
171
|
+
readonly FeesNotInQuoteToken: "This sale's template collects fees in the sale token, and Pangu only accepts templates that collect them in the paying token.";
|
|
172
|
+
readonly ZeroCap: "A sale needs a per-wallet limit above zero.";
|
|
173
|
+
readonly InvalidAccessMode: "That access mode does not exist, or the settings do not match the mode chosen.";
|
|
174
|
+
readonly InvalidBand: "The price band settings are incomplete or out of range.";
|
|
175
|
+
readonly SaleStillRunning: "The sale is still running and this record still counts tokens, so it cannot be closed yet. Sell them back or wait for the sale to finish.";
|
|
176
|
+
readonly NotIssuer: "Only the issuer of this sale can do that.";
|
|
177
|
+
readonly MathOverflow: "The sale's counters cannot go any higher.";
|
|
178
|
+
readonly WrongLayoutVersion: "These sale rules were written by an older build of the program, so this build will not act on them.";
|
|
179
|
+
readonly BandNeedsDollarQuote: "A price ceiling needs buyers to pay in a dollar token the program recognises (USDC, or on devnet the demo dollar). Turn the ceiling off, or pick a dollar paying token.";
|
|
180
|
+
readonly IssuerControlsPayingToken: "You hold the freeze authority of the token buyers pay in, which would let you stop sellers being paid. Pick a paying token whose freeze authority is not yours.";
|
|
181
|
+
readonly CapCoversWholeSale: "The per-wallet limit is as large as everything the curve sells, so one wallet could buy the whole sale. Set a limit below the curve's supply.";
|
|
182
|
+
readonly EndInThePast: "The end of the offering period has to be later than now. Pick a future time, or leave it at zero for no end.";
|
|
183
|
+
};
|
|
184
|
+
type PanguErrorName = keyof typeof EXPLANATIONS;
|
|
185
|
+
declare function explainPanguError(name: PanguErrorName | string): string;
|
|
186
|
+
|
|
187
|
+
/** What one Pyth price feed account carries. */
|
|
188
|
+
interface PriceUpdate {
|
|
189
|
+
/** The key Pyth's receiver program recorded as having written this update. */
|
|
190
|
+
writeAuthority: PublicKey;
|
|
191
|
+
/** True when two thirds of the Wormhole guardians signed it. */
|
|
192
|
+
fullyVerified: boolean;
|
|
193
|
+
/** Lowercase hex, no prefix, matching Sale.priceFeedId. */
|
|
194
|
+
feedId: string;
|
|
195
|
+
/** Pyth's whole number. The dollar price is this times ten to the exponent. */
|
|
196
|
+
price: bigint;
|
|
197
|
+
/** How wide the publishers' disagreement is, in the same units as the price. */
|
|
198
|
+
conf: bigint;
|
|
199
|
+
exponent: number;
|
|
200
|
+
/** Unix seconds Pyth's publishers agreed this price. */
|
|
201
|
+
publishTime: number;
|
|
202
|
+
prevPublishTime: number;
|
|
203
|
+
/** The Solana slot the update was posted in. */
|
|
204
|
+
postedSlot: bigint;
|
|
205
|
+
}
|
|
206
|
+
/** A sale's live stock price, and whether a buy could use it right now. */
|
|
207
|
+
interface PriceReading {
|
|
208
|
+
/** The Pyth price feed account this sale reads. */
|
|
209
|
+
address: PublicKey;
|
|
210
|
+
/** The stock price in dollars, scaled by 1e18. Zero when there is none. */
|
|
211
|
+
price: bigint;
|
|
212
|
+
/** The same price as an ordinary number, for display. */
|
|
213
|
+
priceDollars: number;
|
|
214
|
+
/** Unix seconds the price was published, or zero when unknown. */
|
|
215
|
+
publishTime: number;
|
|
216
|
+
/** How old it is, in seconds, against the chain's own clock. */
|
|
217
|
+
ageSecs: number;
|
|
218
|
+
/** Pyth's confidence interval as basis points of the price, rounded up. */
|
|
219
|
+
confBps: number;
|
|
220
|
+
/** True when two thirds of the Wormhole guardians signed the update. */
|
|
221
|
+
fullyVerified: boolean;
|
|
222
|
+
usable: boolean;
|
|
223
|
+
/** Why it cannot be used, as the program's own refusal. Null when usable. */
|
|
224
|
+
error: PanguErrorName | null;
|
|
225
|
+
/** One sentence for a buyer. Null when usable. */
|
|
226
|
+
reason: string | null;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Reads a Pyth price feed account without the Pyth package.
|
|
230
|
+
*
|
|
231
|
+
* Every offset is the one `programs/pangu/src/price.rs` reads on chain through
|
|
232
|
+
* Pyth's own Rust SDK, checked against the real account bytes in
|
|
233
|
+
* docs/measurements/price-band-pyth.md. Safe in a browser: it is arithmetic
|
|
234
|
+
* over bytes the caller already has, and it pulls in nothing.
|
|
235
|
+
*
|
|
236
|
+
* A partly verified update is decoded rather than refused, because the caller
|
|
237
|
+
* has to be able to say which of the two it is. `fullyVerified` is the answer,
|
|
238
|
+
* and `readPrice` refuses on it.
|
|
239
|
+
*
|
|
240
|
+
* Throws PanguInputError for anything that is not a well formed price update.
|
|
241
|
+
*/
|
|
242
|
+
declare function decodePriceUpdate(data: Uint8Array): PriceUpdate;
|
|
243
|
+
/**
|
|
244
|
+
* The live stock price a banded sale checks against, read straight off the
|
|
245
|
+
* chain, plus whether a buy could use it this moment.
|
|
246
|
+
*
|
|
247
|
+
* Runs the hook's own checks in the hook's own order: the account sits at the
|
|
248
|
+
* one address this sale's shard and feed id produce, it is owned by Pyth's
|
|
249
|
+
* receiver program, its bytes are a price update, two thirds of the guardians
|
|
250
|
+
* signed it, the feed inside it is this sale's feed, it is no older than the
|
|
251
|
+
* sale allows, it was not published in the future, the price is above zero, and
|
|
252
|
+
* Pyth's confidence interval is inside the sale's limit. Any doubt comes back
|
|
253
|
+
* as `usable: false` with the program's own error name, because that is what
|
|
254
|
+
* the buy would hit.
|
|
255
|
+
*
|
|
256
|
+
* The age is measured against the chain's own clock, the same clock the hook
|
|
257
|
+
* compares against, rather than this machine's. If the Clock sysvar cannot be
|
|
258
|
+
* read, this machine's clock stands in.
|
|
259
|
+
*
|
|
260
|
+
* Does not cover the Wormhole guardian signatures. Nothing can redo those from
|
|
261
|
+
* the account's bytes: what stands in for them is the owner check, because only
|
|
262
|
+
* Pyth's receiver program can write an account it owns and it checks them
|
|
263
|
+
* first.
|
|
264
|
+
*
|
|
265
|
+
* Throws PanguInputError when the sale has no price band.
|
|
266
|
+
*/
|
|
267
|
+
declare function readPrice(connection: Connection, sale: Sale): Promise<PriceReading>;
|
|
268
|
+
|
|
269
|
+
export { type ApproveBuyerInput as A, type CloseBuyerRecordInput as C, type OpenBuyerRecordInput as O, type PriceBandInput as P, type RevokeBuyerInput as R, type PanguErrorName as a, type PriceReading as b, type CreateSaleInput as c, PANGU_ERRORS as d, type PanguError as e, type PriceUpdate as f, approveBuyerInstruction as g, closeBuyerRecordInstruction as h, createSaleInstruction as i, decodePriceUpdate as j, explainPanguError as k, revokeBuyerInstruction as l, openBuyerRecordInstruction as o, panguErrorFromLogs as p, readPrice as r };
|