-
Notifications
You must be signed in to change notification settings - Fork 36
Expand file tree
/
Copy path09-mayhem-mode.ts
More file actions
185 lines (167 loc) · 5.66 KB
/
Copy path09-mayhem-mode.ts
File metadata and controls
185 lines (167 loc) · 5.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/**
* Example 09: Mayhem Mode
*
* Category: Token Lifecycle
*
* Builds the same launch twice with createV2Instruction, once with mayhem
* mode off and once with it on, then diffs the two instructions byte for
* byte. Shows the four Mayhem program accounts every create_v2 carries and
* exactly where the flag lands in the instruction data.
*
* Run: npm run example 09
*/
import {
PUMP_SDK,
MAYHEM_PROGRAM_ID,
getGlobalParamsPda,
getMayhemStatePda,
getSolVaultPda,
getTokenVaultPda,
} from "@nirholas/pump-sdk";
import { Keypair, PublicKey, TransactionInstruction } from "@solana/web3.js";
import { heading, row } from "./_lib/format";
import { loadWallet } from "./_lib/wallet";
/** The Mayhem program accounts createV2 threads into every launch. */
export interface MayhemAccounts {
/** Program-wide Mayhem parameters, shared by every mayhem token. */
globalParams: PublicKey;
/** The Mayhem SOL vault, shared by every mayhem token. */
solVault: PublicKey;
/** Per-mint Mayhem state. */
mayhemState: PublicKey;
/** The SOL vault's Token-2022 ATA for this mint. */
tokenVault: PublicKey;
}
/** Derive every Mayhem PDA a create_v2 for `mint` references. */
export function mayhemAccounts(mint: PublicKey): MayhemAccounts {
return {
globalParams: getGlobalParamsPda(),
solVault: getSolVaultPda(),
mayhemState: getMayhemStatePda(mint),
tokenVault: getTokenVaultPda(mint),
};
}
/** The result of comparing two instructions account-for-account, byte-for-byte. */
export interface InstructionDiff {
/** True when both reference the same accounts in the same order with the same flags. */
accountsIdentical: boolean;
/** True when both data buffers have the same length. */
sameDataLength: boolean;
/** Indexes into the data buffer where the two instructions differ. */
changedDataOffsets: number[];
}
/** Compare two instructions built for the same launch. */
export function diffInstructions(
a: TransactionInstruction,
b: TransactionInstruction,
): InstructionDiff {
const accountsIdentical =
a.keys.length === b.keys.length &&
a.keys.every((key, i) => {
const other = b.keys[i];
return (
other !== undefined &&
key.pubkey.equals(other.pubkey) &&
key.isSigner === other.isSigner &&
key.isWritable === other.isWritable
);
});
const changedDataOffsets: number[] = [];
const shared = Math.min(a.data.length, b.data.length);
for (let i = 0; i < shared; i += 1) {
if (a.data[i] !== b.data[i]) changedDataOffsets.push(i);
}
return {
accountsIdentical,
sameDataLength: a.data.length === b.data.length,
changedDataOffsets,
};
}
export interface MayhemLaunchParams {
mint: PublicKey;
name: string;
symbol: string;
uri: string;
creator: PublicKey;
user: PublicKey;
}
/** Build the plain and mayhem variants of one launch, ready to compare. */
export async function buildMayhemPair(
params: MayhemLaunchParams,
): Promise<{ plain: TransactionInstruction; mayhem: TransactionInstruction }> {
const [plain, mayhem] = await Promise.all([
PUMP_SDK.createV2Instruction({ ...params, mayhemMode: false }),
PUMP_SDK.createV2Instruction({ ...params, mayhemMode: true }),
]);
return { plain, mayhem };
}
/** Index of an account inside an instruction's account list, or -1. */
export function accountIndex(
ix: TransactionInstruction,
account: PublicKey,
): number {
return ix.keys.findIndex((key) => key.pubkey.equals(account));
}
export async function main(): Promise<void> {
const wallet = loadWallet();
const mint = Keypair.generate();
heading("Launch parameters");
row("Mint (new keypair)", mint.publicKey.toBase58());
row("Creator / payer", wallet.publicKey.toBase58());
const { plain, mayhem } = await buildMayhemPair({
mint: mint.publicKey,
name: "Mayhem Example",
symbol: "MHEX",
uri: "https://example.com/metadata.json",
creator: wallet.publicKey,
user: wallet.publicKey,
});
heading("Mayhem program accounts (present in BOTH variants)");
row("Mayhem program", MAYHEM_PROGRAM_ID.toBase58());
const accounts = mayhemAccounts(mint.publicKey);
for (const [label, account] of Object.entries(accounts)) {
row(label, `${account.toBase58()} @ix[${accountIndex(plain, account)}]`);
}
console.log(
"\ncreateV2 always threads these four accounts, mayhem on or off. The",
);
console.log(
"flag decides whether the program initializes mayhem state for the mint.",
);
heading("Instruction shape");
row("Accounts (mayhemMode false)", plain.keys.length);
row("Accounts (mayhemMode true)", mayhem.keys.length);
row("Data bytes (false)", plain.data.length);
row("Data bytes (true)", mayhem.data.length);
heading("Byte diff");
const diff = diffInstructions(plain, mayhem);
row("Accounts identical", diff.accountsIdentical);
row("Same data length", diff.sameDataLength);
row("Changed data offsets", `[${diff.changedDataOffsets.join(", ")}]`);
for (const offset of diff.changedDataOffsets) {
row(
` data[${offset}]`,
`${String(plain.data[offset])} -> ${String(mayhem.data[offset])}`,
);
}
console.log(
"\nThe whole difference is one boolean byte in the Anchor-encoded args.",
);
console.log(
"The account lists are identical, so a launcher can flip mayhem mode",
);
console.log("without changing how it assembles the transaction.");
heading("Next step (not performed here)");
console.log(
"Sign the chosen instruction with both the wallet and the mint keypair,",
);
console.log(
"then send it. Example 10 covers the other create_v2 flag, cashback.",
);
}
if (require.main === module) {
main().catch((err) => {
console.error(err);
process.exit(1);
});
}