@solana/spl-token#MintLayout TypeScript Examples

The following examples show how to use @solana/spl-token#MintLayout. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.ts    From zo-client with Apache License 2.0 6 votes vote down vote up
export async function createMintIxs(
  mint: Keypair,
  provider: Provider,
  authority: PublicKey,
  decimals: number,
  freezeAuthority?: PublicKey,
): Promise<TransactionInstruction[]> {
  return [
    SystemProgram.createAccount({
      fromPubkey: provider.wallet.publicKey,
      newAccountPubkey: mint.publicKey,
      space: MintLayout.span,
      lamports: await Token.getMinBalanceRentForExemptMint(provider.connection),
      programId: TOKEN_PROGRAM_ID,
    }),
    Token.createInitMintInstruction(
      TOKEN_PROGRAM_ID,
      mint.publicKey,
      decimals,
      authority,
      freezeAuthority ?? null,
    ),
  ];
}
Example #2
Source File: instructions.ts    From serum-ts with Apache License 2.0 6 votes vote down vote up
deserializeMint = (data: Buffer): MintInfo => {
  if (data.length !== MintLayout.span) {
    throw new Error('Not a valid Mint');
  }

  const mintInfo = MintLayout.decode(data);

  if (mintInfo.mintAuthorityOption === 0) {
    mintInfo.mintAuthority = null;
  } else {
    mintInfo.mintAuthority = new PublicKey(mintInfo.mintAuthority);
  }

  mintInfo.supply = u64.fromBuffer(mintInfo.supply);
  mintInfo.isInitialized = mintInfo.isInitialized !== 0;

  if (mintInfo.freezeAuthorityOption === 0) {
    mintInfo.freezeAuthority = null;
  } else {
    mintInfo.freezeAuthority = new PublicKey(mintInfo.freezeAuthority);
  }

  return mintInfo as MintInfo;
}
Example #3
Source File: getNewMint.ts    From metaplex with Apache License 2.0 6 votes vote down vote up
export async function getNewMint(
  wallet: WalletContextState,
  connection: Connection,
): Promise<Response> {
  const instructions: TransactionInstruction[] = [];
  const signers: Keypair[] = [];

  if (!wallet.publicKey) {
    throw new Error('Wallet pubKey is not provided');
  }

  const mintRentExempt = await connection.getMinimumBalanceForRentExemption(
    MintLayout.span,
  );

  const newMint = await createMintAndAccountWithOne(
    wallet,
    wallet.publicKey.toString(),
    mintRentExempt,
    instructions,
    signers,
  );

  return { ...newMint, instructions, signers };
}
Example #4
Source File: accounts.ts    From metaplex with Apache License 2.0 6 votes vote down vote up
getMintInfo = async (
  connection: Connection,
  mint: string,
): Promise<{ key: PublicKey; info: MintInfo }> => {
  let mintKey: PublicKey;
  try {
    mintKey = new PublicKey(mint);
  } catch (err) {
    throw new Error(`Invalid mint key ${err}`);
  }
  const mintAccount = await connection.getAccountInfo(mintKey);
  if (mintAccount === null) {
    throw new Error(`Could not fetch mint`);
  }
  if (!mintAccount.owner.equals(TOKEN_PROGRAM_ID)) {
    const mintOwner = mintAccount.owner.toBase58();
    throw new Error(`Invalid mint owner ${mintOwner}`);
  }
  if (mintAccount.data.length !== MintLayout.span) {
    throw new Error(`Invalid mint size ${mintAccount.data.length}`);
  }
  const mintInfo = MintLayout.decode(Buffer.from(mintAccount.data));
  return {
    key: mintKey,
    info: mintInfo,
  };
}
Example #5
Source File: deserialize.ts    From metaplex with Apache License 2.0 6 votes vote down vote up
deserializeMint = (data: Buffer) => {
  if (data.length !== MintLayout.span) {
    throw new Error('Not a valid Mint');
  }

  const mintInfo = MintLayout.decode(data);

  if (mintInfo.mintAuthorityOption === 0) {
    mintInfo.mintAuthority = null;
  } else {
    mintInfo.mintAuthority = new PublicKey(mintInfo.mintAuthority);
  }

  mintInfo.supply = u64.fromBuffer(mintInfo.supply);
  mintInfo.isInitialized = mintInfo.isInitialized !== 0;

  if (mintInfo.freezeAuthorityOption === 0) {
    mintInfo.freezeAuthority = null;
  } else {
    mintInfo.freezeAuthority = new PublicKey(mintInfo.freezeAuthority);
  }

  return mintInfo as MintInfo;
}
Example #6
Source File: account.ts    From metaplex with Apache License 2.0 6 votes vote down vote up
export function createUninitializedMint(
  instructions: TransactionInstruction[],
  payer: PublicKey,
  amount: number,
  signers: Keypair[],
) {
  const account = Keypair.generate();
  instructions.push(
    SystemProgram.createAccount({
      fromPubkey: payer,
      newAccountPubkey: account.publicKey,
      lamports: amount,
      space: MintLayout.span,
      programId: TOKEN_PROGRAM_ID,
    }),
  );

  signers.push(account);

  return account.publicKey;
}
Example #7
Source File: accountParser.ts    From jet-engine with GNU Affero General Public License v3.0 6 votes vote down vote up
parseMintAccount = (info: AccountInfo<Buffer>, address: PublicKey): Mint => {
  if (!info) throw new TokenAccountNotFoundError()
  if (!info.owner.equals(TOKEN_PROGRAM_ID)) throw new TokenInvalidAccountOwnerError()
  if (info.data.length != MINT_SIZE) throw new TokenInvalidAccountSizeError()

  const rawMint = MintLayout.decode(info.data)

  return {
    address,
    mintAuthority: rawMint.mintAuthorityOption ? rawMint.mintAuthority : null,
    supply: rawMint.supply,
    decimals: rawMint.decimals,
    isInitialized: rawMint.isInitialized,
    freezeAuthority: rawMint.freezeAuthorityOption ? rawMint.freezeAuthority : null
  }
}
Example #8
Source File: mockAccounts.ts    From protocol-v1 with Apache License 2.0 6 votes vote down vote up
export async function mockUSDCMint(provider: Provider): Promise<Keypair> {
	const fakeUSDCMint = anchor.web3.Keypair.generate();
	const createUSDCMintAccountIx = SystemProgram.createAccount({
		fromPubkey: provider.wallet.publicKey,
		newAccountPubkey: fakeUSDCMint.publicKey,
		lamports: await Token.getMinBalanceRentForExemptMint(provider.connection),
		space: MintLayout.span,
		programId: TOKEN_PROGRAM_ID,
	});
	const initCollateralMintIx = Token.createInitMintInstruction(
		TOKEN_PROGRAM_ID,
		fakeUSDCMint.publicKey,
		6,
		provider.wallet.publicKey,
		null
	);

	const fakeUSDCTx = new Transaction();
	fakeUSDCTx.add(createUSDCMintAccountIx);
	fakeUSDCTx.add(initCollateralMintIx);

	const _fakeUSDCTxResult = await sendAndConfirmTransaction(
		provider.connection,
		fakeUSDCTx,
		// @ts-ignore
		[provider.wallet.payer, fakeUSDCMint],
		{
			skipPreflight: false,
			commitment: 'recent',
			preflightCommitment: 'recent',
		}
	);
	return fakeUSDCMint;
}
Example #9
Source File: index.ts    From zo-client with Apache License 2.0 6 votes vote down vote up
export async function getMintInfo(
  provider: Provider,
  pubkey: PublicKey,
): Promise<MintInfo> {
  const data = (await provider.connection.getAccountInfo(pubkey))?.data;
  if (!data) throw Error(`Couldn't load mint data for ${pubkey.toBase58()}`);
  const m = MintLayout.decode(data);
  return {
    mintAuthority: new PublicKey(m.mintAuthority),
    supply: u64.fromBuffer(m.supply),
    decimals: m.decimals,
    isInitialized: !!m.isInitialized,
    freezeAuthority: new PublicKey(m.freezeAuthority),
  };
}
Example #10
Source File: testHelpers.ts    From protocol-v1 with Apache License 2.0 6 votes vote down vote up
export async function mockUSDCMint(provider: Provider): Promise<Keypair> {
	const fakeUSDCMint = anchor.web3.Keypair.generate();
	const createUSDCMintAccountIx = SystemProgram.createAccount({
		fromPubkey: provider.wallet.publicKey,
		newAccountPubkey: fakeUSDCMint.publicKey,
		lamports: await Token.getMinBalanceRentForExemptMint(provider.connection),
		space: MintLayout.span,
		programId: TOKEN_PROGRAM_ID,
	});
	const initCollateralMintIx = Token.createInitMintInstruction(
		TOKEN_PROGRAM_ID,
		fakeUSDCMint.publicKey,
		6,
		provider.wallet.publicKey,
		null
	);

	const fakeUSDCTx = new Transaction();
	fakeUSDCTx.add(createUSDCMintAccountIx);
	fakeUSDCTx.add(initCollateralMintIx);

	await sendAndConfirmTransaction(
		provider.connection,
		fakeUSDCTx,
		// @ts-ignore
		[provider.wallet.payer, fakeUSDCMint],
		{
			skipPreflight: false,
			commitment: 'recent',
			preflightCommitment: 'recent',
		}
	);
	return fakeUSDCMint;
}
Example #11
Source File: index.tsx    From metaplex with Apache License 2.0 5 votes vote down vote up
async function calculateTotalCostOfRedeemingOtherPeoplesBids(
  connection: Connection,
  auctionView: AuctionView,
  bids: ParsedAccount<BidderMetadata>[],
  bidRedemptions: Record<string, ParsedAccount<BidRedemptionTicket>>,
): Promise<number> {
  const accountRentExempt = await connection.getMinimumBalanceForRentExemption(
    AccountLayout.span,
  );
  const mintRentExempt = await connection.getMinimumBalanceForRentExemption(
    MintLayout.span,
  );
  const metadataRentExempt = await connection.getMinimumBalanceForRentExemption(
    MAX_METADATA_LEN,
  );
  const editionRentExempt = await connection.getMinimumBalanceForRentExemption(
    MAX_EDITION_LEN,
  );
  const prizeTrackingTicketExempt =
    await connection.getMinimumBalanceForRentExemption(
      MAX_PRIZE_TRACKING_TICKET_SIZE,
    );

  const eligibleParticipations =
    await findEligibleParticipationBidsForRedemption(
      auctionView,
      bids,
      bidRedemptions,
    );
  const max = auctionView.auction.info.bidState.max.toNumber();
  let totalWinnerItems = 0;
  for (let i = 0; i < max; i++) {
    const winner = auctionView.auction.info.bidState.getWinnerAt(i);
    if (!winner) {
      break;
    } else {
      const bid = bids.find(b => b.info.bidderPubkey === winner);
      if (bid) {
        for (
          let j = 0;
          j < auctionView.auctionManager.safetyDepositBoxesExpected.toNumber();
          j++
        ) {
          totalWinnerItems += auctionView.auctionManager
            .getAmountForWinner(i, j)
            .toNumber();
        }
      }
    }
  }
  return (
    (mintRentExempt +
      accountRentExempt +
      metadataRentExempt +
      editionRentExempt +
      prizeTrackingTicketExempt) *
    (eligibleParticipations.length + totalWinnerItems)
  );
}
Example #12
Source File: setupMintEditionIntoWalletInstructions.ts    From metaplex with Apache License 2.0 5 votes vote down vote up
export async function setupMintEditionIntoWalletInstructions(
  art: Art,
  wallet: WalletContextState,
  connection: Connection,
  mintTokenAccount: TokenAccount,
  edition: BN,
  instructions: any,
  signers: any,
  mintDestination: StringPublicKey,
) {
  if (!art.mint) throw new Error('Art mint is not provided');
  if (typeof art.supply === 'undefined') {
    throw new Error('Art supply is not provided');
  }
  if (!wallet.publicKey) throw new Error('Wallet pubKey is not provided');
  if (!mintTokenAccount) {
    throw new Error('Art mint token account is not provided');
  }
  const walletPubKey = wallet.publicKey.toString();
  const { mint: tokenMint } = art;
  const { pubkey: mintTokenAccountPubKey } = mintTokenAccount;
  const mintTokenAccountOwner = mintTokenAccount.info.owner.toString();

  const mintRentExempt = await connection.getMinimumBalanceForRentExemption(
    MintLayout.span,
  );
  const { mint: newMint } = await createMintAndAccountWithOne(
    wallet,
    mintDestination,
    mintRentExempt,
    instructions,
    signers,
  );

  await mintNewEditionFromMasterEditionViaToken(
    newMint,
    tokenMint,
    walletPubKey,
    walletPubKey,
    mintTokenAccountOwner,
    mintTokenAccountPubKey,
    instructions,
    walletPubKey,
    edition,
  );
}
Example #13
Source File: helpers.ts    From psyoptions with Apache License 2.0 5 votes vote down vote up
initNewTokenMint = async (
  connection: Connection,
  /** The owner for the new mint account */
  owner: PublicKey,
  wallet: Keypair
) => {
  const mintAccount = new Keypair();
  const transaction = new Transaction();
  // Create the Option Mint Account with rent exemption
  // Allocate memory for the account
  const mintRentBalance = await connection.getMinimumBalanceForRentExemption(
    MintLayout.span
  );

  transaction.add(
    SystemProgram.createAccount({
      fromPubkey: wallet.publicKey,
      newAccountPubkey: mintAccount.publicKey,
      lamports: mintRentBalance,
      space: MintLayout.span,
      programId: TOKEN_PROGRAM_ID,
    })
  );
  transaction.add(
    Token.createInitMintInstruction(
      TOKEN_PROGRAM_ID,
      mintAccount.publicKey,
      8,
      owner,
      null
    )
  );
  await sendAndConfirmTransaction(
    connection,
    transaction,
    [wallet, mintAccount],
    {
      commitment: "confirmed",
    }
  );
  return {
    mintAccount,
  };
}
Example #14
Source File: mockUSDCFaucet.ts    From protocol-v1 with Apache License 2.0 5 votes vote down vote up
public async initialize(): Promise<TransactionSignature> {
		const stateAccountRPCResponse = await this.connection.getParsedAccountInfo(
			await this.getMockUSDCFaucetStatePublicKey()
		);
		if (stateAccountRPCResponse.value !== null) {
			throw new Error('Faucet already initialized');
		}

		const fakeUSDCMint = anchor.web3.Keypair.generate();
		const createUSDCMintAccountIx = SystemProgram.createAccount({
			fromPubkey: this.wallet.publicKey,
			newAccountPubkey: fakeUSDCMint.publicKey,
			lamports: await Token.getMinBalanceRentForExemptMint(this.connection),
			space: MintLayout.span,
			programId: TOKEN_PROGRAM_ID,
		});

		const [mintAuthority, _mintAuthorityNonce] =
			await PublicKey.findProgramAddress(
				[fakeUSDCMint.publicKey.toBuffer()],
				this.program.programId
			);

		const initUSDCMintIx = Token.createInitMintInstruction(
			TOKEN_PROGRAM_ID,
			fakeUSDCMint.publicKey,
			6,
			mintAuthority,
			null
		);

		const [mockUSDCFaucetStatePublicKey, mockUSDCFaucetStateNonce] =
			await this.getMockUSDCFaucetStatePublicKeyAndNonce();
		return await this.program.rpc.initialize(mockUSDCFaucetStateNonce, {
			accounts: {
				mockUsdcFaucetState: mockUSDCFaucetStatePublicKey,
				admin: this.wallet.publicKey,
				mintAccount: fakeUSDCMint.publicKey,
				rent: SYSVAR_RENT_PUBKEY,
				systemProgram: anchor.web3.SystemProgram.programId,
			},
			instructions: [createUSDCMintAccountIx, initUSDCMintIx],
			signers: [fakeUSDCMint],
		});
	}
Example #15
Source File: fair-launch-cli.ts    From metaplex with Apache License 2.0 5 votes vote down vote up
program
  .command('create_dummy_payment_mint')
  .option(
    '-e, --env <string>',
    'Solana cluster env name',
    'devnet', //mainnet-beta, testnet, devnet
  )
  .option(
    '-k, --keypair <path>',
    `Solana wallet location`,
    '--keypair not provided',
  )
  .action(async (_, cmd) => {
    const { env, keypair } = cmd.opts();

    const walletKeyPair = loadWalletKey(keypair);
    const anchorProgram = await loadFairLaunchProgram(walletKeyPair, env);
    const mint = anchor.web3.Keypair.generate();
    const token = (
      await getAtaForMint(mint.publicKey, walletKeyPair.publicKey)
    )[0];
    const instructions: anchor.web3.TransactionInstruction[] = [
      anchor.web3.SystemProgram.createAccount({
        fromPubkey: walletKeyPair.publicKey,
        newAccountPubkey: mint.publicKey,
        space: MintLayout.span,
        lamports:
          await anchorProgram.provider.connection.getMinimumBalanceForRentExemption(
            MintLayout.span,
          ),
        programId: TOKEN_PROGRAM_ID,
      }),
      Token.createInitMintInstruction(
        TOKEN_PROGRAM_ID,
        mint.publicKey,
        6,
        walletKeyPair.publicKey,
        walletKeyPair.publicKey,
      ),
      createAssociatedTokenAccountInstruction(
        token,
        walletKeyPair.publicKey,
        walletKeyPair.publicKey,
        mint.publicKey,
      ),
    ];

    const signers = [mint];

    await sendTransactionWithRetryWithKeypair(
      anchorProgram.provider.connection,
      walletKeyPair,
      instructions,
      signers,
      'single',
    );
    console.log(`create mint Done: ${mint.publicKey.toBase58()}.`);
  });
Example #16
Source File: index.ts    From serum-ts with Apache License 2.0 5 votes vote down vote up
export function parseMintAccount(data: Buffer): MintInfo {
  const m = MintLayout.decode(data);
  m.mintAuthority = new PublicKey(m.mintAuthority);
  m.supply = u64.fromBuffer(m.supply);
  m.isInitialized = m.state !== 0;
  return m;
}
Example #17
Source File: candy-machine.ts    From metaplex with Apache License 2.0 5 votes vote down vote up
createAccountsForMint = async (
  candyMachine: CandyMachineAccount,
  payer: anchor.web3.PublicKey,
): Promise<SetupState> => {
  const mint = anchor.web3.Keypair.generate();
  const userTokenAccountAddress = (
    await getAtaForMint(mint.publicKey, payer)
  )[0];

  const signers: anchor.web3.Keypair[] = [mint];
  const instructions = [
    anchor.web3.SystemProgram.createAccount({
      fromPubkey: payer,
      newAccountPubkey: mint.publicKey,
      space: MintLayout.span,
      lamports:
        await candyMachine.program.provider.connection.getMinimumBalanceForRentExemption(
          MintLayout.span,
        ),
      programId: TOKEN_PROGRAM_ID,
    }),
    Token.createInitMintInstruction(
      TOKEN_PROGRAM_ID,
      mint.publicKey,
      0,
      payer,
      payer,
    ),
    createAssociatedTokenAccountInstruction(
      userTokenAccountAddress,
      payer,
      payer,
      mint.publicKey,
    ),
    Token.createMintToInstruction(
      TOKEN_PROGRAM_ID,
      mint.publicKey,
      userTokenAccountAddress,
      payer,
      [],
      1,
    ),
  ];

  return {
    mint: mint,
    userTokenAccount: userTokenAccountAddress,
    transaction: (
      await sendTransactions(
        candyMachine.program.provider.connection,
        candyMachine.program.provider.wallet,
        [instructions],
        [signers],
        SequenceType.StopOnFailure,
        'singleGossip',
        () => {},
        () => false,
        undefined,
        [],
        [],
      )
    ).txs[0].txid,
  };
}
Example #18
Source File: pools.ts    From serum-ts with Apache License 2.0 4 votes vote down vote up
/**
   * Note: for seed param, this must be <= 32 characters for the txn to succeed
   */
  static async makeInitializePoolTransaction<T extends PublicKey | Account>(
    connection: Connection,
    tokenSwapProgram: PublicKey,
    owner: T,
    componentMints: PublicKey[],
    sourceTokenAccounts: {
      mint: PublicKey;
      tokenAccount: PublicKey;
      amount: number; // note this is raw amount, not decimal
    }[],
    options: PoolConfig,
    liquidityTokenPrecision = DEFAULT_LIQUIDITY_TOKEN_PRECISION,
    accounts?: {
      liquidityTokenMint?: Account;
      tokenSwapPoolAddress?: Account;
    },
    seed?: string,
  ): Promise<{
    initializeAccountsTransaction: Transaction;
    initializeAccountsSigners: Account[];
    initializePoolTransaction: Transaction;
    initializePoolSigners: Account[];
  }> {
    // @ts-ignore
    const ownerAddress: PublicKey = owner.publicKey ?? owner;
    const initializeAccountsInstructions: TransactionInstruction[] = [];
    const initializeAccountsSigners: Account[] = [];

    const liquidityTokenMintAccount = accounts?.liquidityTokenMint
      ? accounts.liquidityTokenMint
      : new Account();
    initializeAccountsInstructions.push(
      SystemProgram.createAccount({
        fromPubkey: ownerAddress,
        newAccountPubkey: liquidityTokenMintAccount.publicKey,
        lamports: await connection.getMinimumBalanceForRentExemption(
          MintLayout.span,
        ),
        space: MintLayout.span,
        programId: TOKEN_PROGRAM_ID,
      }),
    );
    initializeAccountsSigners.push(liquidityTokenMintAccount);
    let tokenSwapAccountPubkey;
    let tokenSwapAccountSigner;
    if (accounts?.tokenSwapPoolAddress) {
      tokenSwapAccountPubkey = accounts.tokenSwapPoolAddress.publicKey;
      tokenSwapAccountSigner = accounts.tokenSwapPoolAddress;
    } else if (seed) {
      // Only works when owner is of type Account
      tokenSwapAccountSigner = owner;
      tokenSwapAccountPubkey = await PublicKey.createWithSeed(ownerAddress, seed, tokenSwapProgram);
    } else {
      tokenSwapAccountSigner = new Account();
      tokenSwapAccountPubkey = tokenSwapAccountSigner.pubkey;
    }

    const [authority, nonce] = await PublicKey.findProgramAddress(
      [tokenSwapAccountPubkey.toBuffer()],
      tokenSwapProgram,
    );

    // create mint for pool liquidity token
    initializeAccountsInstructions.push(
      Token.createInitMintInstruction(
        TOKEN_PROGRAM_ID,
        liquidityTokenMintAccount.publicKey,
        liquidityTokenPrecision,
        // pass control of liquidity mint to swap program
        authority,
        // swap program can freeze liquidity token mint
        null,
      ),
    );
    const accountRentExempt = await connection.getMinimumBalanceForRentExemption(
      AccountLayout.span,
    );
    const holdingAccounts: { [mint: string]: Account } = {};
    componentMints.forEach(mint => {
      const {
        account,
        instructions: createHoldingTokenAccountInstructions,
      } = createTokenAccount(authority, ownerAddress, mint, accountRentExempt);
      initializeAccountsInstructions.push(
        ...createHoldingTokenAccountInstructions,
      );
      initializeAccountsSigners.push(account);
      holdingAccounts[mint.toBase58()] = account;
    });

    const {
      account: depositorAccount,
      instructions: createLPTokenAccountInstructions,
    } = createTokenAccount(
      ownerAddress,
      ownerAddress,
      liquidityTokenMintAccount.publicKey,
      accountRentExempt,
    );
    initializeAccountsSigners.push(depositorAccount);
    initializeAccountsInstructions.push(...createLPTokenAccountInstructions);

    const {
      account: feeAccount,
      instructions: createFeeAccountInstructions,
    } = createTokenAccount(
      SWAP_PROGRAM_OWNER_FEE_ADDRESS,
      ownerAddress,
      liquidityTokenMintAccount.publicKey,
      accountRentExempt,
    );
    initializeAccountsSigners.push(feeAccount);
    initializeAccountsInstructions.push(...createFeeAccountInstructions);
    const initializeAccountsTransaction = new Transaction();
    initializeAccountsTransaction.add(...initializeAccountsInstructions);

    // break up these into two transactions because it does not fit in a single transaction
    const initializePoolSigners: Account[] = [];
    const initializePoolInstructions: TransactionInstruction[] = [];
    const cleanupInstructions: TransactionInstruction[] = [];

    let initializeTokenSwapAccountInstruction;
    if (seed) {
      initializeTokenSwapAccountInstruction = SystemProgram.createAccountWithSeed({
        fromPubkey: ownerAddress,
        basePubkey: ownerAddress,
        newAccountPubkey: tokenSwapAccountPubkey,
        seed: seed,
        lamports: await connection.getMinimumBalanceForRentExemption(
          getLayoutForProgramId(tokenSwapProgram).span
        ),
        space: getLayoutForProgramId(tokenSwapProgram).span,
        programId: tokenSwapProgram,
      });
    } else {
      initializeTokenSwapAccountInstruction = SystemProgram.createAccount({
        fromPubkey: ownerAddress,
        newAccountPubkey: tokenSwapAccountPubkey,
        lamports: await connection.getMinimumBalanceForRentExemption(
          getLayoutForProgramId(tokenSwapProgram).span,
        ),
        space: getLayoutForProgramId(tokenSwapProgram).span,
        programId: tokenSwapProgram,
      })
    }
    initializePoolInstructions.push(initializeTokenSwapAccountInstruction);

    sourceTokenAccounts.forEach(({ mint, tokenAccount, amount }) => {
      let wrappedAccount: PublicKey;
      if (mint.equals(WRAPPED_SOL_MINT)) {
        const {
          account,
          instructions: createWrappedSolInstructions,
          cleanUpInstructions: removeWrappedSolInstructions,
        } = createTokenAccount(
          ownerAddress,
          ownerAddress,
          WRAPPED_SOL_MINT,
          amount + accountRentExempt,
        );
        wrappedAccount = account.publicKey;
        initializePoolSigners.push(account);
        initializePoolInstructions.push(...createWrappedSolInstructions);
        cleanupInstructions.push(...removeWrappedSolInstructions);
      } else {
        wrappedAccount = tokenAccount;
      }

      initializePoolInstructions.push(
        Token.createTransferInstruction(
          TOKEN_PROGRAM_ID,
          wrappedAccount,
          holdingAccounts[mint.toBase58()].publicKey,
          ownerAddress,
          [],
          amount,
        ),
      );
    });

    initializePoolInstructions.push(
      createInitSwapInstruction(
        tokenSwapAccountPubkey,
        authority,
        holdingAccounts[sourceTokenAccounts[0].mint.toBase58()].publicKey,
        holdingAccounts[sourceTokenAccounts[1].mint.toBase58()].publicKey,
        liquidityTokenMintAccount.publicKey,
        feeAccount.publicKey,
        depositorAccount.publicKey,
        TOKEN_PROGRAM_ID,
        tokenSwapProgram,
        nonce,
        options,
      ),
    );
    initializePoolSigners.push(tokenSwapAccountSigner);
    const initializePoolTransaction = new Transaction();
    initializePoolTransaction.add(
      ...initializePoolInstructions,
      ...cleanupInstructions,
    );
    return {
      initializeAccountsTransaction,
      initializeAccountsSigners,
      initializePoolTransaction,
      initializePoolSigners,
    };
  }
Example #19
Source File: Port.ts    From port-sdk with MIT License 4 votes vote down vote up
public async createReserve({
    provider,
    reserveConfig,
    transferAuthority,
    sourceTokenWallet,
    initialLiquidity,
    oracle,
    price,
  }: {
    provider: Provider;
    reserveConfig: ReserveConfigProto;
    transferAuthority: PublicKey;
    sourceTokenWallet: PublicKey;
    initialLiquidity: number | BN;
    oracle?: PublicKey;
    price?: BN;
  }): Promise<[TransactionEnvelope[], PublicKey]> {
    invariant(!!oracle !== !!price, "Oracle and price can't both be present");

    const [createReserveAccountIx, reservePubKey] = await this.createAccount({
      provider,
      space: ReserveLayout.span,
      owner: PORT_LENDING,
    });
    const [collateralMintIx, collateralMintPubKey] = await this.createAccount({
      provider,
      space: MintLayout.span,
      owner: TOKEN_PROGRAM_ID,
    });
    const [liquiditySupplyIx, liquiditySupplyPubKey] = await this.createAccount(
      {
        provider,
        space: AccountLayout.span,
        owner: TOKEN_PROGRAM_ID,
      }
    );
    const [collateralSupplyIx, collateralSupplyPubKey] =
      await this.createAccount({
        provider,
        space: AccountLayout.span,
        owner: TOKEN_PROGRAM_ID,
      });
    const [userCollateralIx, userCollateralPubKey] = await this.createAccount({
      provider,
      space: AccountLayout.span,
      owner: TOKEN_PROGRAM_ID,
    });
    const [feeReceiverIx, feeReceiverPubkey] = await this.createAccount({
      provider,
      space: AccountLayout.span,
      owner: TOKEN_PROGRAM_ID,
    });

    const tokenAccount = await getTokenAccount(provider, sourceTokenWallet);

    const initReserveIx = initReserveInstruction(
      initialLiquidity,
      oracle ? 0 : 1, // price Option
      price ?? new BN(1),
      reserveConfig,
      sourceTokenWallet,
      collateralSupplyPubKey,
      reservePubKey,
      tokenAccount.mint,
      liquiditySupplyPubKey,
      feeReceiverPubkey,
      oracle ?? Keypair.generate().publicKey,
      collateralMintPubKey,
      userCollateralPubKey,
      this.lendingMarket,
      (await this.getLendingMarketAuthority())[0],
      provider.wallet.publicKey,
      transferAuthority
    );

    let tx1 = new TransactionEnvelope(provider, []);
    tx1 = tx1.combine(createReserveAccountIx);
    tx1 = tx1.combine(collateralMintIx);
    tx1 = tx1.combine(liquiditySupplyIx);
    tx1 = tx1.combine(collateralSupplyIx);
    tx1 = tx1.combine(userCollateralIx);

    let tx2 = new TransactionEnvelope(provider, []);
    tx2 = tx2.combine(feeReceiverIx);
    tx2 = tx2.addInstructions(initReserveIx);

    return [[tx1, tx2], reservePubKey];
  }
Example #20
Source File: Dex.tsx    From swap-ui with Apache License 2.0 4 votes vote down vote up
export function DexContextProvider(props: any) {
  const [ooAccounts, setOoAccounts] = useState<Map<string, Array<OpenOrders>>>(
    new Map()
  );
  const swapClient = props.swapClient;

  // Removes the given open orders from the context.
  const closeOpenOrders = async (openOrder: OpenOrders) => {
    const newOoAccounts = new Map(ooAccounts);
    const openOrders = newOoAccounts
      .get(openOrder.market.toString())
      ?.filter((oo: OpenOrders) => !oo.address.equals(openOrder.address));
    if (openOrders && openOrders.length > 0) {
      newOoAccounts.set(openOrder.market.toString(), openOrders);
    } else {
      newOoAccounts.delete(openOrder.market.toString());
    }
    setOoAccounts(newOoAccounts);
  };

  // Three operations:
  //
  // 1. Fetch all open orders accounts for the connected wallet.
  // 2. Batch fetch all market accounts for those open orders.
  // 3. Batch fetch all mints associated with the markets.
  useEffect(() => {
    if (!swapClient.program.provider.wallet.publicKey) {
      setOoAccounts(new Map());
      return;
    }
    OpenOrders.findForOwner(
      swapClient.program.provider.connection,
      swapClient.program.provider.wallet.publicKey,
      DEX_PID
    ).then(async (openOrders) => {
      const newOoAccounts = new Map();
      let markets = new Set<string>();
      openOrders.forEach((oo) => {
        markets.add(oo.market.toString());
        if (newOoAccounts.get(oo.market.toString())) {
          newOoAccounts.get(oo.market.toString()).push(oo);
        } else {
          newOoAccounts.set(oo.market.toString(), [oo]);
        }
      });
      if (markets.size > 100) {
        // Punt request chunking until there's user demand.
        throw new Error(
          "Too many markets. Please file an issue to update this"
        );
      }
      const multipleMarkets = await anchor.utils.rpc.getMultipleAccounts(
        swapClient.program.provider.connection,
        Array.from(markets.values()).map((m) => new PublicKey(m))
      );
      const marketClients = multipleMarkets.map((programAccount) => {
        return {
          publicKey: programAccount?.publicKey,
          account: new Market(
            Market.getLayout(DEX_PID).decode(programAccount?.account.data),
            -1, // Set below so that we can batch fetch mints.
            -1, // Set below so that we can batch fetch mints.
            swapClient.program.provider.opts,
            DEX_PID
          ),
        };
      });

      setOoAccounts(newOoAccounts);

      // Batch fetch all the mints, since we know we'll need them at some
      // point.
      const mintPubkeys = Array.from(
        new Set<string>(
          marketClients
            .map((m) => [
              m.account.baseMintAddress.toString(),
              m.account.quoteMintAddress.toString(),
            ])
            .flat()
        ).values()
      ).map((pk) => new PublicKey(pk));

      if (mintPubkeys.length > 100) {
        // Punt request chunking until there's user demand.
        throw new Error("Too many mints. Please file an issue to update this");
      }

      const mints = await anchor.utils.rpc.getMultipleAccounts(
        swapClient.program.provider.connection,
        mintPubkeys
      );
      const mintInfos = mints.map((mint) => {
        const mintInfo = MintLayout.decode(mint!.account.data);
        setMintCache(mint!.publicKey, mintInfo);
        return { publicKey: mint!.publicKey, mintInfo };
      });

      marketClients.forEach((m) => {
        const baseMintInfo = mintInfos.filter((mint) =>
          mint.publicKey.equals(m.account.baseMintAddress)
        )[0];
        const quoteMintInfo = mintInfos.filter((mint) =>
          mint.publicKey.equals(m.account.quoteMintAddress)
        )[0];
        assert.ok(baseMintInfo && quoteMintInfo);
        // @ts-ignore
        m.account._baseSplTokenDecimals = baseMintInfo.mintInfo.decimals;
        // @ts-ignore
        m.account._quoteSplTokenDecimals = quoteMintInfo.mintInfo.decimals;
        _MARKET_CACHE.set(
          m.publicKey!.toString(),
          new Promise<Market>((resolve) => resolve(m.account))
        );
      });
    });
  }, [
    swapClient.program.provider.connection,
    swapClient.program.provider.wallet.publicKey,
    swapClient.program.provider.opts,
  ]);
  return (
    <_DexContext.Provider
      value={{
        openOrders: ooAccounts,
        closeOpenOrders,
        swapClient,
      }}
    >
      {props.children}
    </_DexContext.Provider>
  );
}
Example #21
Source File: mint.ts    From sdk with MIT License 4 votes vote down vote up
export async function getMintNftInstructions(
	connection: Connection,
	signer: IWalletSigner,
	params: {
		metadataLink: string,
		//mutableMetadata: boolean,
		collection: PublicKey | null,
		maxSupply: number,
		verifyCreators: boolean,
		use?: Uses,
	}
): Promise<ITransactionPreparedInstructions & { mint: PublicKey }> {
	// Retrieve metadata
	const data = await createMetadata(
		params.metadataLink,
		params.collection,
		params.verifyCreators ? { [signer.publicKey.toString()]: true } : undefined,
		params.use,
	)

	if (!data) {
		throw new Error("Empty metadata")
	}

	// Allocate memory for the account
	const mintRent = await connection.getMinimumBalanceForRentExemption(
		MintLayout.span,
	)

	// Generate a mint
	const mint = SolanaKeypairWallet.generate()
	const instructions: TransactionInstruction[] = []
	const signers: IWalletSigner[] = [mint, signer]

	instructions.push(
		SystemProgram.createAccount({
			fromPubkey: signer.publicKey,
			// eslint-disable-next-line unicorn/no-keyword-prefix
			newAccountPubkey: mint.publicKey,
			lamports: mintRent,
			space: MintLayout.span,
			programId: TOKEN_PROGRAM_ID,
		}),
	)
	instructions.push(
		Token.createInitMintInstruction(
			TOKEN_PROGRAM_ID,
			mint.publicKey,
			0,
			signer.publicKey,
			signer.publicKey,
		),
	)

	const userTokenAccoutAddress = await getTokenWallet(
		signer.publicKey,
		mint.publicKey,
	)
	instructions.push(
		createAssociatedTokenAccountInstruction(
			userTokenAccoutAddress,
			signer.publicKey,
			signer.publicKey,
			mint.publicKey,
		),
	)

	// Create metadata
	const metadataAccount = await getMetadata(mint.publicKey)

	instructions.push(
		...new CreateMetadataV2(
			{ feePayer: signer.publicKey },
			{
				metadata: metadataAccount,
				metadataData: data,
				updateAuthority: signer.publicKey,
				mint: mint.publicKey,
				mintAuthority: signer.publicKey,
			},
		).instructions,
	)

	instructions.push(
		Token.createMintToInstruction(
			TOKEN_PROGRAM_ID,
			mint.publicKey,
			userTokenAccoutAddress,
			signer.publicKey,
			[],
			1,
		),
	)

	// Create master edition
	const editionAccount = await getMasterEdition(mint.publicKey)
	instructions.push(
		...new CreateMasterEditionV3(
			{
				feePayer: signer.publicKey,
			},
			{
				edition: editionAccount,
				metadata: metadataAccount,
				mint: mint.publicKey,
				mintAuthority: signer.publicKey,
				updateAuthority: signer.publicKey,
				maxSupply: new BN(params.maxSupply),
			},
		).instructions,
	)

	/*
	// not working with current mpl-token-metadata version

	if (params.mutableMetadata === false) {
		instructions.push(
			...new UpdateMetadataV2(
				{},
				{
					metadata: metadataAccount,
					metadataData: data,
					updateAuthority: signer.publicKey,
					primarySaleHappened: null,
					isMutable: false,
				},
			).instructions,
		)
	}*/

	return { instructions, signers, mint: mint.publicKey }
}
Example #22
Source File: mint.ts    From candy-machine-v2 with MIT License 4 votes vote down vote up
mintOneToken = async (
  candyMachine: CandyMachineAccount,
  payer: anchor.web3.PublicKey,
  mint: anchor.web3.Keypair
): Promise<(string | undefined)[]> => {
  // const mint = anchor.web3.Keypair.generate();
  console.log(mint.publicKey);
  const userTokenAccountAddress = (
    await getAtaForMint(mint.publicKey, payer)
  )[0];

  const userPayingAccountAddress = candyMachine.state.tokenMint
    ? (await getAtaForMint(candyMachine.state.tokenMint, payer))[0]
    : payer;

  const candyMachineAddress = candyMachine.id;
  const remainingAccounts = [];
  const signers: anchor.web3.Keypair[] = [mint];
  const cleanupInstructions = [];
  const metadataAddress = await getMetadata(mint.publicKey);
  const masterEdition = await getMasterEdition(mint.publicKey);
  const [candyMachineCreator, creatorBump] = await getCandyMachineCreator(
    candyMachineAddress
  );
  const instructions = [
    anchor.web3.SystemProgram.createAccount({
      fromPubkey: payer,
      newAccountPubkey: mint.publicKey,
      space: MintLayout.span,
      lamports:
        await candyMachine.program.provider.connection.getMinimumBalanceForRentExemption(
          MintLayout.span
        ),
      programId: TOKEN_PROGRAM_ID,
    }),
    Token.createInitMintInstruction(
      TOKEN_PROGRAM_ID,
      mint.publicKey,
      0,
      payer,
      payer
    ),
    createAssociatedTokenAccountInstruction(
      userTokenAccountAddress,
      payer,
      payer,
      mint.publicKey
    ),
    Token.createMintToInstruction(
      TOKEN_PROGRAM_ID,
      mint.publicKey,
      userTokenAccountAddress,
      payer,
      [],
      1
    ),
  ];

  if (candyMachine.state.whitelistMintSettings) {
    const mint = new anchor.web3.PublicKey(
      candyMachine.state.whitelistMintSettings.mint
    );

    const whitelistToken = (await getAtaForMint(mint, payer))[0];
    remainingAccounts.push({
      pubkey: whitelistToken,
      isWritable: true,
      isSigner: false,
    });

    if (candyMachine.state.whitelistMintSettings.mode.burnEveryTime) {
      const whitelistBurnAuthority = anchor.web3.Keypair.generate();

      remainingAccounts.push({
        pubkey: mint,
        isWritable: true,
        isSigner: false,
      });
      remainingAccounts.push({
        pubkey: whitelistBurnAuthority.publicKey,
        isWritable: false,
        isSigner: true,
      });
      signers.push(whitelistBurnAuthority);
      const exists =
        await candyMachine.program.provider.connection.getAccountInfo(
          whitelistToken
        );
      if (exists) {
        instructions.push(
          Token.createApproveInstruction(
            TOKEN_PROGRAM_ID,
            whitelistToken,
            whitelistBurnAuthority.publicKey,
            payer,
            [],
            1
          )
        );
        cleanupInstructions.push(
          Token.createRevokeInstruction(
            TOKEN_PROGRAM_ID,
            whitelistToken,
            payer,
            []
          )
        );
      }
    }
  }
  if (candyMachine.state.tokenMint) {
    const transferAuthority = anchor.web3.Keypair.generate();

    signers.push(transferAuthority);
    remainingAccounts.push({
      pubkey: userPayingAccountAddress,
      isWritable: true,
      isSigner: false,
    });
    remainingAccounts.push({
      pubkey: transferAuthority.publicKey,
      isWritable: false,
      isSigner: true,
    });

    instructions.push(
      Token.createApproveInstruction(
        TOKEN_PROGRAM_ID,
        userPayingAccountAddress,
        transferAuthority.publicKey,
        payer,
        [],
        candyMachine.state.price.toNumber()
      )
    );
    cleanupInstructions.push(
      Token.createRevokeInstruction(
        TOKEN_PROGRAM_ID,
        userPayingAccountAddress,
        payer,
        []
      )
    );
  }
  instructions.push(
    await candyMachine.program.instruction.mintNft(creatorBump, {
      accounts: {
        candyMachine: candyMachineAddress,
        candyMachineCreator,
        payer: payer,
        wallet: candyMachine.state.treasury,
        mint: mint.publicKey,
        metadata: metadataAddress,
        masterEdition,
        mintAuthority: payer,
        updateAuthority: payer,
        tokenMetadataProgram: TOKEN_METADATA_PROGRAM_ID,
        tokenProgram: TOKEN_PROGRAM_ID,
        systemProgram: SystemProgram.programId,
        rent: anchor.web3.SYSVAR_RENT_PUBKEY,
        clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
        recentBlockhashes: anchor.web3.SYSVAR_RECENT_BLOCKHASHES_PUBKEY,
        instructionSysvarAccount: anchor.web3.SYSVAR_INSTRUCTIONS_PUBKEY,
      },
      remainingAccounts:
        remainingAccounts.length > 0 ? remainingAccounts : undefined,
    })
  );

  try {
    return (
      await sendTransactions(
        candyMachine.program.provider.connection,
        candyMachine.program.provider.wallet,
        [instructions, cleanupInstructions],
        [signers, []]
      )
    ).txs.map((t) => t.txid);
  } catch (e) {
    console.log(e);
  }

  return [];
}
Example #23
Source File: index.tsx    From metaplex with Apache License 2.0 4 votes vote down vote up
ReviewStep = (props: {
  confirm: () => void;
  attributes: AuctionState;
  setAttributes: Function;
  connection: Connection;
}) => {
  const [showFundsIssueModal, setShowFundsIssueModal] = useState(false);
  const [cost, setCost] = useState(0);
  const { account } = useNativeAccount();
  useEffect(() => {
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const rentCall = Promise.all([
      props.connection.getMinimumBalanceForRentExemption(MintLayout.span),
      props.connection.getMinimumBalanceForRentExemption(MAX_METADATA_LEN),
    ]);
    // TODO: add
  }, [setCost]);

  const balance = (account?.lamports || 0) / LAMPORTS_PER_SOL;

  const item = props.attributes.items?.[0];

  const handleConfirm = () => {
    props.setAttributes({
      ...props.attributes,
      startListTS: props.attributes.startListTS || moment().unix(),
      startSaleTS: props.attributes.startSaleTS || moment().unix(),
    });
    props.confirm();
  };

  return (
    <>
      <Row className="call-to-action">
        <h2>Review and list</h2>
        <p>Review your listing before publishing.</p>
      </Row>
      <Row className="content-action">
        <Col xl={12}>
          {item?.metadata.info && (
            <ArtCard pubkey={item.metadata.pubkey} small={true} />
          )}
        </Col>
        <Col className="section" xl={12}>
          <Statistic
            className="create-statistic"
            title="Copies"
            value={
              props.attributes.editions === undefined
                ? 'Unique'
                : props.attributes.editions
            }
          />
          {cost ? (
            <AmountLabel
              title="Cost to Create"
              amount={cost}
              tokenInfo={useTokenList().tokenMap.get(
                WRAPPED_SOL_MINT.toString(),
              )}
            />
          ) : (
            <Spin />
          )}
        </Col>
      </Row>
      <Row style={{ display: 'block' }}>
        <Divider />
        <Statistic
          className="create-statistic"
          title="Start date"
          value={
            props.attributes.startSaleTS
              ? moment
                  .unix(props.attributes.startSaleTS as number)
                  .format('dddd, MMMM Do YYYY, h:mm a')
              : 'Right after successfully published'
          }
        />
        <br />
        {props.attributes.startListTS && (
          <Statistic
            className="create-statistic"
            title="Listing go live date"
            value={moment
              .unix(props.attributes.startListTS as number)
              .format('dddd, MMMM Do YYYY, h:mm a')}
          />
        )}
        <Divider />
        <Statistic
          className="create-statistic"
          title="Sale ends"
          value={
            props.attributes.endTS
              ? moment
                  .unix(props.attributes.endTS as number)
                  .format('dddd, MMMM Do YYYY, h:mm a')
              : 'Until sold'
          }
        />
      </Row>
      <Row>
        <Button
          type="primary"
          size="large"
          onClick={() => {
            if (balance < MINIMUM_SAFE_FEE_AUCTION_CREATION) {
              setShowFundsIssueModal(true);
            } else {
              handleConfirm();
            }
          }}
          className="action-btn"
        >
          {props.attributes.category === AuctionCategory.InstantSale
            ? 'List for Sale'
            : 'Publish Auction'}
        </Button>
        <FundsIssueModal
          message={'Estimated Minimum Fee'}
          minimumFunds={MINIMUM_SAFE_FEE_AUCTION_CREATION}
          currentFunds={balance}
          isModalVisible={showFundsIssueModal}
          onClose={() => setShowFundsIssueModal(false)}
        />
      </Row>
    </>
  );
}
Example #24
Source File: index.tsx    From metaplex with Apache License 2.0 4 votes vote down vote up
LaunchStep = (props: {
  confirm: () => void;
  attributes: IMetadataExtension;
  files: File[];
  connection: Connection;
}) => {
  const [cost, setCost] = useState(0);
  const { image } = useArtworkFiles(props.files, props.attributes);
  const files = props.files;
  const metadata = props.attributes;
  useEffect(() => {
    const rentCall = Promise.all([
      props.connection.getMinimumBalanceForRentExemption(MintLayout.span),
      props.connection.getMinimumBalanceForRentExemption(MAX_METADATA_LEN),
    ]);
    if (files.length)
      getAssetCostToStore([
        ...files,
        new File([JSON.stringify(metadata)], 'metadata.json'),
      ]).then(async lamports => {
        const sol = lamports / LAMPORT_MULTIPLIER;

        // TODO: cache this and batch in one call
        const [mintRent, metadataRent] = await rentCall;

        // const uriStr = 'x';
        // let uriBuilder = '';
        // for (let i = 0; i < MAX_URI_LENGTH; i++) {
        //   uriBuilder += uriStr;
        // }

        const additionalSol = (metadataRent + mintRent) / LAMPORT_MULTIPLIER;

        // TODO: add fees based on number of transactions and signers
        setCost(sol + additionalSol);
      });
  }, [files, metadata, setCost]);

  return (
    <>
      <Row className="call-to-action">
        <h2>Launch your creation</h2>
        <p>
          Provide detailed description of your creative process to engage with
          your audience.
        </p>
      </Row>
      <Row className="content-action" justify="space-around">
        <Col>
          {props.attributes.image && (
            <ArtCard
              image={image}
              animationURL={props.attributes.animation_url}
              category={props.attributes.properties?.category}
              name={props.attributes.name}
              symbol={props.attributes.symbol}
              small={true}
              artView={props.files[1]?.type === 'unknown'}
              className="art-create-card"
            />
          )}
        </Col>
        <Col className="section" style={{ minWidth: 300 }}>
          <Statistic
            className="create-statistic"
            title="Royalty Percentage"
            value={props.attributes.seller_fee_basis_points / 100}
            precision={2}
            suffix="%"
          />
          {cost ? (
            <AmountLabel
              title="Cost to Create"
              amount={cost.toFixed(5)}
              tokenInfo={useTokenList().tokenMap.get(
                WRAPPED_SOL_MINT.toString(),
              )}
            />
          ) : (
            <Spin />
          )}
        </Col>
      </Row>
      <Row>
        <Button
          type="primary"
          size="large"
          onClick={props.confirm}
          className="action-btn"
        >
          Pay with SOL
        </Button>
        <Button
          disabled={true}
          size="large"
          onClick={props.confirm}
          className="action-btn"
        >
          Pay with Credit Card
        </Button>
      </Row>
    </>
  );
}
Example #25
Source File: solana.tx.ts    From tatum-js with MIT License 4 votes vote down vote up
mintNft = async (
  body: MintSolanaNft,
  web3: SolanaWeb3,
  provider?: string,
  feePayer?: string,
  feePayerPrivateKey?: string,
) => {
  const connection = web3.getClient(provider)
  const from = new PublicKey(body.from)
  const transaction = new Transaction({ feePayer: feePayer ? new PublicKey(feePayer) : from })
  const mintRent = await connection.getMinimumBalanceForRentExemption(MintLayout.span)
  const mint = Keypair.generate()
  const instructions = []
  instructions.push(
    SystemProgram.createAccount({
      fromPubkey: from,
      newAccountPubkey: mint.publicKey,
      lamports: mintRent,
      space: MintLayout.span,
      programId: TOKEN_PROGRAM_ID,
    }),
    createInitializeMintInstruction(mint.publicKey, 0, from, null, TOKEN_PROGRAM_ID),
  )

  const userTokenAccountAddress = (
    await PublicKey.findProgramAddress(
      [new PublicKey(body.to).toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), mint.publicKey.toBuffer()],
      SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID,
    )
  )[0]

  instructions.push(
    createAssociatedTokenAccountInstruction(
      userTokenAccountAddress,
      from,
      new PublicKey(body.to),
      mint.publicKey,
    ),
  )

  const metadataAccount = (
    await PublicKey.findProgramAddress(
      [Buffer.from('metadata'), TOKEN_METADATA_PROGRAM_ID.toBuffer(), mint.publicKey.toBuffer()],
      TOKEN_METADATA_PROGRAM_ID,
    )
  )[0]

  const metadata = new SolanaNftMetadata(
    body.metadata.name,
    body.metadata.symbol,
    body.metadata.uri,
    body.metadata.sellerFeeBasisPoints,
    body.metadata.creators,
  )
  if (body.metadata.creators) {
    metadata.creators = body.metadata.creators.map(
      (c) => new SolanaNftMetadataCreator(c.address, c.verified, c.share),
    )
  }
  const txnData = Buffer.from(
    serialize(
      METADATA_SCHEMA,
      new CreateMetadataArgs({
        data: metadata,
        isMutable: true,
      }),
    ),
  )

  instructions.push(
    createMetadataInstruction(metadataAccount, mint.publicKey, from, from, from, txnData),
    createMintToInstruction(mint.publicKey, userTokenAccountAddress, from, 1, [], TOKEN_PROGRAM_ID),
  )

  const editionAccount = (
    await PublicKey.findProgramAddress(
      [
        Buffer.from('metadata'),
        TOKEN_METADATA_PROGRAM_ID.toBuffer(),
        mint.publicKey.toBuffer(),
        Buffer.from('edition'),
      ],
      TOKEN_METADATA_PROGRAM_ID,
    )
  )[0]

  const masterEditionTxnData = Buffer.from(
    serialize(METADATA_SCHEMA, new CreateMasterEditionArgs({ maxSupply: new BN(0) })),
  )

  instructions.push(
    createMasterEditionInstruction(
      metadataAccount,
      editionAccount,
      mint.publicKey,
      from,
      from,
      from,
      masterEditionTxnData,
    ),
  )

  transaction.add(...instructions)

  if (body.signatureId) {
    transaction.recentBlockhash = '7WyEshBZcZwEbJsvSeGgCkSNMxxxFAym3x7Cuj6UjAUE'
    return {
      txData: transaction.compileMessage().serialize().toString('hex'),
      mintPK: Buffer.from(mint.secretKey).toString('hex'),
    }
  }

  const wallet = web3.generateKeyPair(body.fromPrivateKey as string)
  const signers = [mint, wallet]
  if (feePayerPrivateKey) {
    signers.push(web3.generateKeyPair(feePayerPrivateKey))
  }
  return {
    txId: await connection.sendTransaction(transaction, [wallet, ...signers]),
    nftAddress: mint.publicKey.toBase58(),
    nftAccountAddress: userTokenAccountAddress.toBase58(),
  }
}
Example #26
Source File: index.tsx    From metaplex with Apache License 2.0 4 votes vote down vote up
ArtMinting = ({ id, onMint }: ArtMintingProps) => {
  const wallet = useWallet();
  const connection = useConnection();
  const { accountByMint } = useUserAccounts();
  const [showMintModal, setShowMintModal] = useState<boolean>(false);
  const [showCongrats, setShowCongrats] = useState<boolean>(false);
  const [mintingDestination, setMintingDestination] = useState<string>('');
  const [editions, setEditions] = useState<number>(1);
  const [editionNumber, setEditionNumber] = useState<number | undefined>(
    undefined,
  );
  const [totalCost, setTotalCost] = useState<number>(0);
  const [isLoading, setIsLoading] = useState<boolean>(false);
  const art = useArt(id);

  const walletPubKey = wallet?.publicKey?.toString() || '';
  const maxEditionsToMint = art.maxSupply! - art.supply!;
  const isArtMasterEdition = art.type === ArtType.Master;
  const artMintTokenAccount = accountByMint.get(art.mint!);
  const isArtOwnedByUser =
    ((accountByMint.has(art.mint!) &&
      artMintTokenAccount?.info.amount.toNumber()) ||
      0) > 0;
  const isMasterEditionV1 = artMintTokenAccount
    ? decodeMasterEdition(artMintTokenAccount.account.data).key ===
      MetadataKey.MasterEditionV1
    : false;
  const renderMintEdition =
    isArtMasterEdition &&
    isArtOwnedByUser &&
    !isMasterEditionV1 &&
    maxEditionsToMint > 0;

  const mintingDestinationErr = useMemo(() => {
    if (!mintingDestination) return 'Required';

    try {
      new PublicKey(mintingDestination);
      return '';
    } catch (e) {
      return 'Invalid address format';
    }
  }, [mintingDestination]);

  const isMintingDisabled =
    isLoading || editions < 1 || Boolean(mintingDestinationErr);

  const debouncedEditionsChangeHandler = useCallback(
    debounce(val => {
      setEditions(val < 1 ? 1 : val);
    }, 300),
    [],
  );

  useEffect(() => {
    if (editions < 1) return;

    (async () => {
      const mintRentExempt = await connection.getMinimumBalanceForRentExemption(
        MintLayout.span,
      );
      const accountRentExempt =
        await connection.getMinimumBalanceForRentExemption(AccountLayout.span);
      const metadataRentExempt =
        await connection.getMinimumBalanceForRentExemption(MAX_METADATA_LEN);
      const editionRentExempt =
        await connection.getMinimumBalanceForRentExemption(MAX_EDITION_LEN);

      const cost =
        ((mintRentExempt +
          accountRentExempt +
          metadataRentExempt +
          editionRentExempt) *
          editions) /
        LAMPORTS_PER_SOL;

      setTotalCost(cost);
    })();
  }, [connection, editions]);

  useEffect(() => {
    if (!walletPubKey) return;

    setMintingDestination(walletPubKey);
  }, [walletPubKey]);

  useEffect(() => {
    return debouncedEditionsChangeHandler.cancel();
  }, []);

  const onSuccessfulMint = () => {
    setShowMintModal(false);
    setMintingDestination(walletPubKey);
    setEditions(1);
    setShowCongrats(true);
  };

  const mint = async () => {
    try {
      setIsLoading(true);
      await mintEditionsToWallet(
        art,
        wallet!,
        connection,
        artMintTokenAccount!,
        editions,
        mintingDestination,
        editionNumber,
      );
      onSuccessfulMint();
    } catch (e) {
      console.error(e);
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <>
      {renderMintEdition && (
        <div>
          <Button
            type="primary"
            size="large"
            className="action-btn"
            style={{ marginTop: 20 }}
            onClick={() => setShowMintModal(true)}
          >
            Mint
          </Button>

          <Modal
            visible={showMintModal}
            centered
            okText="Mint"
            closable={!isLoading}
            okButtonProps={{
              disabled: isMintingDisabled,
            }}
            cancelButtonProps={{ disabled: isLoading }}
            onOk={mint}
            onCancel={() => setShowMintModal(false)}
            className="art-minting-modal"
          >
            <Form.Item
              style={{
                width: '100%',
                flexDirection: 'column',
                paddingTop: 30,
                marginBottom: 4,
              }}
              label={<h3 style={{ color: 'white' }}>Mint to</h3>}
              labelAlign="left"
              colon={false}
              validateStatus={mintingDestinationErr ? 'error' : 'success'}
              help={mintingDestinationErr}
            >
              <Input
                placeholder="Address to mint edition to"
                value={mintingDestination}
                onChange={e => {
                  setMintingDestination(e.target.value);
                }}
              />
            </Form.Item>

            <Form.Item
              style={{
                width: '100%',
                flexDirection: 'column',
                paddingTop: 30,
              }}
              label={
                <h3 style={{ color: 'white' }}>Number of editions to mint</h3>
              }
              labelAlign="left"
              colon={false}
            >
              <InputNumber
                type="number"
                placeholder="1"
                style={{ width: '100%' }}
                min={1}
                max={maxEditionsToMint}
                value={editions}
                precision={0}
                onChange={debouncedEditionsChangeHandler}
              />
            </Form.Item>
            <Form.Item
              style={{
                width: '100%',
                flexDirection: 'column',
                paddingTop: 30,
              }}
              label={
                <h3 style={{ color: 'white' }}>Edition Number (Optional)</h3>
              }
              labelAlign="left"
              colon={false}
            >
              <InputNumber
                type="number"
                style={{ width: '100%' }}
                min={1}
                max={art.supply}
                value={editionNumber}
                precision={0}
                onChange={setEditionNumber}
              />
            </Form.Item>

            <div>Total cost: {`◎${totalCost}`}</div>
          </Modal>

          <MetaplexOverlay visible={showCongrats}>
            <Confetti />
            <h1
              className="title"
              style={{
                fontSize: '3rem',
                marginBottom: 20,
              }}
            >
              Congratulations
            </h1>
            <p
              style={{
                color: 'white',
                textAlign: 'center',
                fontSize: '2rem',
              }}
            >
              New editions have been minted please view your NFTs in{' '}
              <Link to="/artworks">My Items</Link>.
            </p>
            <Button
              onClick={async () => {
                await onMint();
                setShowCongrats(false);
              }}
              className="overlay-btn"
            >
              Got it
            </Button>
          </MetaplexOverlay>
        </div>
      )}
    </>
  );
}
Example #27
Source File: sendRedeemBid.ts    From metaplex with Apache License 2.0 4 votes vote down vote up
export async function sendRedeemBid(
  connection: Connection,
  wallet: WalletSigner,
  payingAccount: StringPublicKey,
  auctionView: AuctionView,
  accountsByMint: Map<string, TokenAccount>,
  prizeTrackingTickets: Record<string, ParsedAccount<PrizeTrackingTicket>>,
  bidRedemptions: Record<string, ParsedAccount<BidRedemptionTicket>>,
  bids: ParsedAccount<BidderMetadata>[],
) {
  if (!wallet.publicKey) throw new WalletNotConnectedError();

  const signers: Array<Keypair[]> = [];
  const instructions: Array<TransactionInstruction[]> = [];

  if (
    auctionView.auction.info.ended() &&
    auctionView.auction.info.state !== AuctionState.Ended
  ) {
    await setupPlaceBid(
      connection,
      wallet,
      payingAccount,
      auctionView,
      accountsByMint,
      0,
      instructions,
      signers,
    );
  }

  const accountRentExempt = await connection.getMinimumBalanceForRentExemption(
    AccountLayout.span,
  );

  const mintRentExempt = await connection.getMinimumBalanceForRentExemption(
    MintLayout.span,
  );

  let winnerIndex: number | null = null;
  if (auctionView.myBidderPot?.pubkey)
    winnerIndex = auctionView.auction.info.bidState.getWinnerIndex(
      auctionView.myBidderPot?.info.bidderAct,
    );

  if (winnerIndex !== null) {
    // items is a prebuilt array of arrays where each entry represents one
    // winning spot, and each entry in it represents one type of item that can
    // be received.
    const winningSet = auctionView.items[winnerIndex];

    for (let i = 0; i < winningSet.length; i++) {
      const item = winningSet[i];
      const safetyDeposit = item.safetyDeposit;
      switch (item.winningConfigType) {
        case WinningConfigType.PrintingV1:
          console.log('Redeeming printing v1');
          await deprecatedSetupRedeemPrintingV1Instructions(
            auctionView,
            accountsByMint,
            accountRentExempt,
            mintRentExempt,
            wallet,
            safetyDeposit,
            item,
            winnerIndex,
            signers,
            instructions,
          );
          break;
        case WinningConfigType.PrintingV2:
          console.log('Redeeming printing v2');
          await setupRedeemPrintingV2Instructions(
            connection,
            auctionView,
            mintRentExempt,
            wallet,
            wallet.publicKey.toBase58(),
            safetyDeposit,
            item,
            signers,
            instructions,
            winnerIndex,
            prizeTrackingTickets,
          );
          break;
        case WinningConfigType.FullRightsTransfer:
          console.log('Redeeming Full Rights');
          await setupRedeemFullRightsTransferInstructions(
            auctionView,
            accountsByMint,
            accountRentExempt,
            wallet,
            safetyDeposit,
            item,
            winnerIndex,
            signers,
            instructions,
          );
          break;
        case WinningConfigType.TokenOnlyTransfer:
          console.log('Redeeming Token only');
          await setupRedeemInstructions(
            auctionView,
            accountsByMint,
            accountRentExempt,
            wallet,
            safetyDeposit,
            winnerIndex,
            signers,
            instructions,
          );
          break;
      }
    }

    if (auctionView.myBidderMetadata && auctionView.myBidderPot) {
      const claimSigners: Keypair[] = [];
      const claimInstructions: TransactionInstruction[] = [];
      instructions.push(claimInstructions);
      signers.push(claimSigners);
      console.log('Claimed');
      await claimBid(
        auctionView.auctionManager.acceptPayment,
        auctionView.myBidderMetadata.info.bidderPubkey,
        auctionView.myBidderPot?.info.bidderPot,
        auctionView.vault.pubkey,
        auctionView.auction.info.tokenMint,
        claimInstructions,
      );
    }
  } else {
    // If you didnt win, you must have a bid we can refund before we check for open editions.
    await setupCancelBid(
      auctionView,
      accountsByMint,
      accountRentExempt,
      wallet,
      signers,
      instructions,
      connection,
    );
  }

  if (
    auctionView.participationItem &&
    eligibleForParticipationPrizeGivenWinningIndex(
      winnerIndex,
      auctionView,
      auctionView.myBidderMetadata,
      auctionView.myBidRedemption,
    )
  ) {
    console.log('eligible for participation');
    const item = auctionView.participationItem;
    const safetyDeposit = item.safetyDeposit;
    if (item.masterEdition?.info.key == MetadataKey.MasterEditionV1) {
      await deprecatedSetupRedeemParticipationInstructions(
        connection,
        auctionView,
        accountsByMint,
        accountRentExempt,
        mintRentExempt,
        wallet,
        safetyDeposit,
        item,
        signers,
        instructions,
      );
    } else {
      await setupRedeemParticipationInstructions(
        connection,
        auctionView,
        accountsByMint,
        accountRentExempt,
        mintRentExempt,
        wallet,
        wallet.publicKey.toBase58(),
        safetyDeposit,
        auctionView.myBidRedemption,
        auctionView.myBidderMetadata,
        item,
        signers,
        instructions,
      );
    }
  }

  if (wallet.publicKey.toBase58() === auctionView.auctionManager.authority) {
    await claimUnusedPrizes(
      connection,
      wallet,
      auctionView,
      accountsByMint,
      bids,
      bidRedemptions,
      prizeTrackingTickets,
      signers,
      instructions,
    );
  }

  await sendTransactionsWithManualRetry(
    connection,
    wallet,
    instructions,
    signers,
  );
}
Example #28
Source File: nft.tsx    From metaplex with Apache License 2.0 4 votes vote down vote up
mintNFT = async (
  connection: Connection,
  wallet: WalletSigner | undefined,
  endpoint: ENDPOINT_NAME,
  files: File[],
  metadata: {
    name: string;
    symbol: string;
    description: string;
    image: string | undefined;
    animation_url: string | undefined;
    attributes: Attribute[] | undefined;
    external_url: string;
    properties: any;
    creators: Creator[] | null;
    sellerFeeBasisPoints: number;
    collection?: string;
    uses?: Uses;
  },
  progressCallback: Dispatch<SetStateAction<number>>,
  maxSupply?: number,
): Promise<{
  metadataAccount: StringPublicKey;
} | void> => {
  if (!wallet?.publicKey) return;

  const metadataContent = {
    name: metadata.name,
    symbol: metadata.symbol,
    description: metadata.description,
    seller_fee_basis_points: metadata.sellerFeeBasisPoints,
    image: metadata.image,
    animation_url: metadata.animation_url,
    attributes: metadata.attributes,
    external_url: metadata.external_url,
    properties: {
      ...metadata.properties,
      creators: metadata.creators?.map(creator => {
        return {
          address: creator.address,
          share: creator.share,
        };
      }),
    },
    collection: metadata.collection
      ? new PublicKey(metadata.collection).toBase58()
      : null,
    use: metadata.uses ? metadata.uses : null,
  };

  const realFiles: File[] = [
    ...files,
    new File([JSON.stringify(metadataContent)], RESERVED_METADATA),
  ];

  const { instructions: pushInstructions, signers: pushSigners } =
    await prepPayForFilesTxn(wallet, realFiles);

  progressCallback(1);

  const TOKEN_PROGRAM_ID = programIds().token;

  // Allocate memory for the account
  const mintRent = await connection.getMinimumBalanceForRentExemption(
    MintLayout.span,
  );
  // const accountRent = await connection.getMinimumBalanceForRentExemption(
  //   AccountLayout.span,
  // );

  // This owner is a temporary signer and owner of metadata we use to circumvent requesting signing
  // twice post Arweave. We store in an account (payer) and use it post-Arweave to update MD with new link
  // then give control back to the user.
  // const payer = new Account();
  const payerPublicKey = wallet.publicKey.toBase58();
  const instructions: TransactionInstruction[] = [...pushInstructions];
  const signers: Keypair[] = [...pushSigners];

  // This is only temporarily owned by wallet...transferred to program by createMasterEdition below
  const mintKey = createMint(
    instructions,
    wallet.publicKey,
    mintRent,
    0,
    // Some weird bug with phantom where it's public key doesnt mesh with data encode wellff
    toPublicKey(payerPublicKey),
    toPublicKey(payerPublicKey),
    signers,
  ).toBase58();

  const recipientKey = (
    await findProgramAddress(
      [
        wallet.publicKey.toBuffer(),
        programIds().token.toBuffer(),
        toPublicKey(mintKey).toBuffer(),
      ],
      programIds().associatedToken,
    )
  )[0];

  createAssociatedTokenAccountInstruction(
    instructions,
    toPublicKey(recipientKey),
    wallet.publicKey,
    wallet.publicKey,
    toPublicKey(mintKey),
  );

  const metadataAccount = await createMetadataV2(
    new DataV2({
      symbol: metadata.symbol,
      name: metadata.name,
      uri: ' '.repeat(64), // size of url for arweave
      sellerFeeBasisPoints: metadata.sellerFeeBasisPoints,
      creators: metadata.creators,
      collection: metadata.collection
        ? new Collection({
            key: new PublicKey(metadata.collection).toBase58(),
            verified: false,
          })
        : null,
      uses: metadata.uses || null,
    }),
    payerPublicKey,
    mintKey,
    payerPublicKey,
    instructions,
    wallet.publicKey.toBase58(),
  );
  progressCallback(2);

  // TODO: enable when using payer account to avoid 2nd popup
  // const block = await connection.getRecentBlockhash('singleGossip');
  // instructions.push(
  //   SystemProgram.transfer({
  //     fromPubkey: wallet.publicKey,
  //     toPubkey: payerPublicKey,
  //     lamports: 0.5 * LAMPORTS_PER_SOL // block.feeCalculator.lamportsPerSignature * 3 + mintRent, // TODO
  //   }),
  // );

  const { txid } = await sendTransactionWithRetry(
    connection,
    wallet,
    instructions,
    signers,
    'single',
  );
  progressCallback(3);

  try {
    await connection.confirmTransaction(txid, 'max');
    progressCallback(4);
  } catch {
    // ignore
  }

  // Force wait for max confirmations
  // await connection.confirmTransaction(txid, 'max');
  await connection.getParsedConfirmedTransaction(txid, 'confirmed');

  progressCallback(5);

  // this means we're done getting AR txn setup. Ship it off to ARWeave!
  const data = new FormData();
  data.append('transaction', txid);
  data.append('env', endpoint);

  const tags = realFiles.reduce(
    (acc: Record<string, Array<{ name: string; value: string }>>, f) => {
      acc[f.name] = [{ name: 'mint', value: mintKey }];
      return acc;
    },
    {},
  );
  data.append('tags', JSON.stringify(tags));
  realFiles.map(f => data.append('file[]', f));

  // TODO: convert to absolute file name for image

  const result: IArweaveResult = await uploadToArweave(data);
  progressCallback(6);

  const metadataFile = result.messages?.find(
    m => m.filename === RESERVED_TXN_MANIFEST,
  );
  if (metadataFile?.transactionId && wallet.publicKey) {
    const updateInstructions: TransactionInstruction[] = [];
    const updateSigners: Keypair[] = [];

    // TODO: connect to testnet arweave
    const arweaveLink = `https://arweave.net/${metadataFile.transactionId}`;
    await updateMetadataV2(
      new DataV2({
        symbol: metadata.symbol,
        name: metadata.name,
        uri: arweaveLink,
        sellerFeeBasisPoints: metadata.sellerFeeBasisPoints,
        creators: metadata.creators,
        collection: metadata.collection
          ? new Collection({
              key: new PublicKey(metadata.collection).toBase58(),
              verified: false,
            })
          : null,
        uses: metadata.uses || null,
      }),
      undefined,
      undefined,
      mintKey,
      payerPublicKey,
      updateInstructions,
      metadataAccount,
    );

    updateInstructions.push(
      Token.createMintToInstruction(
        TOKEN_PROGRAM_ID,
        toPublicKey(mintKey),
        toPublicKey(recipientKey),
        toPublicKey(payerPublicKey),
        [],
        1,
      ),
    );

    progressCallback(7);
    // // In this instruction, mint authority will be removed from the main mint, while
    // // minting authority will be maintained for the Printing mint (which we want.)
    await createMasterEditionV3(
      maxSupply !== undefined ? new BN(maxSupply) : undefined,
      mintKey,
      payerPublicKey,
      payerPublicKey,
      payerPublicKey,
      updateInstructions,
    );

    // TODO: enable when using payer account to avoid 2nd popup
    /*  if (maxSupply !== undefined)
      updateInstructions.push(
        setAuthority({
          target: authTokenAccount,
          currentAuthority: payerPublicKey,
          newAuthority: wallet.publicKey,
          authorityType: 'AccountOwner',
        }),
      );
*/
    // TODO: enable when using payer account to avoid 2nd popup
    // Note with refactoring this needs to switch to the updateMetadataAccount command
    // await transferUpdateAuthority(
    //   metadataAccount,
    //   payerPublicKey,
    //   wallet.publicKey,
    //   updateInstructions,
    // );

    progressCallback(8);

    await sendTransactionWithRetry(
      connection,
      wallet,
      updateInstructions,
      updateSigners,
    );

    notify({
      message: 'Art created on Solana',
      description: (
        <a href={arweaveLink} target="_blank" rel="noopener noreferrer">
          Arweave Link
        </a>
      ),
      type: 'success',
    });

    // TODO: refund funds

    // send transfer back to user
  }
  // TODO:
  // 1. Jordan: --- upload file and metadata to storage API
  // 2. pay for storage by hashing files and attaching memo for each file

  return { metadataAccount };
}
Example #29
Source File: createVault.ts    From metaplex with Apache License 2.0 4 votes vote down vote up
// This command creates the external pricing oracle a vault
// This gets the vault ready for adding the tokens.
export async function createVault(
  connection: Connection,
  wallet: WalletSigner,
  priceMint: StringPublicKey,
  externalPriceAccount: StringPublicKey,
): Promise<{
  vault: StringPublicKey;
  fractionalMint: StringPublicKey;
  redeemTreasury: StringPublicKey;
  fractionTreasury: StringPublicKey;
  instructions: TransactionInstruction[];
  signers: Keypair[];
}> {
  if (!wallet.publicKey) throw new WalletNotConnectedError();

  const PROGRAM_IDS = utils.programIds();

  const signers: Keypair[] = [];
  const instructions: TransactionInstruction[] = [];

  const accountRentExempt = await connection.getMinimumBalanceForRentExemption(
    AccountLayout.span,
  );

  const mintRentExempt = await connection.getMinimumBalanceForRentExemption(
    MintLayout.span,
  );

  const vaultRentExempt = await connection.getMinimumBalanceForRentExemption(
    MAX_VAULT_SIZE,
  );

  const vault = Keypair.generate();

  const vaultAuthority = (
    await findProgramAddress(
      [
        Buffer.from(VAULT_PREFIX),
        toPublicKey(PROGRAM_IDS.vault).toBuffer(),
        vault.publicKey.toBuffer(),
      ],
      toPublicKey(PROGRAM_IDS.vault),
    )
  )[0];

  const fractionalMint = createMint(
    instructions,
    wallet.publicKey,
    mintRentExempt,
    0,
    toPublicKey(vaultAuthority),
    toPublicKey(vaultAuthority),
    signers,
  ).toBase58();

  const redeemTreasury = createTokenAccount(
    instructions,
    wallet.publicKey,
    accountRentExempt,
    toPublicKey(priceMint),
    toPublicKey(vaultAuthority),
    signers,
  ).toBase58();

  const fractionTreasury = createTokenAccount(
    instructions,
    wallet.publicKey,
    accountRentExempt,
    toPublicKey(fractionalMint),
    toPublicKey(vaultAuthority),
    signers,
  ).toBase58();

  const uninitializedVault = SystemProgram.createAccount({
    fromPubkey: wallet.publicKey,
    newAccountPubkey: vault.publicKey,
    lamports: vaultRentExempt,
    space: MAX_VAULT_SIZE,
    programId: toPublicKey(PROGRAM_IDS.vault),
  });
  instructions.push(uninitializedVault);
  signers.push(vault);

  await initVault(
    true,
    fractionalMint,
    redeemTreasury,
    fractionTreasury,
    vault.publicKey.toBase58(),
    wallet.publicKey.toBase58(),
    externalPriceAccount,
    instructions,
  );

  return {
    vault: vault.publicKey.toBase58(),
    fractionalMint,
    redeemTreasury,
    fractionTreasury,
    signers,
    instructions,
  };
}