mirror of https://github.com/certusone/oyster.git
feat: bridge
This commit is contained in:
parent
2c04bb5c46
commit
e5750fb129
|
@ -8,8 +8,10 @@ const { useConnectionConfig } = contexts.Connection;
|
|||
|
||||
const { Option } = Select;
|
||||
|
||||
// User can choose a collateral they want to use, and then this will display the balance they have in Oyster's lending
|
||||
// reserve for that collateral type.
|
||||
// TODO: add way to add new token account
|
||||
|
||||
|
||||
|
||||
export function SolanaInput(props: {
|
||||
title: string;
|
||||
amount?: number | null;
|
||||
|
@ -27,7 +29,7 @@ export function SolanaInput(props: {
|
|||
const [lastAmount, setLastAmount] = useState<string>('');
|
||||
|
||||
|
||||
const renderReserveAccounts = [].map((reserve: any) => {
|
||||
const renderTokens = [].map((reserve: any) => {
|
||||
const mint = reserve.info.liquidityMint.toBase58();
|
||||
const address = reserve.pubkey.toBase58();
|
||||
const name = getTokenName(tokenMap, mint);
|
||||
|
@ -97,7 +99,7 @@ export function SolanaInput(props: {
|
|||
option?.name?.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}
|
||||
>
|
||||
{renderReserveAccounts}
|
||||
{renderTokens}
|
||||
</Select>
|
||||
) : (
|
||||
<TokenDisplay
|
||||
|
|
|
@ -19,14 +19,16 @@ export const AppLayout = React.memo((props: any) => {
|
|||
'/faucet': '7',
|
||||
};
|
||||
|
||||
const isRoot = location.pathname !== '/';
|
||||
|
||||
const current =
|
||||
[...Object.keys(paths)].find(key => location.pathname.startsWith(key)) ||
|
||||
'';
|
||||
return (
|
||||
<div className={`App${wormholeReady ? `` : ` wormhole-bg`}`}>
|
||||
<Wormhole onCreated={() => setWormholeReady(true)}>
|
||||
<div className={`App`}>
|
||||
<Wormhole onCreated={() => setWormholeReady(true)} show={isRoot}>
|
||||
<Layout title={LABELS.APP_TITLE}>
|
||||
{location.pathname !== '/' && (
|
||||
{isRoot && (
|
||||
<Header className="App-Bar">
|
||||
<div className="app-title">
|
||||
<h2>WORMHOLE</h2>
|
||||
|
|
|
@ -21,7 +21,7 @@ export const hasWindow =
|
|||
* @returns {[]}
|
||||
*/
|
||||
export const calculateTorusProperties = (particleSize: number): any => {
|
||||
let bufferGeometry: BufferGeometry = new TorusGeometry(60, 45, 160, 160);
|
||||
let bufferGeometry: BufferGeometry = new TorusGeometry(60, 45, 60, 80);
|
||||
|
||||
// if normal and uv attributes are not removed,
|
||||
// mergeVertices() can't consolidate identical vertices
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
import disc from "./disc.png";
|
||||
|
||||
// The individual "particle size".
|
||||
const PARTICLE_SIZE = 15;
|
||||
const PARTICLE_SIZE = 10;
|
||||
|
||||
/**
|
||||
* Three JS Point Geometry calculating points around a Torus.
|
||||
|
@ -28,7 +28,7 @@ const WormholeGeometry = () => {
|
|||
const uniforms = React.useMemo(
|
||||
() => ({
|
||||
// Adapt the color of the WormholeCanvas here.
|
||||
color: { value: new Color("gray") },
|
||||
color: { value: new Color("dimgrey") },
|
||||
pointTexture: {
|
||||
value: pointTexture,
|
||||
},
|
||||
|
@ -47,7 +47,7 @@ const WormholeGeometry = () => {
|
|||
if (mesh.current) {
|
||||
// x-Axis defines the "top" we're looking at, try e.g. 30.5
|
||||
mesh.current.rotation.x = 30;
|
||||
mesh.current.rotation.z += 0.001;
|
||||
mesh.current.rotation.z += 0.0005;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -12,10 +12,13 @@ import './wormhole.less';
|
|||
const Wormhole = ({
|
||||
onCreated,
|
||||
children,
|
||||
show,
|
||||
}: {
|
||||
onCreated: any;
|
||||
show: boolean;
|
||||
children: React.ReactNode;
|
||||
}) => (
|
||||
show ? <>{children}</> :
|
||||
<>
|
||||
<WormholeCanvas onCreated={onCreated} />
|
||||
<div className="wormhole-overlay">{children}</div>
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
import React, { createContext, FunctionComponent, useCallback, useContext, useEffect, useState } from "react";
|
||||
import Web3 from "web3";
|
||||
import Web3Modal from "web3modal";
|
||||
// @ts-ignore
|
||||
import WalletConnectProvider from "@walletconnect/web3-provider";
|
||||
// @ts-ignore
|
||||
import Fortmatic from "fortmatic";
|
||||
import Torus from "@toruslabs/torus-embed";
|
||||
import Authereum from "authereum";
|
||||
import { Bitski } from "bitski";
|
||||
import { useWallet } from "@oyster/common";
|
||||
|
||||
const providerOptions = {
|
||||
walletconnect: {
|
||||
package: WalletConnectProvider,
|
||||
options: {
|
||||
infuraId: process.env.REACT_APP_INFURA_ID
|
||||
}
|
||||
},
|
||||
torus: {
|
||||
package: Torus
|
||||
},
|
||||
fortmatic: {
|
||||
package: Fortmatic,
|
||||
options: {
|
||||
key: process.env.REACT_APP_FORTMATIC_KEY
|
||||
}
|
||||
},
|
||||
authereum: {
|
||||
package: Authereum
|
||||
},
|
||||
bitski: {
|
||||
package: Bitski,
|
||||
options: {
|
||||
clientId: process.env.REACT_APP_BITSKI_CLIENT_ID,
|
||||
callbackUrl: window.location.href + "bitski-callback.html"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export interface EthereumContextState {
|
||||
connect: () => Promise<void>;
|
||||
web3?: Web3
|
||||
}
|
||||
|
||||
export const EthereumContext = createContext<EthereumContextState>({
|
||||
connect: async () => { },
|
||||
});
|
||||
|
||||
export const EthereumProvider: FunctionComponent = ({children}) => {
|
||||
const [web3, setWeb3] = useState<Web3>();
|
||||
const { connected } = useWallet();
|
||||
|
||||
const connect = useCallback(async () => {
|
||||
const web3Modal = new Web3Modal({
|
||||
theme: "dark",
|
||||
network: "mainnet", // optional (TODO: add network selector)
|
||||
cacheProvider: false, // optional
|
||||
providerOptions // required
|
||||
});
|
||||
|
||||
const provider = await web3Modal.connect();
|
||||
|
||||
provider.on('error', (e: any) => console.error('WS Error', e));
|
||||
provider.on('end', (e: any) => console.error('WS End', e));
|
||||
|
||||
provider.on('disconnect', (error: { code: number; message: string }) => {
|
||||
console.log(error);
|
||||
});
|
||||
provider.on('connect', (info: { chainId: number }) => {
|
||||
console.log(info);
|
||||
});
|
||||
|
||||
const instance = new Web3(provider);
|
||||
setWeb3(instance);
|
||||
}, [setWeb3]);
|
||||
|
||||
useEffect(() => {
|
||||
if(connected) {
|
||||
connect();
|
||||
}
|
||||
}, [connect, connected])
|
||||
|
||||
return (
|
||||
<EthereumContext.Provider value={{ web3, connect }}>
|
||||
{children}
|
||||
</EthereumContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export const useEthereum = () => {
|
||||
const context = useContext(EthereumContext);
|
||||
return context;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export * from "./market";
|
||||
export * from "./tokenPair";
|
||||
export * from "./ethereum";
|
|
@ -73,7 +73,7 @@ export const useCurrencyLeg = (defaultMint?: string) => {
|
|||
);
|
||||
};
|
||||
|
||||
export function CurrencyPairProvider({ children = null as any }) {
|
||||
export function TokenPairProvider({ children = null as any }) {
|
||||
const connection = useConnection();
|
||||
const { tokens } = useConnectionConfig();
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { HashRouter, Route, Switch } from 'react-router-dom';
|
||||
import React from 'react';
|
||||
import { contexts } from '@oyster/common';
|
||||
import { MarketProvider } from './contexts/market';
|
||||
import { MarketProvider, TokenPairProvider, EthereumProvider } from './contexts';
|
||||
import { AppLayout } from './components/Layout';
|
||||
|
||||
import {
|
||||
|
@ -19,17 +19,21 @@ export function Routes() {
|
|||
<HashRouter basename={'/'}>
|
||||
<ConnectionProvider>
|
||||
<WalletProvider>
|
||||
<AccountsProvider>
|
||||
<MarketProvider>
|
||||
<AppLayout>
|
||||
<Switch>
|
||||
<Route exact path="/" component={() => <HomeView />} />
|
||||
<Route path="/move" children={<TransferView />} />
|
||||
<Route exact path="/faucet" children={<FaucetView />} />
|
||||
</Switch>
|
||||
</AppLayout>
|
||||
</MarketProvider>
|
||||
</AccountsProvider>
|
||||
<EthereumProvider>
|
||||
<AccountsProvider>
|
||||
<MarketProvider>
|
||||
<TokenPairProvider>
|
||||
<AppLayout>
|
||||
<Switch>
|
||||
<Route exact path="/" component={() => <HomeView />} />
|
||||
<Route path="/move" children={<TransferView />} />
|
||||
<Route exact path="/faucet" children={<FaucetView />} />
|
||||
</Switch>
|
||||
</AppLayout>
|
||||
</TokenPairProvider>
|
||||
</MarketProvider>
|
||||
</AccountsProvider>
|
||||
</EthereumProvider>
|
||||
</WalletProvider>
|
||||
</ConnectionProvider>
|
||||
</HashRouter>
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { Card } from 'antd';
|
||||
import { LAMPORTS_PER_SOL } from '@solana/web3.js';
|
||||
import { LABELS } from '../../constants';
|
||||
import { contexts, utils, ConnectButton } from '@oyster/common';
|
||||
import { contexts, utils, ConnectButton, useConnection, useWallet } from '@oyster/common';
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import { Transfer } from '../../components/Transfer';
|
||||
const { useConnection } = contexts.Connection;
|
||||
const { useWallet } = contexts.Wallet;
|
||||
const { notify } = utils;
|
||||
import { useEthereum } from '../../contexts';
|
||||
|
||||
export const TransferView = () => {
|
||||
const connection = useConnection();
|
||||
const { wallet } = useWallet();
|
||||
const { wallet, connected } = useWallet();
|
||||
const { connect: connectEth } = useEthereum();
|
||||
|
||||
const tabStyle: React.CSSProperties = { width: 120 };
|
||||
const tabList = [
|
||||
|
@ -31,6 +30,12 @@ export const TransferView = () => {
|
|||
},
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
// connectEth();
|
||||
|
||||
}, [connected, connectEth])
|
||||
|
||||
const location = useLocation();
|
||||
const history = useHistory();
|
||||
const activeTab = location.pathname.indexOf("sol") >= 0 ? "sol" : "eth";
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { KnownToken, useLocalStorageState } from '../utils/utils';
|
||||
import { useLocalStorageState } from '../utils/utils';
|
||||
import { Account, clusterApiUrl, Connection, Transaction, TransactionInstruction } from '@solana/web3.js';
|
||||
import React, { useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { notify } from '../utils/notifications';
|
||||
import { ExplorerLink } from '../components/ExplorerLink';
|
||||
import LocalTokens from '../config/tokens.json';
|
||||
import { setProgramIds } from '../utils/ids';
|
||||
import { KnownToken, TokenListProvider } from '@solana/spl-token-registry';
|
||||
|
||||
export type ENV = 'mainnet-beta' | 'testnet' | 'devnet' | 'localnet' | 'lending';
|
||||
|
||||
|
@ -67,13 +68,11 @@ export function ConnectionProvider({ children = undefined as any }) {
|
|||
const [tokenMap, setTokenMap] = useState<Map<string, KnownToken>>(new Map());
|
||||
useEffect(() => {
|
||||
// fetch token files
|
||||
window
|
||||
.fetch(`https://raw.githubusercontent.com/solana-labs/token-list/main/src/tokens/${env}.json`)
|
||||
.then((res) => {
|
||||
return res.json();
|
||||
})
|
||||
.catch((err) => [])
|
||||
.then((list: KnownToken[]) => {
|
||||
|
||||
|
||||
|
||||
new TokenListProvider().resolve(env)
|
||||
.then((list) => {
|
||||
const knownMints = [...LocalTokens, ...list].reduce((map, item) => {
|
||||
map.set(item.mintAddress, item);
|
||||
return map;
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
import { MintLayout, AccountLayout, Token } from '@solana/spl-token';
|
||||
import {
|
||||
Connection,
|
||||
PublicKey,
|
||||
Transaction,
|
||||
Account,
|
||||
SystemProgram,
|
||||
} from '@solana/web3.js';
|
||||
|
||||
export const mintNFT = async (
|
||||
connection: Connection,
|
||||
wallet: {
|
||||
publicKey: PublicKey;
|
||||
signTransaction: (tx: Transaction) => Transaction;
|
||||
},
|
||||
// SOL account
|
||||
owner: PublicKey,
|
||||
) => {
|
||||
const TOKEN_PROGRAM_ID = new PublicKey(
|
||||
'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
|
||||
);
|
||||
const SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID = new PublicKey(
|
||||
'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL',
|
||||
);
|
||||
const mintAccount = new Account();
|
||||
const tokenAccount = new Account();
|
||||
|
||||
// Allocate memory for the account
|
||||
const mintRent = await connection.getMinimumBalanceForRentExemption(
|
||||
MintLayout.span,
|
||||
);
|
||||
|
||||
const accountRent = await connection.getMinimumBalanceForRentExemption(
|
||||
MintLayout.span,
|
||||
);
|
||||
|
||||
let transaction = new Transaction();
|
||||
const signers = [mintAccount, tokenAccount];
|
||||
transaction.recentBlockhash = (
|
||||
await connection.getRecentBlockhash('max')
|
||||
).blockhash;
|
||||
|
||||
transaction.add(
|
||||
SystemProgram.createAccount({
|
||||
fromPubkey: wallet.publicKey,
|
||||
newAccountPubkey: mintAccount.publicKey,
|
||||
lamports: mintRent,
|
||||
space: MintLayout.span,
|
||||
programId: TOKEN_PROGRAM_ID,
|
||||
}),
|
||||
);
|
||||
|
||||
transaction.add(
|
||||
SystemProgram.createAccount({
|
||||
fromPubkey: wallet.publicKey,
|
||||
newAccountPubkey: tokenAccount.publicKey,
|
||||
lamports: accountRent,
|
||||
space: AccountLayout.span,
|
||||
programId: TOKEN_PROGRAM_ID,
|
||||
}),
|
||||
);
|
||||
|
||||
transaction.add(
|
||||
Token.createInitMintInstruction(
|
||||
TOKEN_PROGRAM_ID,
|
||||
mintAccount.publicKey,
|
||||
0,
|
||||
wallet.publicKey,
|
||||
wallet.publicKey,
|
||||
),
|
||||
);
|
||||
transaction.add(
|
||||
Token.createInitAccountInstruction(
|
||||
TOKEN_PROGRAM_ID,
|
||||
mintAccount.publicKey,
|
||||
tokenAccount.publicKey,
|
||||
owner,
|
||||
),
|
||||
);
|
||||
transaction.add(
|
||||
Token.createMintToInstruction(
|
||||
TOKEN_PROGRAM_ID,
|
||||
mintAccount.publicKey,
|
||||
tokenAccount.publicKey,
|
||||
wallet.publicKey,
|
||||
[],
|
||||
1,
|
||||
),
|
||||
);
|
||||
transaction.add(
|
||||
Token.createSetAuthorityInstruction(
|
||||
TOKEN_PROGRAM_ID,
|
||||
mintAccount.publicKey,
|
||||
null,
|
||||
wallet.publicKey,
|
||||
[],
|
||||
),
|
||||
);
|
||||
|
||||
transaction.setSigners(wallet.publicKey, ...signers.map(s => s.publicKey));
|
||||
if (signers.length > 0) {
|
||||
transaction.partialSign(...signers);
|
||||
}
|
||||
transaction = await wallet.signTransaction(transaction);
|
||||
const rawTransaction = transaction.serialize();
|
||||
let options = {
|
||||
skipPreflight: true,
|
||||
commitment: 'singleGossip',
|
||||
};
|
||||
|
||||
const txid = await connection.sendRawTransaction(rawTransaction, options);
|
||||
|
||||
return { txid, mint: mintAccount.publicKey, account: tokenAccount.publicKey };
|
||||
};
|
|
@ -5,13 +5,7 @@ import { TokenAccount } from './../models';
|
|||
import { PublicKey } from '@solana/web3.js';
|
||||
import BN from 'bn.js';
|
||||
import { WAD, ZERO } from '../constants';
|
||||
|
||||
export interface KnownToken {
|
||||
tokenSymbol: string;
|
||||
tokenName: string;
|
||||
icon: string;
|
||||
mintAddress: string;
|
||||
}
|
||||
import { KnownToken } from '@solana/spl-token-registry';
|
||||
|
||||
export type KnownTokenMap = Map<string, KnownToken>;
|
||||
|
||||
|
@ -32,7 +26,7 @@ export function useLocalStorageState(key: string, defaultState?: string) {
|
|||
});
|
||||
|
||||
const setLocalStorageState = useCallback(
|
||||
(newState) => {
|
||||
newState => {
|
||||
const changed = state !== newState;
|
||||
if (!changed) {
|
||||
return;
|
||||
|
@ -44,7 +38,7 @@ export function useLocalStorageState(key: string, defaultState?: string) {
|
|||
localStorage.setItem(key, JSON.stringify(newState));
|
||||
}
|
||||
},
|
||||
[state, key]
|
||||
[state, key],
|
||||
);
|
||||
|
||||
return [state, setLocalStorageState];
|
||||
|
@ -55,7 +49,11 @@ export function shortenAddress(address: string, chars = 4): string {
|
|||
return `${address.slice(0, chars)}...${address.slice(-chars)}`;
|
||||
}
|
||||
|
||||
export function getTokenName(map: KnownTokenMap, mint?: string | PublicKey, shorten = true): string {
|
||||
export function getTokenName(
|
||||
map: KnownTokenMap,
|
||||
mint?: string | PublicKey,
|
||||
shorten = true,
|
||||
): string {
|
||||
const mintAddress = typeof mint === 'string' ? mint : mint?.toBase58();
|
||||
|
||||
if (!mintAddress) {
|
||||
|
@ -81,8 +79,12 @@ export function getTokenByName(tokenMap: KnownTokenMap, name: string) {
|
|||
return token;
|
||||
}
|
||||
|
||||
export function getTokenIcon(map: KnownTokenMap, mintAddress?: string | PublicKey): string | undefined {
|
||||
const address = typeof mintAddress === 'string' ? mintAddress : mintAddress?.toBase58();
|
||||
export function getTokenIcon(
|
||||
map: KnownTokenMap,
|
||||
mintAddress?: string | PublicKey,
|
||||
): string | undefined {
|
||||
const address =
|
||||
typeof mintAddress === 'string' ? mintAddress : mintAddress?.toBase58();
|
||||
if (!address) {
|
||||
return;
|
||||
}
|
||||
|
@ -97,17 +99,22 @@ export function isKnownMint(map: KnownTokenMap, mintAddress: string) {
|
|||
export const STABLE_COINS = new Set(['USDC', 'wUSDC', 'USDT']);
|
||||
|
||||
export function chunks<T>(array: T[], size: number): T[][] {
|
||||
return Array.apply<number, T[], T[][]>(0, new Array(Math.ceil(array.length / size))).map((_, index) =>
|
||||
array.slice(index * size, (index + 1) * size)
|
||||
);
|
||||
return Array.apply<number, T[], T[][]>(
|
||||
0,
|
||||
new Array(Math.ceil(array.length / size)),
|
||||
).map((_, index) => array.slice(index * size, (index + 1) * size));
|
||||
}
|
||||
|
||||
export function toLamports(account?: TokenAccount | number, mint?: MintInfo): number {
|
||||
export function toLamports(
|
||||
account?: TokenAccount | number,
|
||||
mint?: MintInfo,
|
||||
): number {
|
||||
if (!account) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const amount = typeof account === 'number' ? account : account.info.amount?.toNumber();
|
||||
const amount =
|
||||
typeof account === 'number' ? account : account.info.amount?.toNumber();
|
||||
|
||||
const precision = Math.pow(10, mint?.decimals || 0);
|
||||
return Math.floor(amount * precision);
|
||||
|
@ -117,13 +124,21 @@ export function wadToLamports(amount?: BN): BN {
|
|||
return amount?.div(WAD) || ZERO;
|
||||
}
|
||||
|
||||
export function fromLamports(account?: TokenAccount | number | BN, mint?: MintInfo, rate: number = 1.0): number {
|
||||
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()
|
||||
typeof account === 'number'
|
||||
? account
|
||||
: BN.isBN(account)
|
||||
? account.toNumber()
|
||||
: account.info.amount.toNumber(),
|
||||
);
|
||||
|
||||
const precision = Math.pow(10, mint?.decimals || 0);
|
||||
|
@ -144,8 +159,11 @@ const abbreviateNumber = (number: number, precision: number) => {
|
|||
return scaled.toFixed(precision) + suffix;
|
||||
};
|
||||
|
||||
export const formatAmount = (val: number, precision: number = 6, abbr: boolean = true) =>
|
||||
abbr ? abbreviateNumber(val, precision) : val.toFixed(precision);
|
||||
export const formatAmount = (
|
||||
val: number,
|
||||
precision: number = 6,
|
||||
abbr: boolean = true,
|
||||
) => (abbr ? abbreviateNumber(val, precision) : val.toFixed(precision));
|
||||
|
||||
export function formatTokenAmount(
|
||||
account?: TokenAccount,
|
||||
|
@ -154,13 +172,17 @@ export function formatTokenAmount(
|
|||
prefix = '',
|
||||
suffix = '',
|
||||
precision = 6,
|
||||
abbr = false
|
||||
abbr = false,
|
||||
): string {
|
||||
if (!account) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return `${[prefix]}${formatAmount(fromLamports(account, mint, rate), precision, abbr)}${suffix}`;
|
||||
return `${[prefix]}${formatAmount(
|
||||
fromLamports(account, mint, rate),
|
||||
precision,
|
||||
abbr,
|
||||
)}${suffix}`;
|
||||
}
|
||||
|
||||
export const formatUSD = new Intl.NumberFormat('en-US', {
|
||||
|
@ -190,12 +212,17 @@ export const formatPct = new Intl.NumberFormat('en-US', {
|
|||
maximumFractionDigits: 2,
|
||||
});
|
||||
|
||||
export function convert(account?: TokenAccount | number, mint?: MintInfo, rate: number = 1.0): number {
|
||||
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 amount =
|
||||
typeof account === 'number' ? account : account.info.amount?.toNumber();
|
||||
|
||||
const precision = Math.pow(10, mint?.decimals || 0);
|
||||
let result = (amount / precision) * rate;
|
||||
|
|
Loading…
Reference in New Issue