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 { 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 './style.less';
import { ASSET_CHAIN, chainToName } from '../../utils/assets';
import {
bridgeAuthorityKey,
displayBalance,
fromSolana,
ProgressUpdate,
toSolana,
TransferRequest,
wrappedAssetMintKey,
} from '../../models/bridge';
import { useEthereum } from '../../contexts';
import { TokenDisplay } from '../TokenDisplay';
@ -19,6 +22,7 @@ import BN from 'bn.js';
import { useTokenChainPairState } from '../../contexts/chainPair';
import { LABELS } from '../../constants';
import { useCorrectNetwork } from '../../hooks/useCorrectNetwork';
import { BigNumber } from 'ethers/utils';
const { useConnection } = contexts.Connection;
const { useWallet } = contexts.Wallet;
@ -41,8 +45,8 @@ export const typeToIcon = (type: string, isLast: boolean) => {
export const Transfer = () => {
const connection = useConnection();
const { wallet, connected } = useWallet();
const { provider, accounts, tokenMap } = useEthereum();
const { wallet } = useWallet();
const { provider, tokenMap } = useEthereum();
const hasCorrespondingNetworks = useCorrectNetwork();
const {
A,
@ -53,7 +57,7 @@ export const Transfer = () => {
} = useTokenChainPairState();
const [request, setRequest] = useState<TransferRequest>({
from: ASSET_CHAIN.Ethereum,
toChain: ASSET_CHAIN.Solana,
to: ASSET_CHAIN.Solana,
});
useEffect(() => {
@ -75,114 +79,18 @@ export const Transfer = () => {
amount: A.amount,
asset: mintAddress,
from: A.chain,
toChain: B.chain,
to: B.chain,
info: A.info,
});
}, [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 (
<>
<div className="exchange-card">
<Input
title={`From ${chainToName(request.from)}`}
asset={request.asset}
balance={request.info?.balanceAsNumber || 0}
balance={displayBalance(A.info)}
setAsset={asset => setAssetInformation(asset)}
chain={A.chain}
amount={A.amount}
@ -213,8 +121,9 @@ export const Transfer = () => {
</Button>
<Input
title={`To ${chainToName(request.toChain)}`}
title={`To ${chainToName(request.to)}`}
asset={request.asset}
balance={displayBalance(B.info)}
setAsset={asset => setAssetInformation(asset)}
chain={B.chain}
amount={B.amount}
@ -267,7 +176,7 @@ export const Transfer = () => {
);
}
if (request.toChain === ASSET_CHAIN.Solana) {
if (request.to === ASSET_CHAIN.Solana) {
await toSolana(
connection,
wallet,
@ -296,7 +205,7 @@ export const Transfer = () => {
<div style={{ display: 'flex' }}>
<div>
<h5>{`${chainToName(request.from)} Mainnet -> ${chainToName(
request.toChain,
request.to,
)} Mainnet`}</h5>
<h2>
{request.amount?.toString()} {request.info?.name}
@ -317,7 +226,7 @@ export const Transfer = () => {
<span style={{ margin: 15 }}>{'➔'}</span>
<TokenDisplay
asset={request.asset}
chain={request.toChain}
chain={request.to}
token={token}
/>
</div>

View File

@ -7,12 +7,17 @@ import React, {
} from 'react';
import { useHistory, useLocation } from 'react-router-dom';
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 { ASSET_CHAIN, filterModalSolTokens } from '../utils/assets';
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 {
info?: TransferRequestInfo;
amount: number;
setAmount: (val: number) => void;
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 [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(
() => ({
@ -82,6 +184,7 @@ export const useCurrencyLeg = () => {
setAmount: setAmount,
chain: chain,
setChain: setChain,
info
}),
[amount, setAmount, chain, setChain],
);
@ -96,16 +199,15 @@ export function TokenChainPairProvider({ children = null as any }) {
const [lastTypedAccount, setLastTypedAccount] = useState(0);
const [mintAddress, setMintAddress] = useState('');
const base = useCurrencyLeg();
const base = useCurrencyLeg(mintAddress);
const amountA = base.amount;
const setAmountA = base.setAmount;
const chainA = base.chain;
const setChainA = base.setChain;
const quote = useCurrencyLeg();
const quote = useCurrencyLeg(mintAddress);
const amountB = quote.amount;
const setAmountB = quote.setAmount;
const chainB = quote.chain;
const setChainB = quote.setChain;
const tokens = useMemo(

View File

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

View File

@ -1,3 +1,4 @@
import BN from 'bn.js';
import { ethers } from 'ethers';
import { BigNumber } from 'ethers/utils';
import { ASSET_CHAIN } from '../constants';
@ -14,7 +15,6 @@ export interface TransferRequestInfo {
address: string;
name: string;
balance: BigNumber;
balanceAsNumber: number;
decimals: number;
allowance: BigNumber;
isWrapped: boolean;
@ -31,6 +31,18 @@ export interface TransferRequest {
from?: ASSET_CHAIN;
asset?: string;
toChain?: ASSET_CHAIN;
to?: ASSET_CHAIN;
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 ||
!request.asset ||
!request.recipient ||
!request.toChain ||
!request.to ||
!request.info
) {
return;
@ -236,7 +236,7 @@ export const toSolana = async (
request.asset,
amountBN,
request.recipient,
request.toChain,
request.to,
nonce,
false,
);