feat: pair

This commit is contained in:
bartosz-lipinski 2021-03-19 20:54:51 -05:00
parent 4c2eca612e
commit 92fefe2b9f
5 changed files with 143 additions and 120 deletions

View File

@ -1,15 +1,18 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { notification, Spin, Button } from 'antd'; import { notification, Spin, Button } from 'antd';
import { contexts, ConnectButton, programIds, notify } from '@oyster/common'; import { contexts, ConnectButton, programIds, notify, cache, useUserAccounts } from '@oyster/common';
import { Input } from '../Input'; import { Input } from '../Input';
import './style.less'; import './style.less';
import { ASSET_CHAIN, chainToName } from '../../utils/assets'; import { ASSET_CHAIN, chainToName } from '../../utils/assets';
import { import {
bridgeAuthorityKey,
displayBalance,
fromSolana, fromSolana,
ProgressUpdate, ProgressUpdate,
toSolana, toSolana,
TransferRequest, TransferRequest,
wrappedAssetMintKey,
} from '../../models/bridge'; } from '../../models/bridge';
import { useEthereum } from '../../contexts'; import { useEthereum } from '../../contexts';
import { TokenDisplay } from '../TokenDisplay'; import { TokenDisplay } from '../TokenDisplay';
@ -19,6 +22,7 @@ import BN from 'bn.js';
import { useTokenChainPairState } from '../../contexts/chainPair'; import { useTokenChainPairState } from '../../contexts/chainPair';
import { LABELS } from '../../constants'; import { LABELS } from '../../constants';
import { useCorrectNetwork } from '../../hooks/useCorrectNetwork'; import { useCorrectNetwork } from '../../hooks/useCorrectNetwork';
import { BigNumber } from 'ethers/utils';
const { useConnection } = contexts.Connection; const { useConnection } = contexts.Connection;
const { useWallet } = contexts.Wallet; const { useWallet } = contexts.Wallet;
@ -41,8 +45,8 @@ export const typeToIcon = (type: string, isLast: boolean) => {
export const Transfer = () => { export const Transfer = () => {
const connection = useConnection(); const connection = useConnection();
const { wallet, connected } = useWallet(); const { wallet } = useWallet();
const { provider, accounts, tokenMap } = useEthereum(); const { provider, tokenMap } = useEthereum();
const hasCorrespondingNetworks = useCorrectNetwork(); const hasCorrespondingNetworks = useCorrectNetwork();
const { const {
A, A,
@ -53,7 +57,7 @@ export const Transfer = () => {
} = useTokenChainPairState(); } = useTokenChainPairState();
const [request, setRequest] = useState<TransferRequest>({ const [request, setRequest] = useState<TransferRequest>({
from: ASSET_CHAIN.Ethereum, from: ASSET_CHAIN.Ethereum,
toChain: ASSET_CHAIN.Solana, to: ASSET_CHAIN.Solana,
}); });
useEffect(() => { useEffect(() => {
@ -75,114 +79,18 @@ export const Transfer = () => {
amount: A.amount, amount: A.amount,
asset: mintAddress, asset: mintAddress,
from: A.chain, from: A.chain,
toChain: B.chain, to: B.chain,
info: A.info,
}); });
}, [A, B, mintAddress]); }, [A, B, mintAddress]);
const from = A.chain;
const toChain = B.chain;
console.log(from);
useEffect(() => {
const asset = mintAddress;
if (!asset || (asset === request?.info?.address && request.from === from && request.toChain === toChain)) {
return;
}
console.log(from);
(async () => {
if (!provider || !accounts[0]) {
return;
}
try {
const bridgeAddress = programIds().wormhole.bridge;
if (from === ASSET_CHAIN.Ethereum) {
let signer = provider.getSigner();
let e = WrappedAssetFactory.connect(asset, provider);
let addr = await signer.getAddress();
let balance = await e.balanceOf(addr);
let decimals = await e.decimals();
let symbol = await e.symbol();
let allowance = await e.allowance(addr, bridgeAddress);
let info = {
address: asset,
name: symbol,
balance: balance,
balanceAsNumber:
new BN(balance.toString())
.div(new BN(10).pow(new BN(decimals - 2)))
.toNumber() / 100,
allowance: allowance,
decimals: decimals,
isWrapped: false,
chainID: ASSET_CHAIN.Ethereum,
assetAddress: Buffer.from(asset.slice(2), 'hex'),
mint: '',
};
let b = WormholeFactory.connect(bridgeAddress, provider);
let isWrapped = await b.isWrappedAsset(asset);
if (isWrapped) {
info.chainID = await e.assetChain();
info.assetAddress = Buffer.from(
(await e.assetAddress()).slice(2),
'hex',
);
info.isWrapped = true;
}
setRequest({
...request,
from,
toChain,
asset,
info,
});
} else {
console.log('Asset: ', asset);
//debugger;
// get user address from asset
//
// let info = {
// address: fromAddress,
// name: "",
// balance: acc.balance,
// allowance: 0,
// decimals: acc.assetMeta.decimals,
// isWrapped: acc.assetMeta.chain != ChainID.SOLANA,
// chainID: acc.assetMeta.chain,
// assetAddress: acc.assetMeta.address,
// // Solana specific
// mint: acc.mint,
// };
}
} catch (e) {
console.error(e.toString());
notify({
message: `Error getting asset (${asset}) information`,
description: e.toString(),
type: 'error',
});
}
})();
}, [request, mintAddress, from, toChain, provider, accounts, connected]);
return ( return (
<> <>
<div className="exchange-card"> <div className="exchange-card">
<Input <Input
title={`From ${chainToName(request.from)}`} title={`From ${chainToName(request.from)}`}
asset={request.asset} asset={request.asset}
balance={request.info?.balanceAsNumber || 0} balance={displayBalance(A.info)}
setAsset={asset => setAssetInformation(asset)} setAsset={asset => setAssetInformation(asset)}
chain={A.chain} chain={A.chain}
amount={A.amount} amount={A.amount}
@ -213,8 +121,9 @@ export const Transfer = () => {
</Button> </Button>
<Input <Input
title={`To ${chainToName(request.toChain)}`} title={`To ${chainToName(request.to)}`}
asset={request.asset} asset={request.asset}
balance={displayBalance(B.info)}
setAsset={asset => setAssetInformation(asset)} setAsset={asset => setAssetInformation(asset)}
chain={B.chain} chain={B.chain}
amount={B.amount} amount={B.amount}
@ -267,7 +176,7 @@ export const Transfer = () => {
); );
} }
if (request.toChain === ASSET_CHAIN.Solana) { if (request.to === ASSET_CHAIN.Solana) {
await toSolana( await toSolana(
connection, connection,
wallet, wallet,
@ -296,7 +205,7 @@ export const Transfer = () => {
<div style={{ display: 'flex' }}> <div style={{ display: 'flex' }}>
<div> <div>
<h5>{`${chainToName(request.from)} Mainnet -> ${chainToName( <h5>{`${chainToName(request.from)} Mainnet -> ${chainToName(
request.toChain, request.to,
)} Mainnet`}</h5> )} Mainnet`}</h5>
<h2> <h2>
{request.amount?.toString()} {request.info?.name} {request.amount?.toString()} {request.info?.name}
@ -317,7 +226,7 @@ export const Transfer = () => {
<span style={{ margin: 15 }}>{'➔'}</span> <span style={{ margin: 15 }}>{'➔'}</span>
<TokenDisplay <TokenDisplay
asset={request.asset} asset={request.asset}
chain={request.toChain} chain={request.to}
token={token} token={token}
/> />
</div> </div>

View File

@ -7,12 +7,17 @@ import React, {
} from 'react'; } from 'react';
import { useHistory, useLocation } from 'react-router-dom'; import { useHistory, useLocation } from 'react-router-dom';
import bs58 from 'bs58'; import bs58 from 'bs58';
import { useConnection, useConnectionConfig } from '@oyster/common'; import { programIds, useConnection, useConnectionConfig, useUserAccounts } from '@oyster/common';
import { TokenInfo } from '@solana/spl-token-registry'; import { TokenInfo } from '@solana/spl-token-registry';
import { ASSET_CHAIN, filterModalSolTokens } from '../utils/assets'; import { ASSET_CHAIN, filterModalSolTokens } from '../utils/assets';
import { useEthereum } from './ethereum'; import { useEthereum } from './ethereum';
import { BigNumber } from 'ethers/utils';
import { WrappedAssetFactory } from '../contracts/WrappedAssetFactory';
import { WormholeFactory } from '../contracts/WormholeFactory';
import { bridgeAuthorityKey, TransferRequestInfo, wrappedAssetMintKey } from '../models/bridge';
export interface TokenChainContextState { export interface TokenChainContextState {
info?: TransferRequestInfo;
amount: number; amount: number;
setAmount: (val: number) => void; setAmount: (val: number) => void;
chain: ASSET_CHAIN; chain: ASSET_CHAIN;
@ -72,9 +77,106 @@ function getDefaultTokens(tokens: TokenInfo[], search: string) {
}; };
} }
export const useCurrencyLeg = () => { export const useCurrencyLeg = (mintAddress: string) => {
const [amount, setAmount] = useState(0); const [amount, setAmount] = useState(0);
const [chain, setChain] = useState(ASSET_CHAIN.Ethereum); const [chain, setChain] = useState(ASSET_CHAIN.Ethereum);
const [info, setInfo] = useState<TransferRequestInfo>();
const { userAccounts } = useUserAccounts();
const { provider, tokens: ethTokens } = useEthereum();
const { tokens: solTokens } = useConnectionConfig();
const connection = useConnection();
useEffect(() => {
if(!provider || !connection) {
return;
}
(async () => {
const ethToken = ethTokens.find(t => t.address === mintAddress);
const solToken = solTokens.find(t => t.address === mintAddress);
// eth assets on eth chain
// eth asset on sol chain
// sol asset on eth chain
// sol asset on sol chain
let ethAddress: string = '';
if (solToken) {
// let signer = provider.getSigner();
// let e = WrappedAssetFactory.connect(asset, provider);
// let addr = await signer.getAddress();
// let decimals = await e.decimals();
// let symbol = await e.symbol();
// TODO: checked if mint is wrapped mint from eth...
const accounts = userAccounts
.filter(a => a.info.mint.toBase58() === solToken.address)
.sort((a, b) => a.info.amount.toNumber() - b.info.amount.toNumber());
console.log(accounts);
}
if (ethToken) {
let signer = provider.getSigner();
let e = WrappedAssetFactory.connect(mintAddress, provider);
let addr = await signer.getAddress();
let decimals = await e.decimals();
let symbol = await e.symbol();
const ethBridgeAddress = programIds().wormhole.bridge;
let allowance = await e.allowance(addr, ethBridgeAddress);
const assetAddress = Buffer.from(mintAddress.slice(2), 'hex');
let info = {
address: mintAddress,
name: symbol,
balance: new BigNumber(0),
allowance,
decimals,
isWrapped: false,
chainID: ASSET_CHAIN.Ethereum,
assetAddress,
mint: '',
};
let b = WormholeFactory.connect(ethBridgeAddress, provider);
let isWrapped = await b.isWrappedAsset(mintAddress);
if (isWrapped) {
info.chainID = await e.assetChain();
info.assetAddress = Buffer.from(
(addr).slice(2),
'hex',
);
info.isWrapped = true;
}
if(chain === ASSET_CHAIN.Ethereum) {
info.balance = await e.balanceOf(addr);
} else {
// TODO: get balance on other chains for assets that came from eth
const bridgeId = programIds().wormhole.pubkey;
const bridgeAuthority = await bridgeAuthorityKey(bridgeId);
wrappedAssetMintKey(bridgeId, bridgeAuthority, {
decimals: Math.min(9, info.decimals),
address: info.assetAddress,
chain: info.chainID
})
}
setInfo(info);
}
})();
}, [connection, provider, setInfo, chain, mintAddress, ethTokens, solTokens, userAccounts])
return useMemo( return useMemo(
() => ({ () => ({
@ -82,6 +184,7 @@ export const useCurrencyLeg = () => {
setAmount: setAmount, setAmount: setAmount,
chain: chain, chain: chain,
setChain: setChain, setChain: setChain,
info
}), }),
[amount, setAmount, chain, setChain], [amount, setAmount, chain, setChain],
); );
@ -96,16 +199,15 @@ export function TokenChainPairProvider({ children = null as any }) {
const [lastTypedAccount, setLastTypedAccount] = useState(0); const [lastTypedAccount, setLastTypedAccount] = useState(0);
const [mintAddress, setMintAddress] = useState(''); const [mintAddress, setMintAddress] = useState('');
const base = useCurrencyLeg(); const base = useCurrencyLeg(mintAddress);
const amountA = base.amount; const amountA = base.amount;
const setAmountA = base.setAmount; const setAmountA = base.setAmount;
const chainA = base.chain; const chainA = base.chain;
const setChainA = base.setChain; const setChainA = base.setChain;
const quote = useCurrencyLeg(); const quote = useCurrencyLeg(mintAddress);
const amountB = quote.amount; const amountB = quote.amount;
const setAmountB = quote.setAmount; const setAmountB = quote.setAmount;
const chainB = quote.chain;
const setChainB = quote.setChain; const setChainB = quote.setChain;
const tokens = useMemo( const tokens = useMemo(

View File

@ -31,7 +31,7 @@ export const fromSolana = async (
!request.asset || !request.asset ||
!request.amount || !request.amount ||
!request.recipient || !request.recipient ||
!request.toChain || !request.to ||
!request.info !request.info
) { ) {
return; return;
@ -69,7 +69,7 @@ export const fromSolana = async (
if ( if (
!request.amount || !request.amount ||
!request.recipient || !request.recipient ||
!request.toChain || !request.to ||
!request.info || !request.info ||
!wallet.publicKey !wallet.publicKey
) { ) {
@ -90,7 +90,7 @@ export const fromSolana = async (
new PublicKey(request.info.address), new PublicKey(request.info.address),
new PublicKey(request.info.mint), new PublicKey(request.info.mint),
new BN(request.amount.toString()), new BN(request.amount.toString()),
request.toChain, request.to,
request.recipient, request.recipient,
{ {
chain: request.info.chainID, chain: request.info.chainID,

View File

@ -1,3 +1,4 @@
import BN from 'bn.js';
import { ethers } from 'ethers'; import { ethers } from 'ethers';
import { BigNumber } from 'ethers/utils'; import { BigNumber } from 'ethers/utils';
import { ASSET_CHAIN } from '../constants'; import { ASSET_CHAIN } from '../constants';
@ -14,7 +15,6 @@ export interface TransferRequestInfo {
address: string; address: string;
name: string; name: string;
balance: BigNumber; balance: BigNumber;
balanceAsNumber: number;
decimals: number; decimals: number;
allowance: BigNumber; allowance: BigNumber;
isWrapped: boolean; isWrapped: boolean;
@ -31,6 +31,18 @@ export interface TransferRequest {
from?: ASSET_CHAIN; from?: ASSET_CHAIN;
asset?: string; asset?: string;
toChain?: ASSET_CHAIN; to?: ASSET_CHAIN;
recipient?: Buffer; recipient?: Buffer;
} }
export const displayBalance = (info?: TransferRequestInfo) => {
try {
return (
new BN(info?.balance?.toString() || 0)
.div(new BN(10).pow(new BN(Math.min((info?.decimals || 0) - 2, 0))))
.toNumber() / 100
);
} catch {
return 0;
}
};

View File

@ -216,7 +216,7 @@ export const toSolana = async (
!amountBN || !amountBN ||
!request.asset || !request.asset ||
!request.recipient || !request.recipient ||
!request.toChain || !request.to ||
!request.info !request.info
) { ) {
return; return;
@ -236,7 +236,7 @@ export const toSolana = async (
request.asset, request.asset,
amountBN, amountBN,
request.recipient, request.recipient,
request.toChain, request.to,
nonce, nonce,
false, false,
); );