token.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242
  1. /**
  2. * @flow
  3. */
  4. import assert from 'assert';
  5. import BN from 'bn.js';
  6. import * as BufferLayout from 'buffer-layout';
  7. import {
  8. Account,
  9. PublicKey,
  10. SystemProgram,
  11. Transaction,
  12. TransactionInstruction,
  13. } from '@solana/web3.js';
  14. import type {Connection, TransactionSignature} from '@solana/web3.js';
  15. import * as Layout from './layout';
  16. import {sendAndConfirmTransaction} from './util/send-and-confirm-transaction';
  17. /**
  18. * 64-bit value
  19. */
  20. export class u64 extends BN {
  21. /**
  22. * Convert to Buffer representation
  23. */
  24. toBuffer(): Buffer {
  25. const a = super.toArray().reverse();
  26. const b = Buffer.from(a);
  27. if (b.length === 8) {
  28. return b;
  29. }
  30. assert(b.length < 8, 'u64 too large');
  31. const zeroPad = Buffer.alloc(8);
  32. b.copy(zeroPad);
  33. return zeroPad;
  34. }
  35. /**
  36. * Construct a u64 from Buffer representation
  37. */
  38. static fromBuffer(buffer: Buffer): u64 {
  39. assert(buffer.length === 8, `Invalid buffer length: ${buffer.length}`);
  40. return new BN(
  41. [...buffer]
  42. .reverse()
  43. .map(i => `00${i.toString(16)}`.slice(-2))
  44. .join(''),
  45. 16,
  46. );
  47. }
  48. }
  49. function isAccount(accountOrPublicKey: any): boolean {
  50. return 'publicKey' in accountOrPublicKey;
  51. }
  52. /**
  53. * Information about the mint
  54. */
  55. type MintInfo = {|
  56. /**
  57. * Owner of the mint, given authority to mint new tokens
  58. */
  59. owner: null | PublicKey,
  60. /**
  61. * Number of base 10 digits to the right of the decimal place
  62. */
  63. decimals: number,
  64. /**
  65. * Is this mint initialized
  66. */
  67. initialized: boolean,
  68. |};
  69. const MintLayout = BufferLayout.struct([
  70. BufferLayout.u32('option'),
  71. Layout.publicKey('owner'),
  72. BufferLayout.u8('decimals'),
  73. BufferLayout.u8('is_initialized'),
  74. BufferLayout.u16('padding'),
  75. ]);
  76. /**
  77. * Information about an account
  78. */
  79. type AccountInfo = {|
  80. /**
  81. * The mint associated with this account
  82. */
  83. mint: PublicKey,
  84. /**
  85. * Owner of this account
  86. */
  87. owner: PublicKey,
  88. /**
  89. * Amount of tokens this account holds
  90. */
  91. amount: u64,
  92. /**
  93. * The delegate for this account
  94. */
  95. delegate: null | PublicKey,
  96. /**
  97. * The amount of tokens the delegate authorized to the delegate
  98. */
  99. delegatedAmount: u64,
  100. /**
  101. * Is this account initialized
  102. */
  103. isInitialized: boolean,
  104. /**
  105. * Is this a native token account
  106. */
  107. isNative: boolean,
  108. |};
  109. /**
  110. * @private
  111. */
  112. const AccountLayout = BufferLayout.struct([
  113. Layout.publicKey('mint'),
  114. Layout.publicKey('owner'),
  115. Layout.uint64('amount'),
  116. BufferLayout.u32('option'),
  117. Layout.publicKey('delegate'),
  118. BufferLayout.u8('is_initialized'),
  119. BufferLayout.u8('is_native'),
  120. BufferLayout.u16('padding'),
  121. Layout.uint64('delegatedAmount'),
  122. ]);
  123. /**
  124. * Information about an multisig
  125. */
  126. type MultisigInfo = {|
  127. /**
  128. * The number of signers required
  129. */
  130. m: number,
  131. /**
  132. * Number of possible signers, corresponds to the
  133. * number of `signers` that are valid.
  134. */
  135. n: number,
  136. /**
  137. * Is this mint initialized
  138. */
  139. initialized: boolean,
  140. /**
  141. * The signers
  142. */
  143. signer1: PublicKey,
  144. signer2: PublicKey,
  145. signer3: PublicKey,
  146. signer4: PublicKey,
  147. signer5: PublicKey,
  148. signer6: PublicKey,
  149. signer7: PublicKey,
  150. signer8: PublicKey,
  151. signer9: PublicKey,
  152. signer10: PublicKey,
  153. signer11: PublicKey,
  154. |};
  155. /**
  156. * @private
  157. */
  158. const MultisigLayout = BufferLayout.struct([
  159. BufferLayout.u8('m'),
  160. BufferLayout.u8('n'),
  161. BufferLayout.u8('is_initialized'),
  162. Layout.publicKey('signer1'),
  163. Layout.publicKey('signer2'),
  164. Layout.publicKey('signer3'),
  165. Layout.publicKey('signer4'),
  166. Layout.publicKey('signer5'),
  167. Layout.publicKey('signer6'),
  168. Layout.publicKey('signer7'),
  169. Layout.publicKey('signer8'),
  170. Layout.publicKey('signer9'),
  171. Layout.publicKey('signer10'),
  172. Layout.publicKey('signer11'),
  173. ]);
  174. type TokenAndPublicKey = [Token, PublicKey]; // This type exists to workaround an esdoc parse error
  175. /**
  176. * An ERC20-like Token
  177. */
  178. export class Token {
  179. /**
  180. * @private
  181. */
  182. connection: Connection;
  183. /**
  184. * The public key identifying this mint
  185. */
  186. publicKey: PublicKey;
  187. /**
  188. * Program Identifier for the Token program
  189. */
  190. programId: PublicKey;
  191. /**
  192. * Fee payer
  193. */
  194. payer: Account;
  195. /**
  196. * Create a Token object attached to the specific mint
  197. *
  198. * @param connection The connection to use
  199. * @param token Public key of the mint
  200. * @param programId token programId
  201. * @param payer Payer of fees
  202. */
  203. constructor(
  204. connection: Connection,
  205. publicKey: PublicKey,
  206. programId: PublicKey,
  207. payer: Account,
  208. ) {
  209. Object.assign(this, {connection, publicKey, programId, payer});
  210. }
  211. /**
  212. * Get the minimum balance for the mint to be rent exempt
  213. *
  214. * @return Number of lamports required
  215. */
  216. static async getMinBalanceRentForExemptMint(
  217. connection: Connection,
  218. ): Promise<number> {
  219. return await connection.getMinimumBalanceForRentExemption(MintLayout.span);
  220. }
  221. /**
  222. * Get the minimum balance for the account to be rent exempt
  223. *
  224. * @return Number of lamports required
  225. */
  226. static async getMinBalanceRentForExemptAccount(
  227. connection: Connection,
  228. ): Promise<number> {
  229. return await connection.getMinimumBalanceForRentExemption(
  230. AccountLayout.span,
  231. );
  232. }
  233. /**
  234. * Get the minimum balance for the multsig to be rent exempt
  235. *
  236. * @return Number of lamports required
  237. */
  238. static async getMinBalanceRentForExemptMultisig(
  239. connection: Connection,
  240. ): Promise<number> {
  241. return await connection.getMinimumBalanceForRentExemption(
  242. MultisigLayout.span,
  243. );
  244. }
  245. /**
  246. * Creates and initializes a token.
  247. *
  248. * @param connection The connection to use
  249. * @param owner User account that will own the returned account
  250. * @param supply Initial supply to mint
  251. * @param decimals Location of the decimal place
  252. * @param programId Optional token programId, uses the system programId by default
  253. * @return Token object for the newly minted token, Public key of the account
  254. * holding the total amount of new tokens
  255. */
  256. static async createMint(
  257. connection: Connection,
  258. payer: Account,
  259. mintOwner: PublicKey,
  260. accountOwner: PublicKey,
  261. supply: u64,
  262. decimals: number,
  263. programId: PublicKey,
  264. is_owned: boolean = false,
  265. ): Promise<TokenAndPublicKey> {
  266. let transaction;
  267. const mintAccount = new Account();
  268. const token = new Token(
  269. connection,
  270. mintAccount.publicKey,
  271. programId,
  272. payer,
  273. );
  274. const initialAccountPublicKey = await token.createAccount(accountOwner);
  275. // Allocate memory for the account
  276. const balanceNeeded = await Token.getMinBalanceRentForExemptMint(
  277. connection,
  278. );
  279. transaction = SystemProgram.createAccount({
  280. fromPubkey: payer.publicKey,
  281. newAccountPubkey: mintAccount.publicKey,
  282. lamports: balanceNeeded,
  283. space: MintLayout.span,
  284. programId,
  285. });
  286. // Create the mint
  287. let keys = [
  288. {pubkey: mintAccount.publicKey, isSigner: false, isWritable: true},
  289. ];
  290. if (supply.toNumber() != 0) {
  291. keys.push({
  292. pubkey: initialAccountPublicKey,
  293. isSigner: false,
  294. isWritable: true,
  295. });
  296. }
  297. if (is_owned) {
  298. keys.push({pubkey: mintOwner, isSigner: false, isWritable: false});
  299. }
  300. const commandDataLayout = BufferLayout.struct([
  301. BufferLayout.u8('instruction'),
  302. Layout.uint64('supply'),
  303. BufferLayout.u8('decimals'),
  304. ]);
  305. let data = Buffer.alloc(1024);
  306. {
  307. const encodeLength = commandDataLayout.encode(
  308. {
  309. instruction: 0, // InitializeMint instruction
  310. supply: supply.toBuffer(),
  311. decimals,
  312. },
  313. data,
  314. );
  315. data = data.slice(0, encodeLength);
  316. }
  317. transaction.add({
  318. keys,
  319. programId,
  320. data,
  321. });
  322. // Send the two instructions
  323. await sendAndConfirmTransaction(
  324. 'createAccount and InitializeMint',
  325. connection,
  326. transaction,
  327. payer,
  328. mintAccount,
  329. );
  330. return [token, initialAccountPublicKey];
  331. }
  332. /**
  333. * Create and initializes a new account.
  334. *
  335. * This account may then be used as a `transfer()` or `approve()` destination
  336. *
  337. * @param owner User account that will own the new account
  338. * @return Public key of the new empty account
  339. */
  340. async createAccount(owner: PublicKey): Promise<PublicKey> {
  341. const mintAccount = new Account();
  342. let transaction;
  343. // Allocate memory for the account
  344. const balanceNeeded = await Token.getMinBalanceRentForExemptAccount(
  345. this.connection,
  346. );
  347. transaction = SystemProgram.createAccount({
  348. fromPubkey: this.payer.publicKey,
  349. newAccountPubkey: mintAccount.publicKey,
  350. lamports: balanceNeeded,
  351. space: AccountLayout.span,
  352. programId: this.programId,
  353. });
  354. // create the new account
  355. const keys = [
  356. {pubkey: mintAccount.publicKey, isSigner: false, isWritable: true},
  357. {pubkey: this.publicKey, isSigner: false, isWritable: false},
  358. {pubkey: owner, isSigner: false, isWritable: false},
  359. ];
  360. const dataLayout = BufferLayout.struct([BufferLayout.u8('instruction')]);
  361. const data = Buffer.alloc(dataLayout.span);
  362. dataLayout.encode(
  363. {
  364. instruction: 1, // InitializeAccount instruction
  365. },
  366. data,
  367. );
  368. transaction.add({
  369. keys,
  370. programId: this.programId,
  371. data,
  372. });
  373. // Send the two instructions
  374. await sendAndConfirmTransaction(
  375. 'createAccount and InitializeAccount',
  376. this.connection,
  377. transaction,
  378. this.payer,
  379. mintAccount,
  380. );
  381. return mintAccount.publicKey;
  382. }
  383. /**
  384. * Create and initializes a new multisig.
  385. *
  386. * This account may then be used for multisignature verification
  387. *
  388. * @param owner User account that will own the multsig account
  389. * @return Public key of the new multisig account
  390. */
  391. async createMultisig(
  392. m: number,
  393. signers: Array<PublicKey>,
  394. ): Promise<PublicKey> {
  395. const multisigAccount = new Account();
  396. let transaction;
  397. // Allocate memory for the account
  398. const balanceNeeded = await Token.getMinBalanceRentForExemptMultisig(
  399. this.connection,
  400. );
  401. transaction = SystemProgram.createAccount({
  402. fromPubkey: this.payer.publicKey,
  403. newAccountPubkey: multisigAccount.publicKey,
  404. lamports: balanceNeeded,
  405. space: MultisigLayout.span,
  406. programId: this.programId,
  407. });
  408. // create the new account
  409. let keys = [
  410. {pubkey: multisigAccount.publicKey, isSigner: false, isWritable: true},
  411. ];
  412. signers.forEach(signer =>
  413. keys.push({pubkey: signer, isSigner: false, isWritable: false}),
  414. );
  415. const dataLayout = BufferLayout.struct([
  416. BufferLayout.u8('instruction'),
  417. BufferLayout.u8('m'),
  418. ]);
  419. const data = Buffer.alloc(dataLayout.span);
  420. dataLayout.encode(
  421. {
  422. instruction: 2, // InitializeM<ultisig instruction
  423. m,
  424. },
  425. data,
  426. );
  427. transaction.add({
  428. keys,
  429. programId: this.programId,
  430. data,
  431. });
  432. // Send the two instructions
  433. await sendAndConfirmTransaction(
  434. 'createAccount and InitializeMultisig',
  435. this.connection,
  436. transaction,
  437. this.payer,
  438. multisigAccount,
  439. );
  440. return multisigAccount.publicKey;
  441. }
  442. /**
  443. * Retrieve mint information
  444. */
  445. async getMintInfo(): Promise<MintInfo> {
  446. const info = await this.connection.getAccountInfo(this.publicKey);
  447. if (info === null) {
  448. throw new Error('Failed to find mint account');
  449. }
  450. if (!info.owner.equals(this.programId)) {
  451. throw new Error(`Invalid mint owner: ${JSON.stringify(info.owner)}`);
  452. }
  453. if (info.data.length != MintLayout.span) {
  454. throw new Error(`Invalid mint size`);
  455. }
  456. const data = Buffer.from(info.data);
  457. const mintInfo = MintLayout.decode(data);
  458. if (mintInfo.option === 0) {
  459. mintInfo.owner = null;
  460. } else {
  461. mintInfo.owner = new PublicKey(mintInfo.owner);
  462. }
  463. return mintInfo;
  464. }
  465. /**
  466. * Retrieve account information
  467. *
  468. * @param account Public key of the account
  469. */
  470. async getAccountInfo(account: PublicKey): Promise<AccountInfo> {
  471. const info = await this.connection.getAccountInfo(account);
  472. if (info === null) {
  473. throw new Error('Failed to find account');
  474. }
  475. if (!info.owner.equals(this.programId)) {
  476. throw new Error(`Invalid account owner`);
  477. }
  478. if (info.data.length != AccountLayout.span) {
  479. throw new Error(`Invalid account size`);
  480. }
  481. const data = Buffer.from(info.data);
  482. const accountInfo = AccountLayout.decode(data);
  483. accountInfo.mint = new PublicKey(accountInfo.mint);
  484. accountInfo.owner = new PublicKey(accountInfo.owner);
  485. accountInfo.amount = u64.fromBuffer(accountInfo.amount);
  486. accountInfo.isInitialized = accountInfo.isInitialized != 0;
  487. accountInfo.isNative = accountInfo.isNative != 0;
  488. if (accountInfo.option === 0) {
  489. accountInfo.delegate = null;
  490. accountInfo.delegatedAmount = new u64();
  491. } else {
  492. accountInfo.delegate = new PublicKey(accountInfo.delegate);
  493. accountInfo.delegatedAmount = u64.fromBuffer(accountInfo.delegatedAmount);
  494. }
  495. if (!accountInfo.mint.equals(this.publicKey)) {
  496. throw new Error(
  497. `Invalid account mint: ${JSON.stringify(
  498. accountInfo.mint,
  499. )} !== ${JSON.stringify(this.publicKey)}`,
  500. );
  501. }
  502. return accountInfo;
  503. }
  504. /**
  505. * Retrieve Multisig information
  506. *
  507. * @param multisig Public key of the account
  508. */
  509. async getMultisigInfo(multisig: PublicKey): Promise<MultisigInfo> {
  510. const info = await this.connection.getAccountInfo(multisig);
  511. if (info === null) {
  512. throw new Error('Failed to find multisig');
  513. }
  514. if (!info.owner.equals(this.programId)) {
  515. throw new Error(`Invalid multisig owner`);
  516. }
  517. if (info.data.length != MultisigLayout.span) {
  518. throw new Error(`Invalid multisig size`);
  519. }
  520. const data = Buffer.from(info.data);
  521. const multisigInfo = MultisigLayout.decode(data);
  522. multisigInfo.signer1 = new PublicKey(multisigInfo.signer1);
  523. multisigInfo.signer2 = new PublicKey(multisigInfo.signer2);
  524. multisigInfo.signer3 = new PublicKey(multisigInfo.signer3);
  525. multisigInfo.signer4 = new PublicKey(multisigInfo.signer4);
  526. multisigInfo.signer5 = new PublicKey(multisigInfo.signer5);
  527. multisigInfo.signer6 = new PublicKey(multisigInfo.signer6);
  528. multisigInfo.signer7 = new PublicKey(multisigInfo.signer7);
  529. multisigInfo.signer8 = new PublicKey(multisigInfo.signer8);
  530. multisigInfo.signer9 = new PublicKey(multisigInfo.signer9);
  531. multisigInfo.signer10 = new PublicKey(multisigInfo.signer10);
  532. multisigInfo.signer11 = new PublicKey(multisigInfo.signer11);
  533. return multisigInfo;
  534. }
  535. /**
  536. * Transfer tokens to another account
  537. *
  538. * @param source Source account
  539. * @param destination Destination account
  540. * @param authority Owner of the source account
  541. * @param multiSigners Signing accounts if `authority` is a multiSig
  542. * @param amount Number of tokens to transfer
  543. */
  544. async transfer(
  545. source: PublicKey,
  546. destination: PublicKey,
  547. authority: any,
  548. multiSigners: Array<Account>,
  549. amount: number | u64,
  550. ): Promise<TransactionSignature> {
  551. let ownerPublicKey;
  552. let signers;
  553. if (isAccount(authority)) {
  554. ownerPublicKey = authority.publicKey;
  555. signers = [authority];
  556. } else {
  557. ownerPublicKey = authority;
  558. signers = multiSigners;
  559. }
  560. return await sendAndConfirmTransaction(
  561. 'Transfer',
  562. this.connection,
  563. new Transaction().add(
  564. Token.createTransferInstruction(
  565. this.programId,
  566. source,
  567. destination,
  568. ownerPublicKey,
  569. multiSigners,
  570. amount,
  571. ),
  572. ),
  573. this.payer,
  574. ...signers,
  575. );
  576. }
  577. /**
  578. * Grant a third-party permission to transfer up the specified number of tokens from an account
  579. *
  580. * @param account Public key of the account
  581. * @param delegate Account authorized to perform a transfer tokens from the source account
  582. * @param owner Owner of the source account
  583. * @param multiSigners Signing accounts if `owner` is a multiSig
  584. * @param amount Maximum number of tokens the delegate may transfer
  585. */
  586. async approve(
  587. account: PublicKey,
  588. delegate: PublicKey,
  589. owner: any,
  590. multiSigners: Array<Account>,
  591. amount: number | u64,
  592. ): Promise<void> {
  593. let ownerPublicKey;
  594. let signers;
  595. if (isAccount(owner)) {
  596. ownerPublicKey = owner.publicKey;
  597. signers = [owner];
  598. } else {
  599. ownerPublicKey = owner;
  600. signers = multiSigners;
  601. }
  602. await sendAndConfirmTransaction(
  603. 'Approve',
  604. this.connection,
  605. new Transaction().add(
  606. Token.createApproveInstruction(
  607. this.programId,
  608. account,
  609. delegate,
  610. ownerPublicKey,
  611. multiSigners,
  612. amount,
  613. ),
  614. ),
  615. this.payer,
  616. ...signers,
  617. );
  618. }
  619. /**
  620. * Remove approval for the transfer of any remaining tokens
  621. *
  622. * @param account Public key of the account
  623. * @param owner Owner of the source account
  624. * @param multiSigners Signing accounts if `owner` is a multiSig
  625. */
  626. async revoke(
  627. account: PublicKey,
  628. owner: any,
  629. multiSigners: Array<Account>,
  630. ): Promise<void> {
  631. let ownerPublicKey;
  632. let signers;
  633. if (isAccount(owner)) {
  634. ownerPublicKey = owner.publicKey;
  635. signers = [owner];
  636. } else {
  637. ownerPublicKey = owner;
  638. signers = multiSigners;
  639. }
  640. await sendAndConfirmTransaction(
  641. 'Revoke',
  642. this.connection,
  643. new Transaction().add(
  644. Token.createRevokeInstruction(
  645. this.programId,
  646. account,
  647. ownerPublicKey,
  648. multiSigners,
  649. ),
  650. ),
  651. this.payer,
  652. ...signers,
  653. );
  654. }
  655. /**
  656. * Assign a new owner to the account
  657. *
  658. * @param account Public key of the account
  659. * @param newOwner New owner of the account
  660. * @param owner Owner of the account
  661. * @param multiSigners Signing accounts if `owner` is a multiSig
  662. */
  663. async setOwner(
  664. owned: PublicKey,
  665. newOwner: PublicKey,
  666. owner: any,
  667. multiSigners: Array<Account>,
  668. ): Promise<void> {
  669. let ownerPublicKey;
  670. let signers;
  671. if (isAccount(owner)) {
  672. ownerPublicKey = owner.publicKey;
  673. signers = [owner];
  674. } else {
  675. ownerPublicKey = owner;
  676. signers = multiSigners;
  677. }
  678. await sendAndConfirmTransaction(
  679. 'SetOwner',
  680. this.connection,
  681. new Transaction().add(
  682. Token.createSetOwnerInstruction(
  683. this.programId,
  684. owned,
  685. newOwner,
  686. ownerPublicKey,
  687. multiSigners,
  688. ),
  689. ),
  690. this.payer,
  691. ...signers,
  692. );
  693. }
  694. /**
  695. * Mint new tokens
  696. *
  697. * @param mint Public key of the mint
  698. * @param dest Public key of the account to mint to
  699. * @param authority Owner of the mint
  700. * @param multiSigners Signing accounts if `authority` is a multiSig
  701. * @param amount ammount to mint
  702. */
  703. async mintTo(
  704. dest: PublicKey,
  705. authority: any,
  706. multiSigners: Array<Account>,
  707. amount: number,
  708. ): Promise<void> {
  709. let ownerPublicKey;
  710. let signers;
  711. if (isAccount(authority)) {
  712. ownerPublicKey = authority.publicKey;
  713. signers = [authority];
  714. } else {
  715. ownerPublicKey = authority;
  716. signers = multiSigners;
  717. }
  718. await sendAndConfirmTransaction(
  719. 'MintTo',
  720. this.connection,
  721. new Transaction().add(
  722. Token.createMintToInstruction(
  723. this.programId,
  724. this.publicKey,
  725. dest,
  726. ownerPublicKey,
  727. multiSigners,
  728. amount,
  729. ),
  730. ),
  731. this.payer,
  732. ...signers,
  733. );
  734. }
  735. /**
  736. * Burn tokens
  737. *
  738. * @param account Account to burn tokens from
  739. * @param authority Public key account owner
  740. * @param multiSigners Signing accounts if `authority` is a multiSig
  741. * @param amount ammount to burn
  742. */
  743. async burn(
  744. account: PublicKey,
  745. authority: any,
  746. multiSigners: Array<Account>,
  747. amount: number,
  748. ): Promise<void> {
  749. let ownerPublicKey;
  750. let signers;
  751. if (isAccount(authority)) {
  752. ownerPublicKey = authority.publicKey;
  753. signers = [authority];
  754. } else {
  755. ownerPublicKey = authority;
  756. signers = multiSigners;
  757. }
  758. await sendAndConfirmTransaction(
  759. 'Burn',
  760. this.connection,
  761. new Transaction().add(
  762. Token.createBurnInstruction(
  763. this.programId,
  764. account,
  765. ownerPublicKey,
  766. multiSigners,
  767. amount,
  768. ),
  769. ),
  770. this.payer,
  771. ...signers,
  772. );
  773. }
  774. /**
  775. * Burn account
  776. *
  777. * @param account Account to burn
  778. * @param authority account owner
  779. * @param multiSigners Signing accounts if `owner` is a multiSig
  780. */
  781. async closeAccount(
  782. account: PublicKey,
  783. dest: PublicKey,
  784. owner: any,
  785. multiSigners: Array<Account>,
  786. ): Promise<void> {
  787. let ownerPublicKey;
  788. let signers;
  789. if (isAccount(owner)) {
  790. ownerPublicKey = owner.publicKey;
  791. signers = [owner];
  792. } else {
  793. ownerPublicKey = owner;
  794. signers = multiSigners;
  795. }
  796. await sendAndConfirmTransaction(
  797. 'CloseAccount',
  798. this.connection,
  799. new Transaction().add(
  800. Token.createCloseAccountInstruction(
  801. this.programId,
  802. account,
  803. dest,
  804. ownerPublicKey,
  805. multiSigners,
  806. ),
  807. ),
  808. this.payer,
  809. ...signers,
  810. );
  811. }
  812. /**
  813. * Construct a Transfer instruction
  814. *
  815. * @param source Source account
  816. * @param destination Destination account
  817. * @param authority Owner of the source account
  818. * @param multiSigners Signing accounts if `authority` is a multiSig
  819. * @param amount Number of tokens to transfer
  820. */
  821. static createTransferInstruction(
  822. programId: PublicKey,
  823. source: PublicKey,
  824. destination: PublicKey,
  825. authority: any,
  826. multiSigners: Array<Account>,
  827. amount: number | u64,
  828. ): TransactionInstruction {
  829. const dataLayout = BufferLayout.struct([
  830. BufferLayout.u8('instruction'),
  831. Layout.uint64('amount'),
  832. ]);
  833. const data = Buffer.alloc(dataLayout.span);
  834. dataLayout.encode(
  835. {
  836. instruction: 3, // Transfer instruction
  837. amount: new u64(amount).toBuffer(),
  838. },
  839. data,
  840. );
  841. let keys = [
  842. {pubkey: source, isSigner: false, isWritable: true},
  843. {pubkey: destination, isSigner: false, isWritable: true},
  844. ];
  845. if (isAccount(authority)) {
  846. keys.push({
  847. pubkey: authority.publicKey,
  848. isSigner: true,
  849. isWritable: false,
  850. });
  851. } else {
  852. keys.push({pubkey: authority, isSigner: false, isWritable: false});
  853. multiSigners.forEach(signer =>
  854. keys.push({
  855. pubkey: signer.publicKey,
  856. isSigner: true,
  857. isWritable: false,
  858. }),
  859. );
  860. }
  861. return new TransactionInstruction({
  862. keys,
  863. programId: programId,
  864. data,
  865. });
  866. }
  867. /**
  868. * Construct an Approve instruction
  869. *
  870. * @param account Public key of the account
  871. * @param delegate Account authorized to perform a transfer of tokens from the source account
  872. * @param owner Owner of the source account
  873. * @param multiSigners Signing accounts if `owner` is a multiSig
  874. * @param amount Maximum number of tokens the delegate may transfer
  875. */
  876. static createApproveInstruction(
  877. programId: PublicKey,
  878. account: PublicKey,
  879. delegate: PublicKey,
  880. owner: any,
  881. multiSigners: Array<Account>,
  882. amount: number | u64,
  883. ): TransactionInstruction {
  884. const dataLayout = BufferLayout.struct([
  885. BufferLayout.u8('instruction'),
  886. Layout.uint64('amount'),
  887. ]);
  888. const data = Buffer.alloc(dataLayout.span);
  889. dataLayout.encode(
  890. {
  891. instruction: 4, // Approve instruction
  892. amount: new u64(amount).toBuffer(),
  893. },
  894. data,
  895. );
  896. let keys = [
  897. {pubkey: account, isSigner: false, isWritable: true},
  898. {pubkey: delegate, isSigner: false, isWritable: false},
  899. ];
  900. if (isAccount(owner)) {
  901. keys.push({pubkey: owner.publicKey, isSigner: true, isWritable: false});
  902. } else {
  903. keys.push({pubkey: owner, isSigner: false, isWritable: false});
  904. multiSigners.forEach(signer =>
  905. keys.push({
  906. pubkey: signer.publicKey,
  907. isSigner: true,
  908. isWritable: false,
  909. }),
  910. );
  911. }
  912. return new TransactionInstruction({
  913. keys,
  914. programId: programId,
  915. data,
  916. });
  917. }
  918. /**
  919. * Construct an Approve instruction
  920. *
  921. * @param account Public key of the account
  922. * @param delegate Account authorized to perform a transfer of tokens from the source account
  923. * @param owner Owner of the source account
  924. * @param multiSigners Signing accounts if `owner` is a multiSig
  925. * @param amount Maximum number of tokens the delegate may transfer
  926. */
  927. static createRevokeInstruction(
  928. programId: PublicKey,
  929. account: PublicKey,
  930. owner: any,
  931. multiSigners: Array<Account>,
  932. ): TransactionInstruction {
  933. const dataLayout = BufferLayout.struct([BufferLayout.u8('instruction')]);
  934. const data = Buffer.alloc(dataLayout.span);
  935. dataLayout.encode(
  936. {
  937. instruction: 5, // Approve instruction
  938. },
  939. data,
  940. );
  941. let keys = [{pubkey: account, isSigner: false, isWritable: true}];
  942. if (isAccount(owner)) {
  943. keys.push({pubkey: owner.publicKey, isSigner: true, isWritable: false});
  944. } else {
  945. keys.push({pubkey: owner, isSigner: false, isWritable: false});
  946. multiSigners.forEach(signer =>
  947. keys.push({
  948. pubkey: signer.publicKey,
  949. isSigner: true,
  950. isWritable: false,
  951. }),
  952. );
  953. }
  954. return new TransactionInstruction({
  955. keys,
  956. programId: programId,
  957. data,
  958. });
  959. }
  960. /**
  961. * Construct a SetOwner instruction
  962. *
  963. * @param account Public key of the account
  964. * @param newOwner New owner of the account
  965. * @param owner Owner of the account
  966. * @param multiSigners Signing accounts if `owner` is a multiSig
  967. */
  968. static createSetOwnerInstruction(
  969. programId: PublicKey,
  970. owned: PublicKey,
  971. newOwner: PublicKey,
  972. owner: any,
  973. multiSigners: Array<Account>,
  974. ): TransactionInstruction {
  975. const dataLayout = BufferLayout.struct([BufferLayout.u8('instruction')]);
  976. const data = Buffer.alloc(dataLayout.span);
  977. dataLayout.encode(
  978. {
  979. instruction: 6, // SetOwner instruction
  980. },
  981. data,
  982. );
  983. let keys = [
  984. {pubkey: owned, isSigner: false, isWritable: true},
  985. {pubkey: newOwner, isSigner: false, isWritable: false},
  986. ];
  987. if (isAccount(owner)) {
  988. keys.push({pubkey: owner.publicKey, isSigner: true, isWritable: false});
  989. } else {
  990. keys.push({pubkey: owner, isSigner: false, isWritable: false});
  991. multiSigners.forEach(signer =>
  992. keys.push({
  993. pubkey: signer.publicKey,
  994. isSigner: true,
  995. isWritable: false,
  996. }),
  997. );
  998. }
  999. return new TransactionInstruction({
  1000. keys,
  1001. programId: programId,
  1002. data,
  1003. });
  1004. }
  1005. /**
  1006. * Construct a MintTo instruction
  1007. *
  1008. * @param dest Public key of the account to mint to
  1009. * @param authority Owner of the mint
  1010. * @param multiSigners Signing accounts if `authority` is a multiSig
  1011. * @param amount amount to mint
  1012. */
  1013. static createMintToInstruction(
  1014. programId: PublicKey,
  1015. mint: PublicKey,
  1016. dest: PublicKey,
  1017. authority: any,
  1018. multiSigners: Array<Account>,
  1019. amount: number,
  1020. ): TransactionInstruction {
  1021. const dataLayout = BufferLayout.struct([
  1022. BufferLayout.u8('instruction'),
  1023. Layout.uint64('amount'),
  1024. ]);
  1025. const data = Buffer.alloc(dataLayout.span);
  1026. dataLayout.encode(
  1027. {
  1028. instruction: 7, // MintTo instruction
  1029. amount: new u64(amount).toBuffer(),
  1030. },
  1031. data,
  1032. );
  1033. let keys = [
  1034. {pubkey: mint, isSigner: false, isWritable: true},
  1035. {pubkey: dest, isSigner: false, isWritable: true},
  1036. ];
  1037. if (isAccount(authority)) {
  1038. keys.push({
  1039. pubkey: authority.publicKey,
  1040. isSigner: true,
  1041. isWritable: false,
  1042. });
  1043. } else {
  1044. keys.push({pubkey: authority, isSigner: false, isWritable: false});
  1045. multiSigners.forEach(signer =>
  1046. keys.push({
  1047. pubkey: signer.publicKey,
  1048. isSigner: true,
  1049. isWritable: false,
  1050. }),
  1051. );
  1052. }
  1053. return new TransactionInstruction({
  1054. keys,
  1055. programId: programId,
  1056. data,
  1057. });
  1058. }
  1059. /**
  1060. * Construct a Burn instruction
  1061. *
  1062. * @param account Account to burn tokens from
  1063. * @param authority Public key account owner
  1064. * @param multiSigners Signing accounts if `authority` is a multiSig
  1065. * @param amount ammount to burn
  1066. */
  1067. static createBurnInstruction(
  1068. programId: PublicKey,
  1069. account: PublicKey,
  1070. authority: any,
  1071. multiSigners: Array<Account>,
  1072. amount: number,
  1073. ): TransactionInstruction {
  1074. const dataLayout = BufferLayout.struct([
  1075. BufferLayout.u8('instruction'),
  1076. Layout.uint64('amount'),
  1077. ]);
  1078. const data = Buffer.alloc(dataLayout.span);
  1079. dataLayout.encode(
  1080. {
  1081. instruction: 8, // Burn instruction
  1082. amount: new u64(amount).toBuffer(),
  1083. },
  1084. data,
  1085. );
  1086. let keys = [{pubkey: account, isSigner: false, isWritable: true}];
  1087. if (isAccount(authority)) {
  1088. keys.push({
  1089. pubkey: authority.publicKey,
  1090. isSigner: true,
  1091. isWritable: false,
  1092. });
  1093. } else {
  1094. keys.push({pubkey: authority, isSigner: false, isWritable: false});
  1095. multiSigners.forEach(signer =>
  1096. keys.push({
  1097. pubkey: signer.publicKey,
  1098. isSigner: true,
  1099. isWritable: false,
  1100. }),
  1101. );
  1102. }
  1103. return new TransactionInstruction({
  1104. keys,
  1105. programId: programId,
  1106. data,
  1107. });
  1108. }
  1109. /**
  1110. * Construct a Burn instruction
  1111. *
  1112. * @param account Account to burn tokens from
  1113. * @param owner account owner
  1114. * @param multiSigners Signing accounts if `owner` is a multiSig
  1115. */
  1116. static createCloseAccountInstruction(
  1117. programId: PublicKey,
  1118. account: PublicKey,
  1119. dest: PublicKey,
  1120. owner: any,
  1121. multiSigners: Array<Account>,
  1122. ): TransactionInstruction {
  1123. const dataLayout = BufferLayout.struct([BufferLayout.u8('instruction')]);
  1124. const data = Buffer.alloc(dataLayout.span);
  1125. dataLayout.encode(
  1126. {
  1127. instruction: 9, // CloseAccount instruction
  1128. },
  1129. data,
  1130. );
  1131. let keys = [
  1132. {pubkey: account, isSigner: false, isWritable: true},
  1133. {pubkey: dest, isSigner: false, isWritable: true},
  1134. ];
  1135. if (isAccount(owner)) {
  1136. keys.push({pubkey: owner.publicKey, isSigner: true, isWritable: false});
  1137. } else {
  1138. keys.push({pubkey: owner, isSigner: false, isWritable: false});
  1139. multiSigners.forEach(signer =>
  1140. keys.push({
  1141. pubkey: signer.publicKey,
  1142. isSigner: true,
  1143. isWritable: false,
  1144. }),
  1145. );
  1146. }
  1147. return new TransactionInstruction({
  1148. keys,
  1149. programId: programId,
  1150. data,
  1151. });
  1152. }
  1153. }