diff --git a/packages/bridge/src/components/Input/solana.tsx b/packages/bridge/src/components/Input/solana.tsx index c6193a6..c90ae04 100644 --- a/packages/bridge/src/components/Input/solana.tsx +++ b/packages/bridge/src/components/Input/solana.tsx @@ -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(''); - 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} ) : ( { '/faucet': '7', }; + const isRoot = location.pathname !== '/'; + const current = [...Object.keys(paths)].find(key => location.pathname.startsWith(key)) || ''; return ( -
- setWormholeReady(true)}> +
+ setWormholeReady(true)} show={isRoot}> - {location.pathname !== '/' && ( + {isRoot && (

WORMHOLE

diff --git a/packages/bridge/src/components/Wormhole/Utils.ts b/packages/bridge/src/components/Wormhole/Utils.ts index 861cfcd..40e5fbc 100644 --- a/packages/bridge/src/components/Wormhole/Utils.ts +++ b/packages/bridge/src/components/Wormhole/Utils.ts @@ -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 diff --git a/packages/bridge/src/components/Wormhole/WormholeGeometry.tsx b/packages/bridge/src/components/Wormhole/WormholeGeometry.tsx index 67d55a5..a8cc42d 100644 --- a/packages/bridge/src/components/Wormhole/WormholeGeometry.tsx +++ b/packages/bridge/src/components/Wormhole/WormholeGeometry.tsx @@ -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; } }); diff --git a/packages/bridge/src/components/Wormhole/index.tsx b/packages/bridge/src/components/Wormhole/index.tsx index 09e5727..3fe294d 100644 --- a/packages/bridge/src/components/Wormhole/index.tsx +++ b/packages/bridge/src/components/Wormhole/index.tsx @@ -12,10 +12,13 @@ import './wormhole.less'; const Wormhole = ({ onCreated, children, + show, }: { onCreated: any; + show: boolean; children: React.ReactNode; }) => ( + show ? <>{children} : <>
{children}
diff --git a/packages/bridge/src/contexts/ethereum.tsx b/packages/bridge/src/contexts/ethereum.tsx new file mode 100644 index 0000000..ee3bf49 --- /dev/null +++ b/packages/bridge/src/contexts/ethereum.tsx @@ -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; + web3?: Web3 +} + +export const EthereumContext = createContext({ + connect: async () => { }, +}); + +export const EthereumProvider: FunctionComponent = ({children}) => { + const [web3, setWeb3] = useState(); + 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 ( + + {children} + + ); +} + +export const useEthereum = () => { + const context = useContext(EthereumContext); + return context; +} diff --git a/packages/bridge/src/contexts/index.tsx b/packages/bridge/src/contexts/index.tsx new file mode 100644 index 0000000..14939b3 --- /dev/null +++ b/packages/bridge/src/contexts/index.tsx @@ -0,0 +1,3 @@ +export * from "./market"; +export * from "./tokenPair"; +export * from "./ethereum"; diff --git a/packages/bridge/src/contexts/tokenPair.tsx b/packages/bridge/src/contexts/tokenPair.tsx index 5422fb0..1623899 100644 --- a/packages/bridge/src/contexts/tokenPair.tsx +++ b/packages/bridge/src/contexts/tokenPair.tsx @@ -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(); diff --git a/packages/bridge/src/routes.tsx b/packages/bridge/src/routes.tsx index 1e382fa..ab50807 100644 --- a/packages/bridge/src/routes.tsx +++ b/packages/bridge/src/routes.tsx @@ -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() { - - - - - } /> - } /> - } /> - - - - + + + + + + + } /> + } /> + } /> + + + + + + diff --git a/packages/bridge/src/views/transfer/index.tsx b/packages/bridge/src/views/transfer/index.tsx index e8f4cf6..449e67d 100644 --- a/packages/bridge/src/views/transfer/index.tsx +++ b/packages/bridge/src/views/transfer/index.tsx @@ -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"; diff --git a/packages/common/src/contexts/connection.tsx b/packages/common/src/contexts/connection.tsx index d951575..cfa249d 100644 --- a/packages/common/src/contexts/connection.tsx +++ b/packages/common/src/contexts/connection.tsx @@ -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>(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; diff --git a/packages/common/src/contracts/token.ts b/packages/common/src/contracts/token.ts new file mode 100644 index 0000000..7463f85 --- /dev/null +++ b/packages/common/src/contracts/token.ts @@ -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 }; +}; diff --git a/packages/common/src/utils/utils.ts b/packages/common/src/utils/utils.ts index 0bbb792..fc2eb61 100644 --- a/packages/common/src/utils/utils.ts +++ b/packages/common/src/utils/utils.ts @@ -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; @@ -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(array: T[], size: number): T[][] { - return Array.apply(0, new Array(Math.ceil(array.length / size))).map((_, index) => - array.slice(index * size, (index + 1) * size) - ); + return Array.apply( + 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;