@solana/spl-token#MintInfo TypeScript Examples

The following examples show how to use @solana/spl-token#MintInfo. 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: utils.ts    From metaplex with Apache License 2.0 8 votes vote down vote up
export function toLamports(
  account?: TokenAccount | number,
  mint?: MintInfo,
): number {
  if (!account) {
    return 0;
  }

  const amount =
    typeof account === 'number' ? account : account.info.amount?.toNumber();

  const precision = Math.pow(10, mint?.decimals || 0);
  return Math.floor(amount * precision);
}
Example #2
Source File: utils.ts    From metaplex with Apache License 2.0 7 votes vote down vote up
export function convert(
  account?: TokenAccount | number,
  mint?: MintInfo,
  rate: number = 1.0,
): number {
  if (!account) {
    return 0;
  }

  const amount =
    typeof account === 'number' ? account : account.info.amount?.toNumber();

  const precision = Math.pow(10, mint?.decimals || 0);
  const result = (amount / precision) * rate;

  return result;
}
Example #3
Source File: utils.ts    From metaplex with Apache License 2.0 7 votes vote down vote up
export function fromLamports(
  account?: TokenAccount | number | BN,
  mint?: MintInfo,
  rate: number = 1.0,
): number {
  if (!account) {
    return 0;
  }

  const amount = Math.floor(
    typeof account === 'number'
      ? account
      : BN.isBN(account)
      ? account.toNumber()
      : account.info.amount.toNumber(),
  );

  const precision = Math.pow(10, mint?.decimals || 9);
  return (amount / precision) * rate;
}
Example #4
Source File: utils.ts    From metaplex with Apache License 2.0 7 votes vote down vote up
export function formatTokenAmount(
  account?: TokenAccount | number | BN,
  mint?: MintInfo,
  rate: number = 1.0,
  prefix = '',
  suffix = '',
  precision = 3,
  abbr = false,
): string {
  if (!account) {
    return '';
  }

  return `${[prefix]}${formatAmount(
    fromLamports(account, mint, rate),
    precision,
    abbr,
  )}${suffix}`;
}
Example #5
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 #6
Source File: index.ts    From serum-ts with Apache License 2.0 6 votes vote down vote up
export async function getMintInfo(
  provider: Provider,
  addr: PublicKey,
): Promise<MintInfo> {
  let depositorAccInfo = await provider.connection.getAccountInfo(addr);
  if (depositorAccInfo === null) {
    throw new Error('Failed to find token account');
  }
  return parseMintAccount(depositorAccInfo.data);
}
Example #7
Source File: pools.ts    From serum-ts with Apache License 2.0 6 votes vote down vote up
async getCachedMintAccount(
    connection: Connection,
    pubkey: PublicKey | string,
    cacheDurationMs = 0,
  ): Promise<MintInfo> {
    return this.cached<MintInfo>(
      () => getMintAccount(connection, pubkey),
      this._mintAccountsCache,
      typeof pubkey === 'string' ? pubkey : pubkey.toBase58(),
      cacheDurationMs,
    );
  }
Example #8
Source File: pools.ts    From serum-ts with Apache License 2.0 6 votes vote down vote up
getMintAccount = async (
  connection: Connection,
  pubKey: PublicKey | string,
): Promise<MintInfo> => {
  const address = typeof pubKey === 'string' ? new PublicKey(pubKey) : pubKey;
  const info = await connection.getAccountInfo(address);
  if (info === null) {
    throw new Error('Failed to find mint account');
  }
  return deserializeMint(info.data);
}
Example #9
Source File: Token.tsx    From swap-ui with Apache License 2.0 6 votes vote down vote up
export function useMint(mint?: PublicKey): MintInfo | undefined | null {
  const { provider } = useTokenContext();
  // Lazy load the mint account if needeed.
  const asyncMintInfo = useAsync(async () => {
    if (!mint) {
      return undefined;
    }
    if (_MINT_CACHE.get(mint.toString())) {
      return _MINT_CACHE.get(mint.toString());
    }

    const mintClient = new Token(
      provider.connection,
      mint,
      TOKEN_PROGRAM_ID,
      new Account()
    );
    const mintInfo = mintClient.getMintInfo();
    _MINT_CACHE.set(mint.toString(), mintInfo);
    return mintInfo;
  }, [provider.connection, mint]);

  if (asyncMintInfo.result) {
    return asyncMintInfo.result;
  }
  return undefined;
}
Example #10
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 #11
Source File: queryExtendedMetadata.ts    From metaplex with Apache License 2.0 6 votes vote down vote up
queryExtendedMetadata = async (
  connection: Connection,
  mintToMeta: Record<string, ParsedAccount<Metadata>>,
) => {
  const mintToMetadata = { ...mintToMeta };

  const mints = await getMultipleAccounts(
    connection,
    [...Object.keys(mintToMetadata)].filter(k => !cache.get(k)),
    'single',
  );

  mints.keys.forEach((key, index) => {
    const mintAccount = mints.array[index];
    if (mintAccount) {
      const mint = cache.add(
        key,
        mintAccount,
        MintParser,
        false,
      ) as ParsedAccount<MintInfo>;
      if (!mint.info.supply.eqn(1) || mint.info.decimals !== 0) {
        // naive not NFT check
        delete mintToMetadata[key];
      } else {
        // const metadata = mintToMetadata[key];
      }
    }
  });

  // await Promise.all([...extendedMetadataFetch.values()]);
  const metadata = [...Object.values(mintToMetadata)];

  return {
    metadata,
    mintToMetadata,
  };
}
Example #12
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 #13
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 #14
Source File: accounts.tsx    From metaplex with Apache License 2.0 6 votes vote down vote up
export function useMint(key?: string | PublicKey) {
  const connection = useConnection();
  const [mint, setMint] = useState<MintInfo>();

  const id = typeof key === 'string' ? key : key?.toBase58();

  useEffect(() => {
    if (!id) {
      return;
    }

    cache
      .query(connection, id, MintParser)
      .then(acc => setMint(acc.info as any))
      .catch(err => console.log(err));

    const dispose = cache.emitter.onCache(e => {
      const event = e;
      if (event.id === id) {
        cache
          .query(connection, id, MintParser)
          .then(mint => setMint(mint.info as any));
      }
    });
    return () => {
      dispose();
    };
  }, [connection, id]);

  return mint;
}
Example #15
Source File: Token.tsx    From swap-ui with Apache License 2.0 5 votes vote down vote up
export function setMintCache(pk: PublicKey, account: MintInfo) {
  _MINT_CACHE.set(pk.toString(), new Promise((resolve) => resolve(account)));
}
Example #16
Source File: Token.tsx    From swap-ui with Apache License 2.0 5 votes vote down vote up
_MINT_CACHE = new Map<string, Promise<MintInfo>>([
  [SOL_MINT.toString(), { decimals: 9 }],
])
Example #17
Source File: OpenOrdersDialog.tsx    From swap-ui with Apache License 2.0 5 votes vote down vote up
function toDisplay(mintInfo: MintInfo | undefined | null, value: BN): string {
  if (!mintInfo) {
    return value.toNumber().toString();
  }
  return (value.toNumber() / 10 ** mintInfo.decimals).toFixed(
    mintInfo.decimals
  );
}
Example #18
Source File: mint.ts    From easy-spl with Apache License 2.0 5 votes vote down vote up
async getInfo(): Promise<MintInfo> {
    return mint.get.info(this.conn, this.key)
  }
Example #19
Source File: pools.ts    From serum-ts with Apache License 2.0 5 votes vote down vote up
private _mintAccountsCache: {
    [publickKey: string]: { value: MintInfo; ts: number };
  };
Example #20
Source File: mint.ts    From easy-spl with Apache License 2.0 5 votes vote down vote up
getMintInfo = async (
  conn: web3.Connection,
  mint: web3.PublicKey
): Promise<MintInfo> => {
  const token = new Token(conn, mint, TOKEN_PROGRAM_ID, {} as any)
  return token.getMintInfo()
}
Example #21
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 #22
Source File: cache.ts    From metaplex with Apache License 2.0 5 votes vote down vote up
pendingMintCalls = new Map<string, Promise<MintInfo>>()
Example #23
Source File: cache.ts    From metaplex with Apache License 2.0 5 votes vote down vote up
mintCache = new Map<string, MintInfo>()
Example #24
Source File: newOrder.ts    From psyoptions with Apache License 2.0 4 votes vote down vote up
describe("cpi_examples newOrder", () => {
  const program = anchor.workspace.CpiExamples as Program<CpiExamples>;
  const provider = program.provider;
  const americanOptionsProgram = anchor.workspace
    .PsyAmerican as Program<PsyAmerican>;

  const payer = web3.Keypair.fromSecretKey(
    Buffer.from(
      JSON.parse(
        require("fs").readFileSync(process.env.ANCHOR_WALLET, {
          encoding: "utf-8",
        })
      )
    )
  );
  const wallet = payer;
  const mintAuthority = anchor.web3.Keypair.generate();
  let underlyingToken: Token, usdcToken: Token, optionToken: Token;
  // Global PsyOptions variables
  let optionMarket: OptionMarketV2;
  // Global DEX variables
  let marketProxy: MarketProxy,
    marketAuthority: anchor.web3.PublicKey,
    marketAuthorityBump: number,
    usdcMint: anchor.web3.PublicKey,
    usdcMintInfo: MintInfo,
    referral: anchor.web3.PublicKey,
    openOrders: PublicKey,
    openOrdersBump: number,
    vault: anchor.web3.PublicKey,
    vaultBumpSeed: number,
    vaultAuthority: anchor.web3.PublicKey,
    vaultAuthBump: number;
  before(async () => {
    // Setup - Create an OptionMarket
    const {
      optionMarket: newOptionMarket,
      remainingAccounts,
      instructions,
    } = await initSetup(
      provider,
      wallet,
      mintAuthority,
      americanOptionsProgram
    );
    optionMarket = newOptionMarket;
    await initOptionMarket(
      americanOptionsProgram,
      wallet,
      optionMarket,
      remainingAccounts,
      instructions
    );
    [usdcMint] = await createMintAndVault(
      provider,
      new anchor.BN("1000000000000000000"),
      undefined,
      6
    );
    // Initialize a permissioned Serum Market
    ({ marketAuthority, marketAuthorityBump } = await getMarketAndAuthorityInfo(
      americanOptionsProgram,
      optionMarket,
      DEX_PID,
      usdcMint
    ));
    // Setup - Create a Serum market for the OptionMarket's option tokens
    ({ marketA: marketProxy } = await initMarket(
      provider,
      americanOptionsProgram,
      marketLoader(provider, program, optionMarket.key, marketAuthorityBump),
      optionMarket,
      usdcMint
    ));
    // Set the token variables for use in later tests
    underlyingToken = new Token(
      provider.connection,
      optionMarket.underlyingAssetMint,
      TOKEN_PROGRAM_ID,
      wallet
    );
    optionToken = new Token(
      provider.connection,
      optionMarket.optionMint,
      TOKEN_PROGRAM_ID,
      wallet
    );
    usdcToken = new Token(
      provider.connection,
      usdcMint,
      TOKEN_PROGRAM_ID,
      wallet
    );
    referral = await usdcToken.createAssociatedTokenAccount(FEE_OWNER_KEY);
  });

  describe("cpi_examples initNewOrderVault", () => {
    it("should create a USDC vault owned by the program", async () => {
      // Generate a PDA for the USDC vault
      [vault, vaultBumpSeed] = await anchor.web3.PublicKey.findProgramAddress(
        [usdcToken.publicKey.toBuffer(), textEncoder.encode("vault")],
        program.programId
      );
      [vaultAuthority, vaultAuthBump] =
        await anchor.web3.PublicKey.findProgramAddress(
          [vault.toBuffer(), textEncoder.encode("vaultAuthority")],
          program.programId
        );
      try {
        await program.rpc.initNewOrderVault({
          accounts: {
            authority: wallet.publicKey,
            usdcMint: usdcMint,
            vault,
            vaultAuthority,
            tokenProgram: TOKEN_PROGRAM_ID,
            rent: SYSVAR_RENT_PUBKEY,
            systemProgram: SystemProgram.programId,
          },
        });
      } catch (err) {
        console.log((err as Error).toString());
        throw err;
      }

      // validate that the vault was initialized and owned by the program
      const vaultAcct = await usdcToken.getAccountInfo(vault);
      assert.ok(vaultAcct.owner.equals(vaultAuthority));
    });
  });

  describe("place newOrder", () => {
    before(async () => {
      // Vault is already initialized because these tests run sequentially
      // transfer USDC to that vault so it can place an order
      usdcMintInfo = await usdcToken.getMintInfo();
      await usdcToken.mintTo(
        vault,
        wallet.publicKey,
        [],
        new u64(10_000_000 * usdcMintInfo.decimals)
      );
      // Get the open orders account that needs to be optionally created
      [openOrders, openOrdersBump] = await PublicKey.findProgramAddress(
        [
          openOrdersSeed,
          marketProxy.dexProgramId.toBuffer(),
          marketProxy.market.address.toBuffer(),
          // NOTE: For other developers, this should be changed to be the User or Vault that has the authority over the account.
          vaultAuthority.toBuffer(),
        ],
        americanOptionsProgram.programId
      );
    });

    it("should create an open orders account and place an order on the Serum market", async () => {
      // test the vault contains USDC
      const vaultAcct = await usdcToken.getAccountInfo(vault);
      assert.equal(
        vaultAcct.amount.toString(),
        new u64(10_000_000 * usdcMintInfo.decimals).toString()
      );
      // test the order book is blank
      let bids = await marketProxy.market.loadBids(provider.connection);
      let l2 = await bids.getL2(3);
      assert.equal(l2.length, 0);

      const price = 1;
      const size = 22;

      // Run placeOrder instruction for vault
      try {
        await program.rpc.placeOrder(
          vaultAuthBump,
          openOrdersBump,
          marketAuthorityBump,
          Side.Bid, // Side
          marketProxy.market.priceNumberToLots(price), // liimit_price
          marketProxy.market.baseSizeNumberToLots(size), // max_coin_qty
          OrderType.PostOnly, // order_type
          new anchor.BN(999), // client_order_id
          SelfTradeBehavior.AbortTransaction, // self_trade_behavior
          new anchor.BN(65535), // limit - no idea what this is
          new anchor.BN(
            // @ts-ignore: serum
            marketProxy.market._decoded.quoteLotSize.toNumber()
          ).mul(
            marketProxy.market
              .baseSizeNumberToLots(size)
              .mul(marketProxy.market.priceNumberToLots(price))
          ), // max_native_pc_qty_including_fees - no idea what exactly this is
          {
            accounts: {
              userAuthority: wallet.publicKey,
              psyAmericanProgram: americanOptionsProgram.programId,
              dexProgram: DEX_PID,
              openOrders,
              market: marketProxy.market.address,
              psyMarketAuthority: marketAuthority,
              vault,
              vaultAuthority,
              // @ts-ignore: Dumb serum stuff
              requestQueue: marketProxy.market._decoded.requestQueue,
              // @ts-ignore: Dumb serum stuff
              eventQueue: marketProxy.market._decoded.eventQueue,
              marketBids: marketProxy.market.bidsAddress,
              marketAsks: marketProxy.market.asksAddress,
              // @ts-ignore: Dumb serum stuff
              coinVault: marketProxy.market._decoded.baseVault,
              // @ts-ignore: Dumb serum stuff
              pcVault: marketProxy.market._decoded.quoteVault,

              systemProgram: SystemProgram.programId,
              tokenProgram: TOKEN_PROGRAM_ID,
              rent: SYSVAR_RENT_PUBKEY,
            },
          }
        );
      } catch (err) {
        console.log("*** error", (err as Error).toString());
        throw err;
      }
      // Test that a new open orders account was created
      const openOrdersAcct = await OpenOrders.load(
        provider.connection,
        openOrders,
        DEX_PID
      );
      assert.ok(openOrdersAcct.owner.equals(openOrders));

      // test that the order book contains the new order.
      bids = await marketProxy.market.loadBids(provider.connection);
      l2 = await bids.getL2(3);
      assert.equal(l2.length, 1);
      assert.equal(l2[0][0], price);
      assert.equal(l2[0][1], size);
    });

    describe("Open orders account exists", () => {
      it("should place the order without fail", async () => {
        // Test that the open orders account already exists
        const openOrdersAcct = await OpenOrders.load(
          provider.connection,
          openOrders,
          DEX_PID
        );
        assert.ok(openOrdersAcct.owner.equals(openOrders));
        // test that the order book contains the new order.
        let bids = await marketProxy.market.loadBids(provider.connection);
        let l2 = await bids.getL2(3);
        assert.equal(l2.length, 1);

        const price = 2;
        const size = 1;

        // Run placeOrder instruction for vault
        try {
          await program.rpc.placeOrder(
            vaultAuthBump,
            openOrdersBump,
            marketAuthorityBump,
            Side.Bid, // Side
            marketProxy.market.priceNumberToLots(price), // liimit_price
            marketProxy.market.baseSizeNumberToLots(size), // max_coin_qty
            OrderType.PostOnly, // order_type
            new anchor.BN(998), // client_order_id
            SelfTradeBehavior.AbortTransaction, // self_trade_behavior
            new anchor.BN(65535), // limit
            new anchor.BN(
              // @ts-ignore: serum
              marketProxy.market._decoded.quoteLotSize.toNumber()
            ).mul(
              marketProxy.market
                .baseSizeNumberToLots(size)
                .mul(marketProxy.market.priceNumberToLots(price))
            ), // max_native_pc_qty_including_fees
            {
              accounts: {
                userAuthority: wallet.publicKey,
                psyAmericanProgram: americanOptionsProgram.programId,
                dexProgram: DEX_PID,
                openOrders,
                market: marketProxy.market.address,
                psyMarketAuthority: marketAuthority,
                vault,
                vaultAuthority,
                // @ts-ignore: Dumb serum stuff
                requestQueue: marketProxy.market._decoded.requestQueue,
                // @ts-ignore: Dumb serum stuff
                eventQueue: marketProxy.market._decoded.eventQueue,
                marketBids: marketProxy.market.bidsAddress,
                marketAsks: marketProxy.market.asksAddress,
                // @ts-ignore: Dumb serum stuff
                coinVault: marketProxy.market._decoded.baseVault,
                // @ts-ignore: Dumb serum stuff
                pcVault: marketProxy.market._decoded.quoteVault,

                systemProgram: SystemProgram.programId,
                tokenProgram: TOKEN_PROGRAM_ID,
                rent: SYSVAR_RENT_PUBKEY,
              },
            }
          );
        } catch (err) {
          console.log("*** error", (err as Error).toString());
          throw err;
        }
        bids = await marketProxy.market.loadBids(provider.connection);
        l2 = await bids.getL2(3);
        assert.equal(l2.length, 2);
      });
    });
  });
});
Example #25
Source File: index.tsx    From metaplex with Apache License 2.0 4 votes vote down vote up
BidLine = (props: {
  bid: any;
  index: number;
  mint?: MintInfo;
  isCancelled?: boolean;
  isActive?: boolean;
  mintKey: string;
}) => {
  const { bid, mint, isCancelled, mintKey } = props;
  const { publicKey } = useWallet();
  const bidder = bid.info.bidderPubkey;
  const isme = publicKey?.toBase58() === bidder;
  const tokenInfo = useTokenList().subscribedTokens.filter(
    m => m.address == mintKey,
  )[0];

  // Get Twitter Handle from address
  const connection = useConnection();
  const [bidderTwitterHandle, setBidderTwitterHandle] = useState('');
  useEffect(() => {
    const getTwitterHandle = async (
      connection: Connection,
      bidder: StringPublicKey,
    ): Promise<string | undefined> => {
      try {
        const [twitterHandle] = await getHandleAndRegistryKey(
          connection,
          toPublicKey(bidder),
        );
        setBidderTwitterHandle(twitterHandle);
      } catch (err) {
        console.warn(`err`);
        return undefined;
      }
    };
    getTwitterHandle(connection, bidder);
  }, [bidderTwitterHandle]);
  const { width } = useWindowDimensions();
  if (width < 768) {
    return (
      <Row className="mobile-bid-history">
        <div className="bid-info-container">
          <div className="bidder-info-container">
            <Identicon
              style={{
                width: 24,
                height: 24,
                marginRight: 10,
                marginTop: 2,
              }}
              address={bidder}
            />
            {bidderTwitterHandle ? (
              <a
                target="_blank"
                title={shortenAddress(bidder)}
                href={`https://twitter.com/${bidderTwitterHandle}`}
                rel="noreferrer"
              >{`@${bidderTwitterHandle}`}</a>
            ) : (
              shortenAddress(bidder)
            )}
          </div>
          <div>
            {!isCancelled && (
              <div className={'flex '}>
                {isme && (
                  <>
                    <CheckOutlined />
                    &nbsp;
                  </>
                )}
                <AmountLabel
                  style={{ marginBottom: 0, fontSize: '16px' }}
                  containerStyle={{
                    flexDirection: 'row',
                    alignItems: 'center',
                  }}
                  displaySymbol={tokenInfo?.symbol || 'CUSTOM'}
                  iconSize={24}
                  amount={formatTokenAmount(bid.info.lastBid, mint)}
                />
              </div>
            )}
          </div>
        </div>
        <div className="bid-info-container">
          {format(bid.info.lastBidTimestamp.toNumber() * 1000)}
        </div>
      </Row>
    );
  } else {
    return (
      <Row className={'bid-history'}>
        {isCancelled && (
          <div
            style={{
              position: 'absolute',
              left: 0,
              width: '100%',
              height: 1,
              background: 'grey',
              top: 'calc(50% - 1px)',
              zIndex: 2,
            }}
          />
        )}
        <Col span={8}>
          {!isCancelled && (
            <div className={'flex '}>
              {isme && (
                <>
                  <CheckOutlined />
                  &nbsp;
                </>
              )}
              <AmountLabel
                style={{ marginBottom: 0, fontSize: '16px' }}
                containerStyle={{
                  flexDirection: 'row',
                  alignItems: 'center',
                }}
                displaySymbol={tokenInfo?.symbol || 'CUSTOM'}
                tokenInfo={tokenInfo}
                iconSize={24}
                amount={formatTokenAmount(bid.info.lastBid, mint)}
              />
            </div>
          )}
        </Col>
        <Col span={8} style={{ opacity: 0.7 }}>
          {/* uses milliseconds */}
          {format(bid.info.lastBidTimestamp.toNumber() * 1000)}
        </Col>
        <Col span={8}>
          <div className={'flex-right'}>
            <Identicon
              style={{
                width: 24,
                height: 24,
                marginRight: 10,
                marginTop: 2,
              }}
              address={bidder}
            />{' '}
            <span style={{ opacity: 0.7 }}>
              {bidderTwitterHandle ? (
                <Row className="pubkey-row">
                  <a
                    target="_blank"
                    title={shortenAddress(bidder)}
                    href={`https://twitter.com/${bidderTwitterHandle}`}
                    rel="noreferrer"
                  >{`@${bidderTwitterHandle}`}</a>
                  <ClickToCopy
                    className="copy-pubkey"
                    copyText={bidder as string}
                  />
                </Row>
              ) : (
                <Row className="pubkey-row">
                  {shortenAddress(bidder)}
                  <ClickToCopy
                    className="copy-pubkey"
                    copyText={bidder as string}
                  />
                </Row>
              )}
            </span>
          </div>
        </Col>
      </Row>
    );
  }
}
Example #26
Source File: billing.tsx    From metaplex with Apache License 2.0 4 votes vote down vote up
InnerBillingView = ({
  auctionView,
  wallet,
  connection,
  mint,
}: {
  auctionView: AuctionView;
  wallet: WalletSigner;
  connection: Connection;
  mint: MintInfo;
}) => {
  const id = auctionView.thumbnail.metadata.pubkey;
  const art = useArt(id);
  const balance = useUserBalance(auctionView.auction.info.tokenMint);
  const [escrowBalance, setEscrowBalance] = useState<number | undefined>();
  const { whitelistedCreatorsByCreator, pullBillingPage } = useMeta();
  useEffect(() => {
    pullBillingPage(id);
  }, []);
  const [escrowBalanceRefreshCounter, setEscrowBalanceRefreshCounter] =
    useState(0);

  useEffect(() => {
    connection
      .getTokenAccountBalance(
        toPublicKey(auctionView.auctionManager.acceptPayment),
      )
      .then(resp => {
        if (resp.value.uiAmount !== undefined && resp.value.uiAmount !== null)
          setEscrowBalance(resp.value.uiAmount);
      });
  }, [escrowBalanceRefreshCounter]);

  const myPayingAccount = balance.accounts[0];

  const { accountByMint } = useUserAccounts();

  const {
    bidsToClaim,
    totalWinnerPayments,
    payoutTickets,
    participationPossibleTotal,
    participationUnredeemedTotal,
    hasParticipation,
  } = useBillingInfo({
    auctionView,
  });
  return (
    <Content>
      <Col>
        <Row
          style={{ margin: '0 30px', textAlign: 'left', fontSize: '1.4rem' }}
        >
          <Col span={12}>
            <ArtContent
              pubkey={id}
              className="artwork-image"
              allowMeshRender={true}
            />
          </Col>
          <Col span={12}>
            <div style={{ fontWeight: 700 }}>{art.title}</div>
            <br />
            <div className="info-header">TOTAL AUCTION VALUE</div>
            <div className="escrow">
              {auctionView.auctionManager.acceptPayment ==
              WRAPPED_SOL_MINT.toBase58()
                ? '◎'
                : ''}
              {fromLamports(
                totalWinnerPayments + participationPossibleTotal,
                mint,
              )}
            </div>
            <br />
            <div className="info-header">TOTAL AUCTION REDEEMED VALUE</div>
            <div className="escrow">
              {auctionView.auctionManager.acceptPayment ==
              WRAPPED_SOL_MINT.toBase58()
                ? '◎'
                : ''}
              {fromLamports(
                totalWinnerPayments +
                  participationPossibleTotal -
                  participationUnredeemedTotal,
                mint,
              )}
            </div>
            <br />
            <div className="info-header">
              TOTAL COLLECTED BY ARTISTS AND AUCTIONEER
            </div>
            <div className="escrow">
              {auctionView.auctionManager.acceptPayment ==
              WRAPPED_SOL_MINT.toBase58()
                ? '◎'
                : ''}
              {fromLamports(
                Object.values(payoutTickets).reduce(
                  (acc, el) => (acc += el.sum),
                  0,
                ),
                mint,
              )}
            </div>
            <br />
            <div className="info-header">TOTAL UNSETTLED</div>
            <div className="escrow">
              {auctionView.auctionManager.acceptPayment ==
              WRAPPED_SOL_MINT.toBase58()
                ? '◎'
                : ''}
              {fromLamports(
                bidsToClaim.reduce(
                  (acc, el) => (acc += el.metadata.info.lastBid.toNumber()),
                  0,
                ),
                mint,
              )}
            </div>
            <br />
            <div className="info-header">TOTAL IN ESCROW</div>
            <div className="escrow">
              {escrowBalance !== undefined ? (
                `${
                  auctionView.auction.info.tokenMint ==
                  WRAPPED_SOL_MINT.toBase58()
                    ? '◎'
                    : ''
                } ${escrowBalance}`
              ) : (
                <Spin />
              )}
            </div>
            <br />
            {hasParticipation && (
              <>
                <div className="info-header">
                  TOTAL UNREDEEMED PARTICIPATION FEES OUTSTANDING
                </div>
                <div className="outstanding-open-editions">
                  {auctionView.auctionManager.acceptPayment ==
                  WRAPPED_SOL_MINT.toBase58()
                    ? '◎'
                    : ''}
                  {fromLamports(participationUnredeemedTotal, mint)}
                </div>
                <br />
              </>
            )}
            <br />
            <Button
              type="primary"
              size="large"
              className="action-btn"
              onClick={async () => {
                await settle(
                  connection,
                  wallet,
                  auctionView,
                  bidsToClaim.map(b => b.pot),
                  myPayingAccount.pubkey,
                  accountByMint,
                );
                setEscrowBalanceRefreshCounter(ctr => ctr + 1);
              }}
            >
              SETTLE OUTSTANDING
            </Button>
          </Col>
        </Row>
        <Row>
          <Table
            style={{ width: '100%' }}
            columns={[
              {
                title: 'Name',
                dataIndex: 'name',
                key: 'name',
              },
              {
                title: 'Address',
                dataIndex: 'address',
                key: 'address',
              },
              {
                title: 'Amount Paid',
                dataIndex: 'amountPaid',
                render: (val: number) => (
                  <span>◎{fromLamports(val, mint)}</span>
                ),
                key: 'amountPaid',
              },
            ]}
            dataSource={Object.keys(payoutTickets).map(t => ({
              key: t,
              name: whitelistedCreatorsByCreator[t]?.info?.name || 'N/A',
              address: t,
              amountPaid: payoutTickets[t].sum,
            }))}
          />
        </Row>
      </Col>
    </Content>
  );
}
Example #27
Source File: index.tsx    From metaplex with Apache License 2.0 4 votes vote down vote up
MemoizedBar = React.memo(function BarImpl(props: {
  sortedSales: number[];
  mint: MintInfo;
}) {
  const histogrammedData: Record<number, number> = {
    0: 0,
    5: 0,
    20: 0,
    50: 0,
    100: 0,
    500: 0,
    1000: 0,
    10000: 0,
  };
  const asArray = [0, 5, 20, 50, 100, 500, 1000, 10000];

  for (let i = 0; i < asArray.length; i++) {
    const currRange = asArray[i];

    if (i < asArray.length - 1) {
      const nextRange = asArray[i + 1];
      histogrammedData[currRange] = props.sortedSales.filter(
        s =>
          fromLamports(s, props.mint) >= currRange &&
          fromLamports(s, props.mint) < nextRange,
      ).length;
    } else {
      histogrammedData[currRange] = props.sortedSales.filter(
        s => fromLamports(s, props.mint) >= currRange,
      ).length;
    }
  }

  const histoData = {
    labels: [
      '◎ [0 - 5)',
      '◎ [5 - 20)',
      '◎ [20 - 50)',
      '◎ [50 - 100)',
      '◎ [100 - 500)',
      '◎ [500 - 1000)',
      '◎ [1000 - 10000)',
      '◎ [10000 -',
    ],
    datasets: [
      {
        label: '# bids in these bins',
        data: asArray.map(a => histogrammedData[a]),
        backgroundColor: [
          'rgba(255, 99, 132, 0.2)',
          'rgba(54, 162, 235, 0.2)',
          'rgba(255, 206, 86, 0.2)',
          'rgba(75, 192, 192, 0.2)',
          'rgba(153, 102, 255, 0.2)',
          'rgba(255, 159, 64, 0.2)',
          'rgba(255, 139, 24, 0.2)',
          'rgba(212, 39, 24, 0.2)',
        ],
        borderColor: [
          'rgba(255, 99, 132, 1)',
          'rgba(54, 162, 235, 1)',
          'rgba(255, 206, 86, 1)',
          'rgba(75, 192, 192, 1)',
          'rgba(153, 102, 255, 1)',
          'rgba(255, 159, 64, 1)',
          'rgba(255, 139, 24, 1)',
          'rgba(212, 39, 24, 1)',
        ],
        borderWidth: 1,
      },
    ],
  };

  const histoOptions = {
    scales: {
      yAxes: [
        {
          ticks: {
            beginAtZero: true,
          },
        },
      ],
    },
  };
  // @ts-ignore
  return <Bar data={histoData} options={histoOptions} />;
})
Example #28
Source File: index.tsx    From metaplex with Apache License 2.0 4 votes vote down vote up
function InnerAnalytics({ mint }: { mint: MintInfo }) {
  const [usersWithMetadata, setUsersWithMetadata] = useState<
    Record<string, boolean>
  >({});
  const [usersPublished, setUsersPublished] = useState<Record<string, boolean>>(
    {},
  );
  const [usersBid, setUsersBid] = useState<Record<string, boolean>>({});

  const [usersEngaged, setUsersEngaged] = useState<Record<string, boolean>>({});
  const [byType, setByType] = useState<Record<AuctionType, number>>({
    [AuctionType.Open]: 0,
    [AuctionType.Limited]: 0,
    [AuctionType.Tiered]: 0,
    [AuctionType.OneOfKind]: 0,
  });
  const [averageBids, setAverageBids] = useState<number>(0);
  const [averageSale, setAverageSale] = useState<number>(0);
  const [highestSale, setHighestSale] = useState<number>(0);

  const [sortedSales, setSortedSales] = useState<number[]>([]);
  const {
    metadata,
    // stores,
    auctionManagersByAuction,
    bidderPotsByAuctionAndBidder,
    auctionDataExtended,
  } = useMeta();

  const totalNFTs = metadata.length;
  // const totalMarketplaces = Object.values(stores).length;

  const auctionViews = useAuctions();

  return (
    <Content>
      <Col style={{ marginTop: 10 }}>
        <Button
          type="primary"
          size="large"
          className="action-btn"
          onClick={() =>
            rerun({
              auctionViews,
              auctionManagersByAuction,
              usersEngaged,
              auctionDataExtended,
              bidderPotsByAuctionAndBidder,
              metadata,
              setByType,
              setAverageBids,
              setUsersPublished,
              setAverageSale,
              setHighestSale,
              setSortedSales,
              setUsersWithMetadata,
              setUsersBid,
              setUsersEngaged,
            })
          }
        >
          RERUN CALCULATION
        </Button>
        <h1>Overview</h1>
        <h3>
          Total NFTs: {totalNFTs}
          {/* Total Marketplaces: {totalMarketplaces} */}
        </h3>
        <h1>User Breakdown</h1>
        <h3>Any Engagement: {Object.values(usersEngaged).length}</h3>
        <h3>That bid: {Object.values(usersBid).length}</h3>
        <h3>That sold items: {Object.values(usersPublished).length}</h3>
        <h3>That minted NFTs: {Object.values(usersWithMetadata).length}</h3>
        <h1>Sale Info</h1>
        <h3>
          Total Sales: ◎
          {fromLamports(
            sortedSales.reduce((acc, r) => (acc += r), 0),
            mint,
          )}
        </h3>
        <MemoizedBar sortedSales={sortedSales} mint={mint} />

        <h3>Highest Sale: ◎ {fromLamports(highestSale, mint)}</h3>
        <h3>Average Sale: ◎ {fromLamports(averageSale, mint)}</h3>
        <h1>Auction Info</h1>
        <h3>Average Bids per Auction: {averageBids}</h3>
        <MemoizedPie byType={byType} />
      </Col>
    </Content>
  );
}
Example #29
Source File: sendPlaceBid.ts    From metaplex with Apache License 2.0 4 votes vote down vote up
export async function setupPlaceBid(
  connection: Connection,
  wallet: WalletSigner,
  bidderTokenAccount: string | undefined,
  auctionView: AuctionView,
  accountsByMint: Map<string, TokenAccount>,
  // value entered by the user adjust to decimals of the mint
  // If BN, then assume instant sale and decimals already adjusted.
  amount: number | BN,
  overallInstructions: TransactionInstruction[][],
  overallSigners: Keypair[][],
): Promise<BN> {
  if (!wallet.publicKey) throw new WalletNotConnectedError();

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

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

  const tokenAccount = bidderTokenAccount
    ? (cache.get(bidderTokenAccount) as TokenAccount)
    : undefined;
  const mint = cache.get(
    tokenAccount ? tokenAccount.info.mint : QUOTE_MINT,
  ) as ParsedAccount<MintInfo>;

  const lamports =
    accountRentExempt +
    (typeof amount === 'number'
      ? toLamports(amount, mint.info)
      : amount.toNumber());

  let bidderPotTokenAccount: string | undefined;
  if (auctionView.myBidderPot) {
    bidderPotTokenAccount = auctionView.myBidderPot?.info.bidderPot;
    if (!auctionView.auction.info.ended()) {
      const cancelSigners: Keypair[][] = [];
      const cancelInstr: TransactionInstruction[][] = [];
      await setupCancelBid(
        auctionView,
        accountsByMint,
        accountRentExempt,
        wallet,
        cancelSigners,
        cancelInstr,
        connection,
      );
      signers = [...signers, ...cancelSigners[0]];
      instructions = [...cancelInstr[0], ...instructions];
    }
  }

  let receivingSolAccountOrAta = '';
  if (auctionView.auction.info.tokenMint == WRAPPED_SOL_MINT.toBase58()) {
    receivingSolAccountOrAta = ensureWrappedAccount(
      instructions,
      cleanupInstructions,
      tokenAccount,
      wallet.publicKey,
      lamports + accountRentExempt * 2,
      signers,
    );
  } else {
    receivingSolAccountOrAta = await findAta(auctionView, wallet, connection);
  }
  const transferAuthority = approve(
    instructions,
    cleanupInstructions,
    toPublicKey(receivingSolAccountOrAta),
    wallet.publicKey,
    lamports - accountRentExempt,
  );

  signers.push(transferAuthority);

  const bid = new BN(lamports - accountRentExempt);
  await placeBid(
    wallet.publicKey.toBase58(),
    pubkeyToString(receivingSolAccountOrAta),
    bidderPotTokenAccount,
    auctionView.auction.info.tokenMint,
    transferAuthority.publicKey.toBase58(),
    wallet.publicKey.toBase58(),
    auctionView.auctionManager.vault,
    bid,
    instructions,
  );

  overallInstructions.push([...instructions, ...cleanupInstructions.reverse()]);
  overallSigners.push(signers);
  return bid;
}