@typesol/cli 0.16.0 → 0.17.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/CHANGELOG.md +46 -0
- package/README.md +3 -3
- package/dist/clientgen.js +37 -0
- package/dist/compiler/analyze.d.ts +7 -1
- package/dist/compiler/analyze.js +258 -30
- package/dist/compiler/emit/base.d.ts +7 -0
- package/dist/compiler/emit/base.js +61 -1
- package/dist/compiler/emit/borsh.d.ts +13 -0
- package/dist/compiler/emit/borsh.js +232 -0
- package/dist/compiler/emit/calls.js +43 -15
- package/dist/compiler/emit/cpi.d.ts +32 -8
- package/dist/compiler/emit/cpi.js +522 -20
- package/dist/compiler/emit/expressions.js +19 -2
- package/dist/compiler/emit/function.js +34 -11
- package/dist/compiler/emit/program.js +24 -6
- package/dist/compiler/emit/shared.d.ts +12 -0
- package/dist/compiler/emit/shared.js +19 -2
- package/dist/compiler/emit/statements.js +11 -3
- package/dist/compiler/emit/storage.d.ts +4 -0
- package/dist/compiler/emit/storage.js +60 -12
- package/dist/compiler/idl.js +33 -4
- package/dist/compiler/model.d.ts +41 -2
- package/dist/compiler/model.js +8 -2
- package/dist/portgen.js +60 -4
- package/docs/errors.md +3 -2
- package/docs/language.md +10 -6
- package/docs/migrate-from-anchor.md +14 -5
- package/docs/test-your-program.md +1 -1
- package/lib/typesol.d.ts +259 -3
- package/package.json +1 -1
- package/runtime/typesol_rt.h +32 -7
|
@@ -1,8 +1,36 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
2
|
import { discriminator, snakeCase } from "../analyze.js";
|
|
3
|
+
import { borshMinSize } from "./borsh.js";
|
|
3
4
|
import { INT_INFO, isScalar, isWritable, sameType, scalarSize, typeName, } from "../model.js";
|
|
4
5
|
import { MAX_ACCOUNTS, MAX_SEEDS, MAX_CPI_DATA, MAX_EVENT, U64, U8, BOOL, PUBKEY, STRING, cBytes, isStable, cString, scalarStorageC, optC, valueC, } from "./shared.js";
|
|
5
6
|
import { CallEmitter } from "./calls.js";
|
|
7
|
+
/** Metaplex Core's Plugin / PluginType enums, in order (MasterEdition, VerifiedCreators and Autograph values are refused). */
|
|
8
|
+
const CORE_PLUGINS = [
|
|
9
|
+
"Royalties",
|
|
10
|
+
"FreezeDelegate",
|
|
11
|
+
"BurnDelegate",
|
|
12
|
+
"TransferDelegate",
|
|
13
|
+
"UpdateDelegate",
|
|
14
|
+
"PermanentFreezeDelegate",
|
|
15
|
+
"Attributes",
|
|
16
|
+
"PermanentTransferDelegate",
|
|
17
|
+
"PermanentBurnDelegate",
|
|
18
|
+
"Edition",
|
|
19
|
+
"MasterEdition",
|
|
20
|
+
"AddBlocker",
|
|
21
|
+
"ImmutableMetadata",
|
|
22
|
+
"VerifiedCreators",
|
|
23
|
+
"Autograph",
|
|
24
|
+
"BubblegumV2",
|
|
25
|
+
];
|
|
26
|
+
const CORE_PLUGIN_FIELDS = {
|
|
27
|
+
Royalties: ["basisPoints", "creators"],
|
|
28
|
+
FreezeDelegate: ["frozen"],
|
|
29
|
+
PermanentFreezeDelegate: ["frozen"],
|
|
30
|
+
UpdateDelegate: ["additionalDelegates"],
|
|
31
|
+
Attributes: ["attributes"],
|
|
32
|
+
Edition: ["number"],
|
|
33
|
+
};
|
|
6
34
|
/**
|
|
7
35
|
* Other programs: PDAs and seeds, account creation, System/Token/ATA/Metaplex CPIs, generic invoke, Borsh
|
|
8
36
|
* serialization and events.
|
|
@@ -139,7 +167,10 @@ export class CpiEmitter extends CallEmitter {
|
|
|
139
167
|
// Returns whether it was created now; an existing account must be this PDA, ours, and a ${s.name}.
|
|
140
168
|
const created = this.tmp("created");
|
|
141
169
|
this.line(`bool ${created};`);
|
|
142
|
-
this.line(`TS_TRY(ts_create_pda_if_needed(params, a_${payer.name}, a_${target.name}, ${seeds.name}, ${seeds.count}, TS_DISC_${s.name}, 8 + sizeof(TSS_${s.name}), &${b}, &${created}));`);
|
|
170
|
+
this.line(`TS_TRY(ts_create_pda_if_needed(params, a_${payer.name}, a_${target.name}, ${seeds.name}, ${seeds.count}, TS_DISC_${s.name}, 8 + sizeof(TSS_${s.name}), 8 + ${s.layout === "borsh" ? borshMinSize(s, this.model) : `sizeof(TSS_${s.name})`}, &${b}, &${created}));`);
|
|
171
|
+
// An existing @layout borsh account is unpacked now (a new one starts from the zeroed struct).
|
|
172
|
+
if (s.layout === "borsh")
|
|
173
|
+
this.line(`if (!${created}) TS_TRY(ts_unpack_${s.name}(a_${target.name}->data + 8, a_${target.name}->data_len - 8, d_${target.name}));`);
|
|
143
174
|
this.created.add(target.name);
|
|
144
175
|
this.pendingInit.delete(target.name);
|
|
145
176
|
this.bumps.set(target.name, b);
|
|
@@ -248,11 +279,26 @@ export class CpiEmitter extends CallEmitter {
|
|
|
248
279
|
], buf, len, seeds);
|
|
249
280
|
return undefined;
|
|
250
281
|
}
|
|
251
|
-
/**
|
|
252
|
-
|
|
282
|
+
/**
|
|
283
|
+
* Emit a CPI to the program account `program` with these metas (account, writable, signer), and then (`tail`) a range
|
|
284
|
+
* of the remaining accounts, read-only (a Merkle proof).
|
|
285
|
+
*/
|
|
286
|
+
cpi(program, metas, buf, len, seeds, tail) {
|
|
253
287
|
const m = this.tmp("metas");
|
|
254
|
-
|
|
255
|
-
|
|
288
|
+
const fixed = metas.map(([acc, w, sg]) => `{&${acc}->key, ${w}, ${sg}}`).join(", ");
|
|
289
|
+
if (!tail) {
|
|
290
|
+
this.line(`SolAccountMeta ${m}[${metas.length}] = {${fixed}};`);
|
|
291
|
+
this.line(`TS_TRY(ts_cpi(params, ${program}, ${buf}, ${len}, ${m}, ${metas.length}, ${seeds.name}, ${seeds.count}));`);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
const n = this.ix.accounts.length;
|
|
295
|
+
const cnt = this.tmp("nmetas");
|
|
296
|
+
const i = this.tmp("ri");
|
|
297
|
+
this.line(`if (${metas.length} + (${tail.to} - ${tail.from}) > TS_MAX_ACCOUNTS) TS_FAIL(TS_ERR_TOO_MANY_ACCOUNTS, "TypeSOL: the CPI lists more than ${MAX_ACCOUNTS} accounts");`);
|
|
298
|
+
this.line(`SolAccountMeta ${m}[TS_MAX_ACCOUNTS] = {${fixed}};`);
|
|
299
|
+
this.line(`uint64_t ${cnt} = ${metas.length};`);
|
|
300
|
+
this.line(`for (uint64_t ${i} = ${n} + ${tail.from}; ${i} < ${n} + ${tail.to}; ${i}++) ${m}[${cnt}++] = (SolAccountMeta){¶ms->ka[${i}]->key, 0, 0};`);
|
|
301
|
+
this.line(`TS_TRY(ts_cpi(params, ${program}, ${buf}, ${len}, ${m}, ${cnt}, ${seeds.name}, ${seeds.count}));`);
|
|
256
302
|
}
|
|
257
303
|
signerSeeds(node, authority, at) {
|
|
258
304
|
if (!node) {
|
|
@@ -562,10 +608,12 @@ export class CpiEmitter extends CallEmitter {
|
|
|
562
608
|
return undefined;
|
|
563
609
|
}
|
|
564
610
|
metadataOp(node, program, method) {
|
|
611
|
+
if (method === "createMasterEditionV3" || method === "verifyCollection")
|
|
612
|
+
return this.metadataCollectionOp(node, program, method);
|
|
565
613
|
if (method !== "createV3")
|
|
566
|
-
this.fail(node.expression,
|
|
614
|
+
this.fail(node.expression, `the Token Metadata program supports createV3, createMasterEditionV3 and verifyCollection, not ${method}`);
|
|
567
615
|
const a = node.arguments;
|
|
568
|
-
const usage = "createV3(metadata, mint, mintAuthority, payer, updateAuthority, systemProgram, name, symbol, uri, sellerFeeBasisPoints, isMutable, { creators?, collection? }?, signerSeeds?)";
|
|
616
|
+
const usage = "createV3(metadata, mint, mintAuthority, payer, updateAuthority, systemProgram, name, symbol, uri, sellerFeeBasisPoints, isMutable, { creators?, collection?, isCollection? }?, signerSeeds?)";
|
|
569
617
|
// An object literal after isMutable carries the optional creators / collection; signerSeeds stays last.
|
|
570
618
|
const extras = a[11] && ts.isObjectLiteralExpression(a[11]) ? a[11] : undefined;
|
|
571
619
|
const seedsArg = extras ? a[12] : a[11];
|
|
@@ -598,10 +646,11 @@ export class CpiEmitter extends CallEmitter {
|
|
|
598
646
|
let creators = "NULL";
|
|
599
647
|
let ncreators = "-1";
|
|
600
648
|
let collection = "NULL";
|
|
649
|
+
let sized = "false";
|
|
601
650
|
if (extras) {
|
|
602
651
|
for (const p of extras.properties) {
|
|
603
652
|
if (!ts.isPropertyAssignment(p) || !ts.isIdentifier(p.name))
|
|
604
|
-
this.fail(p, "use creators: [...] and
|
|
653
|
+
this.fail(p, "use creators: [...], collection: key and isCollection: boolean");
|
|
605
654
|
if (p.name.text === "creators") {
|
|
606
655
|
if (!ts.isArrayLiteralExpression(p.initializer))
|
|
607
656
|
this.fail(p.initializer, "creators: [{ address, verified, share }, …] (an array literal)");
|
|
@@ -646,14 +695,66 @@ export class CpiEmitter extends CallEmitter {
|
|
|
646
695
|
this.line(`SolPubkey ${t} = *(${key.c});`);
|
|
647
696
|
collection = `&${t}`;
|
|
648
697
|
}
|
|
698
|
+
else if (p.name.text === "isCollection") {
|
|
699
|
+
const v = this.expect(this.expr(p.initializer, BOOL), BOOL, p.initializer);
|
|
700
|
+
const t = this.tmp("sized");
|
|
701
|
+
this.line(`bool ${t} = ${v.c};`);
|
|
702
|
+
sized = t;
|
|
703
|
+
}
|
|
649
704
|
else
|
|
650
|
-
this.fail(p.name, `createV3 extras are creators and
|
|
705
|
+
this.fail(p.name, `createV3 extras are creators, collection and isCollection, not ${p.name.text}`);
|
|
651
706
|
}
|
|
652
707
|
}
|
|
653
708
|
const seeds = this.signerSeeds(seedsArg, mintAuth, a[2]);
|
|
654
709
|
settleAll(extrasMark);
|
|
655
710
|
const [name, symbol, uri, fee, mutable] = slots.map((i) => vals[i]);
|
|
656
|
-
this.line(`TS_TRY(ts_mpl_create_v3(params, a_${program.name}, a_${metadata.name}, a_${mint.name}, a_${mintAuth.name}, a_${payer.name}, a_${updateAuth.name}, a_${sys.name}, ${name.c}, ${symbol.c}, ${uri.c}, ${fee.c}, ${mutable.c}, ${creators}, ${ncreators}, ${collection}, ${seeds.name}, ${seeds.count}));`);
|
|
711
|
+
this.line(`TS_TRY(ts_mpl_create_v3(params, a_${program.name}, a_${metadata.name}, a_${mint.name}, a_${mintAuth.name}, a_${payer.name}, a_${updateAuth.name}, a_${sys.name}, ${name.c}, ${symbol.c}, ${uri.c}, ${fee.c}, ${mutable.c}, ${creators}, ${ncreators}, ${collection}, ${sized}, ${seeds.name}, ${seeds.count}));`);
|
|
712
|
+
return undefined;
|
|
713
|
+
}
|
|
714
|
+
/** CreateMasterEditionV3 (17) and Verify (52) with VerificationArgs::CollectionV1. */
|
|
715
|
+
metadataCollectionOp(node, program, method) {
|
|
716
|
+
const a = node.arguments;
|
|
717
|
+
const p = `a_${program.name}`;
|
|
718
|
+
const seedsAt = (i) => (a[i] ? this.signingSeeds(a[i]) : { name: "NULL", count: 0 });
|
|
719
|
+
const acct = (i, what, w, s) => {
|
|
720
|
+
if (a[i].kind === ts.SyntaxKind.NullKeyword)
|
|
721
|
+
this.fail(a[i], `pass ${what}`);
|
|
722
|
+
return this.mplMeta(a[i], program, what, w, s);
|
|
723
|
+
};
|
|
724
|
+
if (method === "createMasterEditionV3") {
|
|
725
|
+
if (a.length !== 9 && a.length !== 10)
|
|
726
|
+
this.fail(node, "createMasterEditionV3(edition, mint, updateAuthority, mintAuthority, payer, metadata, tokenProgram, systemProgram, maxSupply | null, signerSeeds?)");
|
|
727
|
+
const metas = [
|
|
728
|
+
acct(0, "the edition", true, false),
|
|
729
|
+
acct(1, "the mint", true, false),
|
|
730
|
+
acct(2, "the update authority", false, true),
|
|
731
|
+
acct(3, "the mint authority", false, true),
|
|
732
|
+
acct(4, "the payer", true, true),
|
|
733
|
+
acct(5, "the metadata", true, false),
|
|
734
|
+
acct(6, "the token program", false, false),
|
|
735
|
+
acct(7, "the system program", false, false),
|
|
736
|
+
];
|
|
737
|
+
// max_supply: Option<u64>; Some(0) makes a one of one, None allows unlimited prints.
|
|
738
|
+
const data = this.mplData(node, 16, a[8].kind === ts.SyntaxKind.NullKeyword ? [{ bytes: [17, 0] }] : [{ bytes: [17, 1] }, { value: a[8], type: { kind: "int", name: "u64" } }]);
|
|
739
|
+
this.cpi(p, metas, data.buf, data.len, seedsAt(9));
|
|
740
|
+
return undefined;
|
|
741
|
+
}
|
|
742
|
+
// Verify: authority, delegate record (absent: the program id), metadata, collection mint, collection metadata,
|
|
743
|
+
// collection master edition, system program, instructions sysvar.
|
|
744
|
+
if (a.length !== 7 && a.length !== 8)
|
|
745
|
+
this.fail(node, "verifyCollection(authority, metadata, collectionMint, collectionMetadata, collectionMasterEdition, systemProgram, instructionsSysvar, signerSeeds?)");
|
|
746
|
+
const metas = [
|
|
747
|
+
acct(0, "the collection authority", false, true),
|
|
748
|
+
[p, false, false],
|
|
749
|
+
acct(1, "the metadata", true, false),
|
|
750
|
+
acct(2, "the collection mint", false, false),
|
|
751
|
+
acct(3, "the collection metadata", true, false),
|
|
752
|
+
acct(4, "the collection master edition", false, false),
|
|
753
|
+
acct(5, "the system program", false, false),
|
|
754
|
+
acct(6, "the instructions sysvar", false, false),
|
|
755
|
+
];
|
|
756
|
+
const data = this.mplData(node, 8, [{ bytes: [52, 1] }]);
|
|
757
|
+
this.cpi(p, metas, data.buf, data.len, seedsAt(7));
|
|
657
758
|
return undefined;
|
|
658
759
|
}
|
|
659
760
|
/**
|
|
@@ -683,17 +784,156 @@ export class CpiEmitter extends CallEmitter {
|
|
|
683
784
|
finish(at);
|
|
684
785
|
return { buf, len };
|
|
685
786
|
}
|
|
686
|
-
/**
|
|
787
|
+
/**
|
|
788
|
+
* Borsh of one Metaplex Core plugin literal (`{ kind: "FreezeDelegate", frozen: true, authority?: … }`): the Plugin
|
|
789
|
+
* enum, then (`withAuthority`) the Option<PluginAuthority> that CreateV1's PluginAuthorityPair and AddPluginV1 take.
|
|
790
|
+
*/
|
|
791
|
+
corePlugin(n, withAuthority) {
|
|
792
|
+
if (!ts.isObjectLiteralExpression(n))
|
|
793
|
+
this.fail(n, 'a Core plugin is an object literal: { kind: "FreezeDelegate", frozen: false }');
|
|
794
|
+
const props = new Map();
|
|
795
|
+
for (const q of n.properties) {
|
|
796
|
+
if (ts.isPropertyAssignment(q) && ts.isIdentifier(q.name))
|
|
797
|
+
props.set(q.name.text, q.initializer);
|
|
798
|
+
else if (ts.isShorthandPropertyAssignment(q))
|
|
799
|
+
props.set(q.name.text, q.name);
|
|
800
|
+
else
|
|
801
|
+
this.fail(q, "write plugin fields as name: value");
|
|
802
|
+
}
|
|
803
|
+
const kindNode = props.get("kind");
|
|
804
|
+
if (!kindNode || !ts.isStringLiteral(kindNode))
|
|
805
|
+
this.fail(n, 'a Core plugin needs kind: "Royalties" | "FreezeDelegate" | …');
|
|
806
|
+
const kind = kindNode.text;
|
|
807
|
+
const index = CORE_PLUGINS.indexOf(kind);
|
|
808
|
+
if (index < 0)
|
|
809
|
+
this.fail(kindNode, `Core plugins are ${CORE_PLUGINS.join(", ")}`);
|
|
810
|
+
if (kind === "MasterEdition" || kind === "VerifiedCreators" || kind === "Autograph")
|
|
811
|
+
this.fail(kindNode, `the ${kind} plugin is not supported yet (removePlugin can remove one)`);
|
|
812
|
+
const known = new Set(["kind", "authority", ...(CORE_PLUGIN_FIELDS[kind] ?? [])]);
|
|
813
|
+
for (const [k, v] of props)
|
|
814
|
+
if (!known.has(k))
|
|
815
|
+
this.fail(v, `${kind} has no field ${k}`);
|
|
816
|
+
const need = (k) => props.get(k) ?? this.fail(n, `${kind} needs ${k}`);
|
|
817
|
+
const list = (k, max) => {
|
|
818
|
+
const e = need(k);
|
|
819
|
+
if (!ts.isArrayLiteralExpression(e))
|
|
820
|
+
this.fail(e, `${k} is an array literal`);
|
|
821
|
+
if (e.elements.length > max)
|
|
822
|
+
this.fail(e, `at most ${max} entries in ${k}`);
|
|
823
|
+
return e.elements;
|
|
824
|
+
};
|
|
825
|
+
const u32 = (x) => [x & 0xff, (x >> 8) & 0xff, (x >> 16) & 0xff, x >>> 24];
|
|
826
|
+
const fields = [{ bytes: [index] }];
|
|
827
|
+
switch (kind) {
|
|
828
|
+
case "Royalties": {
|
|
829
|
+
const bps = need("basisPoints");
|
|
830
|
+
if (ts.isBigIntLiteral(bps) && BigInt(bps.text.slice(0, -1)) > 10000n)
|
|
831
|
+
this.fail(bps, "basisPoints is at most 10000 (100%)");
|
|
832
|
+
fields.push({ value: bps, type: { kind: "int", name: "u16" } });
|
|
833
|
+
const creators = list("creators", 5);
|
|
834
|
+
fields.push({ bytes: u32(creators.length) });
|
|
835
|
+
let sum = 0n;
|
|
836
|
+
let constant = true;
|
|
837
|
+
for (const c of creators) {
|
|
838
|
+
if (!ts.isObjectLiteralExpression(c))
|
|
839
|
+
this.fail(c, "a creator is { address, percentage }");
|
|
840
|
+
const get = (k) => {
|
|
841
|
+
const q = c.properties.find((x) => (ts.isPropertyAssignment(x) || ts.isShorthandPropertyAssignment(x)) && ts.isIdentifier(x.name) && x.name.text === k);
|
|
842
|
+
if (!q)
|
|
843
|
+
this.fail(c, `a creator needs ${k}`);
|
|
844
|
+
return ts.isPropertyAssignment(q) ? q.initializer : q.name;
|
|
845
|
+
};
|
|
846
|
+
const pct = get("percentage");
|
|
847
|
+
if (ts.isBigIntLiteral(pct))
|
|
848
|
+
sum += BigInt(pct.text.slice(0, -1));
|
|
849
|
+
else
|
|
850
|
+
constant = false;
|
|
851
|
+
fields.push({ value: get("address"), type: PUBKEY }, { value: pct, type: U8 });
|
|
852
|
+
}
|
|
853
|
+
if (constant && creators.length && sum !== 100n)
|
|
854
|
+
this.fail(need("creators"), `creator percentages add up to ${sum}; Core requires 100`);
|
|
855
|
+
fields.push({ bytes: [0] }); // rule_set: RuleSet::None
|
|
856
|
+
break;
|
|
857
|
+
}
|
|
858
|
+
case "FreezeDelegate":
|
|
859
|
+
case "PermanentFreezeDelegate":
|
|
860
|
+
fields.push({ value: need("frozen"), type: BOOL });
|
|
861
|
+
break;
|
|
862
|
+
case "UpdateDelegate": {
|
|
863
|
+
const keys = props.has("additionalDelegates") ? list("additionalDelegates", 8) : [];
|
|
864
|
+
fields.push({ bytes: u32(keys.length) }, ...keys.map((k) => ({ value: k, type: PUBKEY })));
|
|
865
|
+
break;
|
|
866
|
+
}
|
|
867
|
+
case "Attributes": {
|
|
868
|
+
const attrs = list("attributes", 32);
|
|
869
|
+
fields.push({ bytes: u32(attrs.length) });
|
|
870
|
+
for (const at of attrs) {
|
|
871
|
+
if (!ts.isObjectLiteralExpression(at))
|
|
872
|
+
this.fail(at, "an attribute is { key, value }");
|
|
873
|
+
for (const k of ["key", "value"]) {
|
|
874
|
+
const q = at.properties.find((x) => (ts.isPropertyAssignment(x) || ts.isShorthandPropertyAssignment(x)) && ts.isIdentifier(x.name) && x.name.text === k);
|
|
875
|
+
if (!q)
|
|
876
|
+
this.fail(at, `an attribute needs ${k}`);
|
|
877
|
+
fields.push({ value: ts.isPropertyAssignment(q) ? q.initializer : q.name, type: STRING });
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
break;
|
|
881
|
+
}
|
|
882
|
+
case "Edition":
|
|
883
|
+
fields.push({ value: need("number"), type: { kind: "int", name: "u32" } });
|
|
884
|
+
break;
|
|
885
|
+
}
|
|
886
|
+
const auth = props.get("authority");
|
|
887
|
+
if (!withAuthority) {
|
|
888
|
+
if (auth)
|
|
889
|
+
this.fail(auth, "a plugin's authority is set when it is added; change it with Core's approve / revoke");
|
|
890
|
+
return fields;
|
|
891
|
+
}
|
|
892
|
+
if (!auth)
|
|
893
|
+
fields.push({ bytes: [0] }); // the plugin type's default authority
|
|
894
|
+
else if (ts.isStringLiteral(auth)) {
|
|
895
|
+
const i = ["none", "owner", "updateAuthority"].indexOf(auth.text);
|
|
896
|
+
if (i < 0)
|
|
897
|
+
this.fail(auth, 'authority is "none", "owner", "updateAuthority" or a Pubkey');
|
|
898
|
+
fields.push({ bytes: [1, i] });
|
|
899
|
+
}
|
|
900
|
+
else
|
|
901
|
+
fields.push({ bytes: [1, 3] }, { value: auth, type: PUBKEY });
|
|
902
|
+
return fields;
|
|
903
|
+
}
|
|
904
|
+
/** CreateV1 / CreateCollectionV1 plugins: Option<Vec<PluginAuthorityPair>>. */
|
|
905
|
+
corePluginList(n) {
|
|
906
|
+
if (!n)
|
|
907
|
+
return [{ bytes: [0] }];
|
|
908
|
+
const els = n.elements;
|
|
909
|
+
if (els.length > 16)
|
|
910
|
+
this.fail(n, "at most 16 plugins");
|
|
911
|
+
const kinds = els.map((e) => ts.isObjectLiteralExpression(e) ? e.properties.find((q) => q.name && ts.isIdentifier(q.name) && q.name.text === "kind") : undefined);
|
|
912
|
+
const seen = new Set();
|
|
913
|
+
kinds.forEach((k, i) => {
|
|
914
|
+
const lit = k && ts.isPropertyAssignment(k) && ts.isStringLiteral(k.initializer) ? k.initializer.text : undefined;
|
|
915
|
+
if (lit && seen.has(lit))
|
|
916
|
+
this.fail(els[i], `${lit} is listed twice; an asset has one plugin of each kind`);
|
|
917
|
+
if (lit)
|
|
918
|
+
seen.add(lit);
|
|
919
|
+
});
|
|
920
|
+
return [{ bytes: [1, els.length & 0xff, 0, 0, 0] }, ...els.flatMap((e) => this.corePlugin(e, true))];
|
|
921
|
+
}
|
|
922
|
+
/** Metaplex Core: assets and collections (with plugins), plugins added, updated and removed, transfer and burn. */
|
|
687
923
|
coreOp(node, core, method) {
|
|
688
924
|
const a = node.arguments;
|
|
689
925
|
const p = `a_${core.name}`;
|
|
690
926
|
const seedsAt = (i) => (a[i] ? this.signingSeeds(a[i]) : { name: "NULL", count: 0 });
|
|
691
927
|
const str = (n) => ({ value: n, type: STRING });
|
|
928
|
+
// An array literal of object literals after the uri is the plugin list; the signer seeds come after it.
|
|
929
|
+
const isPlugins = (n) => !!n && ts.isArrayLiteralExpression(n) && (n.elements.length === 0 || n.elements.every((e) => ts.isObjectLiteralExpression(e)));
|
|
930
|
+
const self = [p, false, false];
|
|
692
931
|
switch (method) {
|
|
693
932
|
case "createAsset": {
|
|
694
933
|
// CreateV1: asset, collection, authority, payer, owner, updateAuthority, systemProgram, logWrapper.
|
|
695
|
-
|
|
696
|
-
|
|
934
|
+
const plugins = isPlugins(a[8]) ? a[8] : undefined;
|
|
935
|
+
if (a.length < 8 || a.length > (plugins ? 10 : 9))
|
|
936
|
+
this.fail(node, "createAsset(asset, collection | null, authority | null, payer, owner | null, systemProgram, name, uri, plugins?, signerSeeds?)");
|
|
697
937
|
const metas = [
|
|
698
938
|
this.mplMeta(a[0], core, "the asset", true, true),
|
|
699
939
|
this.mplMeta(a[1], core, "the collection", true, false),
|
|
@@ -704,24 +944,64 @@ export class CpiEmitter extends CallEmitter {
|
|
|
704
944
|
this.mplMeta(a[5], core, "the system program", false, false),
|
|
705
945
|
[p, false, false],
|
|
706
946
|
];
|
|
707
|
-
const data = this.mplData(node,
|
|
708
|
-
this.cpi(p, metas, data.buf, data.len, seedsAt(8));
|
|
947
|
+
const data = this.mplData(node, 2048, [{ bytes: [0, 0] }, str(a[6]), str(a[7]), ...this.corePluginList(plugins)]);
|
|
948
|
+
this.cpi(p, metas, data.buf, data.len, seedsAt(plugins ? 9 : 8));
|
|
709
949
|
return undefined;
|
|
710
950
|
}
|
|
711
951
|
case "createCollection": {
|
|
712
952
|
// CreateCollectionV1: collection, updateAuthority, payer, systemProgram.
|
|
713
|
-
|
|
714
|
-
|
|
953
|
+
const plugins = isPlugins(a[6]) ? a[6] : undefined;
|
|
954
|
+
if (a.length < 6 || a.length > (plugins ? 8 : 7))
|
|
955
|
+
this.fail(node, "createCollection(collection, updateAuthority | null, payer, systemProgram, name, uri, plugins?, signerSeeds?)");
|
|
715
956
|
const metas = [
|
|
716
957
|
this.mplMeta(a[0], core, "the collection", true, true),
|
|
717
958
|
this.mplMeta(a[1], core, "the update authority", false, false),
|
|
718
959
|
this.mplMeta(a[2], core, "the payer", true, true),
|
|
719
960
|
this.mplMeta(a[3], core, "the system program", false, false),
|
|
720
961
|
];
|
|
721
|
-
const data = this.mplData(node,
|
|
962
|
+
const data = this.mplData(node, 2048, [{ bytes: [1] }, str(a[4]), str(a[5]), ...this.corePluginList(plugins)]);
|
|
963
|
+
this.cpi(p, metas, data.buf, data.len, seedsAt(plugins ? 7 : 6));
|
|
964
|
+
return undefined;
|
|
965
|
+
}
|
|
966
|
+
case "addPlugin":
|
|
967
|
+
case "updatePlugin":
|
|
968
|
+
case "removePlugin": {
|
|
969
|
+
// Add/Remove/UpdatePluginV1: asset, collection, payer, authority, systemProgram, logWrapper.
|
|
970
|
+
const last = method === "removePlugin" ? "kind" : "plugin";
|
|
971
|
+
if (a.length !== 6 && a.length !== 7)
|
|
972
|
+
this.fail(node, `${method}(asset, collection | null, payer, authority | null, systemProgram, ${last}, signerSeeds?)`);
|
|
973
|
+
const metas = [
|
|
974
|
+
this.mplMeta(a[0], core, "the asset", true, false),
|
|
975
|
+
this.mplMeta(a[1], core, "the collection", true, false),
|
|
976
|
+
this.mplMeta(a[2], core, "the payer", true, true),
|
|
977
|
+
this.mplMeta(a[3], core, "the authority", false, true),
|
|
978
|
+
this.mplMeta(a[4], core, "the system program", false, false),
|
|
979
|
+
self,
|
|
980
|
+
];
|
|
981
|
+
const body = method === "removePlugin" ? [this.corePluginType(a[5])] : this.corePlugin(a[5], method === "addPlugin");
|
|
982
|
+
const data = this.mplData(node, 2048, [{ bytes: [{ addPlugin: 2, removePlugin: 4, updatePlugin: 6 }[method]] }, ...body]);
|
|
722
983
|
this.cpi(p, metas, data.buf, data.len, seedsAt(6));
|
|
723
984
|
return undefined;
|
|
724
985
|
}
|
|
986
|
+
case "addCollectionPlugin":
|
|
987
|
+
case "updateCollectionPlugin":
|
|
988
|
+
case "removeCollectionPlugin": {
|
|
989
|
+
// Add/Remove/UpdateCollectionPluginV1: collection, payer, authority, systemProgram, logWrapper.
|
|
990
|
+
const last = method === "removeCollectionPlugin" ? "kind" : "plugin";
|
|
991
|
+
if (a.length !== 5 && a.length !== 6)
|
|
992
|
+
this.fail(node, `${method}(collection, payer, authority | null, systemProgram, ${last}, signerSeeds?)`);
|
|
993
|
+
const metas = [
|
|
994
|
+
this.mplMeta(a[0], core, "the collection", true, false),
|
|
995
|
+
this.mplMeta(a[1], core, "the payer", true, true),
|
|
996
|
+
this.mplMeta(a[2], core, "the authority", false, true),
|
|
997
|
+
this.mplMeta(a[3], core, "the system program", false, false),
|
|
998
|
+
self,
|
|
999
|
+
];
|
|
1000
|
+
const body = method === "removeCollectionPlugin" ? [this.corePluginType(a[4])] : this.corePlugin(a[4], method === "addCollectionPlugin");
|
|
1001
|
+
const data = this.mplData(node, 2048, [{ bytes: [{ addCollectionPlugin: 3, removeCollectionPlugin: 5, updateCollectionPlugin: 7 }[method]] }, ...body]);
|
|
1002
|
+
this.cpi(p, metas, data.buf, data.len, seedsAt(5));
|
|
1003
|
+
return undefined;
|
|
1004
|
+
}
|
|
725
1005
|
case "transferAsset": {
|
|
726
1006
|
// TransferV1: asset, collection, payer, authority, newOwner, systemProgram, logWrapper.
|
|
727
1007
|
if (a.length !== 5 && a.length !== 6)
|
|
@@ -756,7 +1036,16 @@ export class CpiEmitter extends CallEmitter {
|
|
|
756
1036
|
return undefined;
|
|
757
1037
|
}
|
|
758
1038
|
}
|
|
759
|
-
this.fail(node.expression, `Metaplex Core supports createAsset, createCollection, transferAsset and burnAsset, not ${method}`);
|
|
1039
|
+
this.fail(node.expression, `Metaplex Core supports createAsset, createCollection, addPlugin, updatePlugin, removePlugin (and the Collection forms), transferAsset and burnAsset, not ${method}`);
|
|
1040
|
+
}
|
|
1041
|
+
/** PluginType (u8) from a kind string literal: removePlugin(…, "FreezeDelegate"). */
|
|
1042
|
+
corePluginType(n) {
|
|
1043
|
+
if (!ts.isStringLiteral(n))
|
|
1044
|
+
this.fail(n, 'name the plugin kind as a string literal: "FreezeDelegate"');
|
|
1045
|
+
const i = CORE_PLUGINS.indexOf(n.text);
|
|
1046
|
+
if (i < 0)
|
|
1047
|
+
this.fail(n, `Core plugins are ${CORE_PLUGINS.join(", ")}`);
|
|
1048
|
+
return { bytes: [i] };
|
|
760
1049
|
}
|
|
761
1050
|
/** Bubblegum (compressed NFTs on SPL account compression): createTree, mint (MintV1, no collection, no creators). */
|
|
762
1051
|
bubblegumOp(node, bg, method) {
|
|
@@ -819,7 +1108,218 @@ export class CpiEmitter extends CallEmitter {
|
|
|
819
1108
|
return undefined;
|
|
820
1109
|
}
|
|
821
1110
|
}
|
|
822
|
-
this.
|
|
1111
|
+
return this.bubblegumV2(node, bg, method);
|
|
1112
|
+
}
|
|
1113
|
+
/** Bubblegum V2 (trees on mpl-account-compression with mpl-noop; optional Metaplex Core collections). */
|
|
1114
|
+
bubblegumV2(node, bg, method) {
|
|
1115
|
+
const a = node.arguments;
|
|
1116
|
+
const p = `a_${bg.name}`;
|
|
1117
|
+
const acct = (i, what, w, s) => this.mplMeta(a[i], bg, what, w, s);
|
|
1118
|
+
const seedsAt = (i) => (a[i] ? this.signingSeeds(a[i]) : { name: "NULL", count: 0 });
|
|
1119
|
+
const U32 = { kind: "int", name: "u32" };
|
|
1120
|
+
const U64v = { kind: "int", name: "u64" };
|
|
1121
|
+
const HASH = { kind: "pubkey", hash: true };
|
|
1122
|
+
// The leaf a transfer or burn replaces: { root, dataHash, creatorHash, assetDataHash?, flags?, nonce, index }.
|
|
1123
|
+
const leafFields = (n) => {
|
|
1124
|
+
if (!ts.isObjectLiteralExpression(n))
|
|
1125
|
+
this.fail(n, "the leaf is { root, dataHash, creatorHash, assetDataHash?, flags?, nonce, index }");
|
|
1126
|
+
const props = new Map();
|
|
1127
|
+
for (const q of n.properties) {
|
|
1128
|
+
if (ts.isPropertyAssignment(q) && ts.isIdentifier(q.name))
|
|
1129
|
+
props.set(q.name.text, q.initializer);
|
|
1130
|
+
else if (ts.isShorthandPropertyAssignment(q))
|
|
1131
|
+
props.set(q.name.text, q.name);
|
|
1132
|
+
else
|
|
1133
|
+
this.fail(q, "write leaf fields as name: value");
|
|
1134
|
+
}
|
|
1135
|
+
const known = ["root", "dataHash", "creatorHash", "assetDataHash", "flags", "nonce", "index"];
|
|
1136
|
+
for (const [k, v] of props)
|
|
1137
|
+
if (!known.includes(k))
|
|
1138
|
+
this.fail(v, `the leaf has no field ${k}`);
|
|
1139
|
+
const need = (k) => props.get(k) ?? this.fail(n, `the leaf needs ${k}`);
|
|
1140
|
+
const opt = (k, t) => {
|
|
1141
|
+
const v = props.get(k);
|
|
1142
|
+
if (!v || v.kind === ts.SyntaxKind.NullKeyword)
|
|
1143
|
+
return [{ bytes: [0] }];
|
|
1144
|
+
return [{ bytes: [1] }, { value: v, type: t }];
|
|
1145
|
+
};
|
|
1146
|
+
return [
|
|
1147
|
+
{ value: need("root"), type: HASH },
|
|
1148
|
+
{ value: need("dataHash"), type: HASH },
|
|
1149
|
+
{ value: need("creatorHash"), type: HASH },
|
|
1150
|
+
...opt("assetDataHash", HASH),
|
|
1151
|
+
...opt("flags", U8),
|
|
1152
|
+
{ value: need("nonce"), type: U64v },
|
|
1153
|
+
{ value: need("index"), type: U32 },
|
|
1154
|
+
];
|
|
1155
|
+
};
|
|
1156
|
+
const proofRange = (n) => {
|
|
1157
|
+
if (!this.ix)
|
|
1158
|
+
this.fail(n, "a Merkle proof comes from remainingAccounts(), so only instructions can pass one");
|
|
1159
|
+
return this.remainingRange(n) ?? this.fail(n, "pass the proof as remainingAccounts() or rest.slice(a, b)");
|
|
1160
|
+
};
|
|
1161
|
+
switch (method) {
|
|
1162
|
+
case "createTreeV2": {
|
|
1163
|
+
// CreateTreeConfigV2: treeConfig, merkleTree, payer, treeCreator, logWrapper, compressionProgram, systemProgram.
|
|
1164
|
+
if (a.length !== 10 && a.length !== 11)
|
|
1165
|
+
this.fail(node, "createTreeV2(treeConfig, merkleTree, payer, treeCreator | null, logWrapper, compressionProgram, systemProgram, maxDepth, maxBufferSize, isPublic, signerSeeds?)");
|
|
1166
|
+
const metas = [
|
|
1167
|
+
acct(0, "the tree config", true, false),
|
|
1168
|
+
acct(1, "the merkle tree", true, false),
|
|
1169
|
+
acct(2, "the payer", true, true),
|
|
1170
|
+
acct(3, "the tree creator", false, true),
|
|
1171
|
+
acct(4, "the log wrapper", false, false),
|
|
1172
|
+
acct(5, "the compression program", false, false),
|
|
1173
|
+
acct(6, "the system program", false, false),
|
|
1174
|
+
];
|
|
1175
|
+
const data = this.mplData(node, 32, [
|
|
1176
|
+
{ bytes: [55, 99, 95, 215, 142, 203, 227, 205] },
|
|
1177
|
+
{ value: a[7], type: U32 },
|
|
1178
|
+
{ value: a[8], type: U32 },
|
|
1179
|
+
{ bytes: [1] },
|
|
1180
|
+
{ value: a[9], type: BOOL },
|
|
1181
|
+
]);
|
|
1182
|
+
this.cpi(p, metas, data.buf, data.len, seedsAt(10));
|
|
1183
|
+
return undefined;
|
|
1184
|
+
}
|
|
1185
|
+
case "mintV2": {
|
|
1186
|
+
// MintV2: treeConfig, payer, treeCreatorOrDelegate, collectionAuthority, leafOwner, leafDelegate, merkleTree,
|
|
1187
|
+
// coreCollection, mplCoreCpiSigner, logWrapper, compressionProgram, mplCoreProgram, systemProgram.
|
|
1188
|
+
const usage = "mintV2(treeConfig, payer, treeDelegate | null, leafOwner, leafDelegate | null, merkleTree, logWrapper, compressionProgram, mplCoreProgram, systemProgram, { name, symbol, uri, sellerFeeBasisPoints, creators?, isMutable? }, { collection, authority, cpiSigner }?, signerSeeds?)";
|
|
1189
|
+
const collection = a[11] && ts.isObjectLiteralExpression(a[11]) ? a[11] : undefined;
|
|
1190
|
+
if (a.length < 11 || a.length > (collection ? 13 : 12))
|
|
1191
|
+
this.fail(node, usage);
|
|
1192
|
+
const md = a[10];
|
|
1193
|
+
if (!ts.isObjectLiteralExpression(md))
|
|
1194
|
+
this.fail(md, "the metadata is { name, symbol, uri, sellerFeeBasisPoints, creators?, isMutable? }");
|
|
1195
|
+
const field = (o, k) => {
|
|
1196
|
+
const q = o.properties.find((x) => (ts.isPropertyAssignment(x) || ts.isShorthandPropertyAssignment(x)) && ts.isIdentifier(x.name) && x.name.text === k);
|
|
1197
|
+
return q ? (ts.isPropertyAssignment(q) ? q.initializer : q.name) : undefined;
|
|
1198
|
+
};
|
|
1199
|
+
for (const q of md.properties)
|
|
1200
|
+
if (!q.name || !ts.isIdentifier(q.name) || !["name", "symbol", "uri", "sellerFeeBasisPoints", "creators", "isMutable"].includes(q.name.text))
|
|
1201
|
+
this.fail(q, "the metadata takes name, symbol, uri, sellerFeeBasisPoints, creators and isMutable");
|
|
1202
|
+
const coll = collection
|
|
1203
|
+
? ["collection", "authority", "cpiSigner"].map((k) => field(collection, k) ?? this.fail(collection, `the collection needs ${k}`))
|
|
1204
|
+
: undefined;
|
|
1205
|
+
if (collection)
|
|
1206
|
+
for (const q of collection.properties)
|
|
1207
|
+
if (!q.name || !ts.isIdentifier(q.name) || !["collection", "authority", "cpiSigner"].includes(q.name.text))
|
|
1208
|
+
this.fail(q, "the collection is { collection, authority, cpiSigner }");
|
|
1209
|
+
const metas = [
|
|
1210
|
+
acct(0, "the tree config", true, false),
|
|
1211
|
+
acct(1, "the payer", true, true),
|
|
1212
|
+
acct(2, "the tree delegate", false, true),
|
|
1213
|
+
coll ? this.mplMeta(coll[1], bg, "the collection authority", false, true) : [p, false, false],
|
|
1214
|
+
acct(3, "the leaf owner", false, false),
|
|
1215
|
+
acct(4, "the leaf delegate", false, false),
|
|
1216
|
+
acct(5, "the merkle tree", true, false),
|
|
1217
|
+
coll ? this.mplMeta(coll[0], bg, "the core collection", true, false) : [p, false, false],
|
|
1218
|
+
coll ? this.mplMeta(coll[2], bg, "the mpl-core CPI signer", false, false) : [p, false, false],
|
|
1219
|
+
acct(6, "the log wrapper", false, false),
|
|
1220
|
+
acct(7, "the compression program", false, false),
|
|
1221
|
+
acct(8, "the mpl-core program", false, false),
|
|
1222
|
+
acct(9, "the system program", false, false),
|
|
1223
|
+
];
|
|
1224
|
+
const need = (k) => field(md, k) ?? this.fail(md, `the metadata needs ${k}`);
|
|
1225
|
+
const fields = [
|
|
1226
|
+
{ bytes: [120, 121, 23, 146, 173, 110, 199, 205] },
|
|
1227
|
+
{ value: need("name"), type: STRING },
|
|
1228
|
+
{ value: need("symbol"), type: STRING },
|
|
1229
|
+
{ value: need("uri"), type: STRING },
|
|
1230
|
+
{ value: need("sellerFeeBasisPoints"), type: { kind: "int", name: "u16" } },
|
|
1231
|
+
{ bytes: [0] }, // primary_sale_happened
|
|
1232
|
+
];
|
|
1233
|
+
const mutable = field(md, "isMutable");
|
|
1234
|
+
fields.push(mutable ? { value: mutable, type: BOOL } : { bytes: [1] });
|
|
1235
|
+
fields.push({ bytes: [1, 0] }); // token_standard: Some(NonFungible)
|
|
1236
|
+
const creators = field(md, "creators");
|
|
1237
|
+
if (creators && !ts.isArrayLiteralExpression(creators))
|
|
1238
|
+
this.fail(creators, "creators: [{ address, verified, share }, …] (an array literal)");
|
|
1239
|
+
const list = creators ? creators.elements : [];
|
|
1240
|
+
if (list.length > 5)
|
|
1241
|
+
this.fail(creators, "Bubblegum takes at most 5 creators");
|
|
1242
|
+
fields.push({ bytes: [list.length, 0, 0, 0] });
|
|
1243
|
+
let shares = 0n;
|
|
1244
|
+
let constant = true;
|
|
1245
|
+
for (const el of list) {
|
|
1246
|
+
if (!ts.isObjectLiteralExpression(el))
|
|
1247
|
+
this.fail(el, "a creator is { address, verified, share }");
|
|
1248
|
+
const f = (k) => field(el, k) ?? this.fail(el, `a creator needs ${k}`);
|
|
1249
|
+
const share = f("share");
|
|
1250
|
+
if (ts.isBigIntLiteral(share))
|
|
1251
|
+
shares += BigInt(share.text.slice(0, -1));
|
|
1252
|
+
else
|
|
1253
|
+
constant = false;
|
|
1254
|
+
fields.push({ value: f("address"), type: PUBKEY }, { value: f("verified"), type: BOOL }, { value: share, type: U8 });
|
|
1255
|
+
}
|
|
1256
|
+
if (list.length && constant && shares !== 100n)
|
|
1257
|
+
this.fail(creators, `creator shares add up to ${shares}; Bubblegum requires 100`);
|
|
1258
|
+
// collection: Some(the core collection's key) when minting into one.
|
|
1259
|
+
if (coll)
|
|
1260
|
+
fields.push({ bytes: [1] }, { value: this.keyOf(coll[0]), type: PUBKEY });
|
|
1261
|
+
else
|
|
1262
|
+
fields.push({ bytes: [0] });
|
|
1263
|
+
fields.push({ bytes: [0, 0] }); // asset_data, asset_data_schema: None
|
|
1264
|
+
const data = this.mplData(node, 1024, fields);
|
|
1265
|
+
this.cpi(p, metas, data.buf, data.len, seedsAt(collection ? 12 : 11));
|
|
1266
|
+
return undefined;
|
|
1267
|
+
}
|
|
1268
|
+
case "transferV2": {
|
|
1269
|
+
// TransferV2: treeConfig, payer, authority, leafOwner, leafDelegate, newLeafOwner, merkleTree, coreCollection,
|
|
1270
|
+
// logWrapper, compressionProgram, systemProgram; the proof follows as remaining accounts.
|
|
1271
|
+
if (a.length !== 13 && a.length !== 14)
|
|
1272
|
+
this.fail(node, "transferV2(treeConfig, payer, authority | null, leafOwner, leafDelegate | null, newLeafOwner, merkleTree, coreCollection | null, logWrapper, compressionProgram, systemProgram, leaf, proof, signerSeeds?)");
|
|
1273
|
+
const metas = [
|
|
1274
|
+
acct(0, "the tree config", true, false),
|
|
1275
|
+
acct(1, "the payer", true, true),
|
|
1276
|
+
acct(2, "the authority", false, true),
|
|
1277
|
+
acct(3, "the leaf owner", false, false),
|
|
1278
|
+
acct(4, "the leaf delegate", false, false),
|
|
1279
|
+
acct(5, "the new leaf owner", false, false),
|
|
1280
|
+
acct(6, "the merkle tree", true, false),
|
|
1281
|
+
acct(7, "the core collection", false, false),
|
|
1282
|
+
acct(8, "the log wrapper", false, false),
|
|
1283
|
+
acct(9, "the compression program", false, false),
|
|
1284
|
+
acct(10, "the system program", false, false),
|
|
1285
|
+
];
|
|
1286
|
+
const data = this.mplData(node, 160, [{ bytes: [119, 40, 6, 235, 234, 221, 248, 49] }, ...leafFields(a[11])]);
|
|
1287
|
+
this.cpi(p, metas, data.buf, data.len, seedsAt(13), proofRange(a[12]));
|
|
1288
|
+
return undefined;
|
|
1289
|
+
}
|
|
1290
|
+
case "burnV2": {
|
|
1291
|
+
// BurnV2: treeConfig, payer, authority, leafOwner, leafDelegate, merkleTree, coreCollection, mplCoreCpiSigner,
|
|
1292
|
+
// logWrapper, compressionProgram, mplCoreProgram, systemProgram; the proof follows as remaining accounts.
|
|
1293
|
+
if (a.length !== 14 && a.length !== 15)
|
|
1294
|
+
this.fail(node, "burnV2(treeConfig, payer, authority | null, leafOwner, leafDelegate | null, merkleTree, coreCollection | null, mplCoreCpiSigner | null, logWrapper, compressionProgram, mplCoreProgram, systemProgram, leaf, proof, signerSeeds?)");
|
|
1295
|
+
const metas = [
|
|
1296
|
+
acct(0, "the tree config", true, false),
|
|
1297
|
+
acct(1, "the payer", true, true),
|
|
1298
|
+
acct(2, "the authority", false, true),
|
|
1299
|
+
acct(3, "the leaf owner", false, false),
|
|
1300
|
+
acct(4, "the leaf delegate", false, false),
|
|
1301
|
+
acct(5, "the merkle tree", true, false),
|
|
1302
|
+
acct(6, "the core collection", true, false),
|
|
1303
|
+
acct(7, "the mpl-core CPI signer", false, false),
|
|
1304
|
+
acct(8, "the log wrapper", false, false),
|
|
1305
|
+
acct(9, "the compression program", false, false),
|
|
1306
|
+
acct(10, "the mpl-core program", false, false),
|
|
1307
|
+
acct(11, "the system program", false, false),
|
|
1308
|
+
];
|
|
1309
|
+
const data = this.mplData(node, 160, [{ bytes: [115, 210, 34, 240, 232, 143, 183, 16] }, ...leafFields(a[12])]);
|
|
1310
|
+
this.cpi(p, metas, data.buf, data.len, seedsAt(14), proofRange(a[13]));
|
|
1311
|
+
return undefined;
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
this.fail(node.expression, `Bubblegum supports createTree, mint, createTreeV2, mintV2, transferV2 and burnV2, not ${method}`);
|
|
1315
|
+
}
|
|
1316
|
+
/** `acct.key` of an account parameter, as an expression to serialize. */
|
|
1317
|
+
keyOf(n) {
|
|
1318
|
+
const f = ts.factory;
|
|
1319
|
+
const e = f.createPropertyAccessExpression(n, "key");
|
|
1320
|
+
ts.setTextRange(e, n);
|
|
1321
|
+
e.parent = n.parent;
|
|
1322
|
+
return e;
|
|
823
1323
|
}
|
|
824
1324
|
/**
|
|
825
1325
|
* `remainingAccounts()` (or a const bound to it): every remaining account; `rest.slice(a, b?)`: indexes a..b of them
|
|
@@ -1106,6 +1606,8 @@ export class CpiEmitter extends CallEmitter {
|
|
|
1106
1606
|
const s = targ && ts.isTypeReferenceNode(targ) && ts.isIdentifier(targ.typeName) ? this.model.structs.get(targ.typeName.text) : undefined;
|
|
1107
1607
|
if (!s)
|
|
1108
1608
|
this.fail(node, "emit needs an explicit event type: emit<MyEvent>({ ... })");
|
|
1609
|
+
if (s.layout === "borsh")
|
|
1610
|
+
this.fail(node, `${s.name} is @layout borsh, an account layout; declare a separate event struct`);
|
|
1109
1611
|
const obj = node.arguments[0];
|
|
1110
1612
|
if (node.arguments.length !== (cpi ? 3 : 1) || !ts.isObjectLiteralExpression(obj))
|
|
1111
1613
|
this.fail(node, "emit<T>({ field: value, ... }) takes one object literal");
|