mirror of https://github.com/certusone/oyster.git
feat: pair
This commit is contained in:
parent
4c2eca612e
commit
92fefe2b9f
|
@ -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>
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue