mirror of https://github.com/certusone/oyster.git
feat: bridge
This commit is contained in:
parent
5f55bdf0ef
commit
581bb3ecb7
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"name": "@solana/bridge-ethereum",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@oyster/common": "0.0.1",
|
||||
"@solana/spl-token": "0.0.13",
|
||||
"@solana/spl-token-swap": "0.1.0",
|
||||
"@solana/web3.js": "^0.86.2",
|
||||
"bn.js": "^5.1.3",
|
||||
"bs58": "^4.0.1",
|
||||
"buffer-layout": "^1.2.0",
|
||||
"ethers": "^4.0.48",
|
||||
"eventemitter3": "^4.0.7",
|
||||
"lodash": "^4.17.20",
|
||||
"typescript": "^4.1.3",
|
||||
"web3": "^1.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"format:fix": "prettier --write \"**/*.+(js|jsx|ts|tsx|json|css|md)\"",
|
||||
"ethers": "typechain --target ethers-v4 --outDir src/contracts 'contracts/*.json'"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/solana-labs/oyster"
|
||||
},
|
||||
"homepage": ".",
|
||||
"devDependencies": {
|
||||
"@typechain/ethers-v4": "^1.0.0",
|
||||
"@types/bn.js": "^5.1.0",
|
||||
"@types/bs58": "^4.0.1",
|
||||
"@types/node": "^12.12.62",
|
||||
"arweave-deploy": "^1.9.1",
|
||||
"gh-pages": "^3.1.0",
|
||||
"prettier": "^2.1.2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
// TODO: move bridge interaction code to this library
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"downlevelIteration": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react",
|
||||
"typeRoots": ["../../types"]
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
|
@ -3,7 +3,6 @@
|
|||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^4.4.0",
|
||||
"@ant-design/pro-layout": "^6.7.0",
|
||||
"@babel/preset-typescript": "^7.12.13",
|
||||
"@craco/craco": "^5.7.0",
|
||||
"@oyster/common": "0.0.1",
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
</style>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css"
|
||||
href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css"
|
||||
integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA=="
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
@import "~antd/dist/antd.dark.less";
|
||||
@import "./ant-custom.less";
|
||||
|
||||
@solana-green: #00FFA3;
|
||||
|
||||
body {
|
||||
--row-highlight: @background-color-base;
|
||||
}
|
||||
|
@ -14,6 +16,36 @@ body {
|
|||
width: 32px;
|
||||
}
|
||||
|
||||
.app-title {
|
||||
font-family: "FF Oxide Solid";
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
letter-spacing: 0px;
|
||||
|
||||
h1 {
|
||||
font-size: 48px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
span {
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
background: linear-gradient(270deg, @solana-green 0%, #DC1FFF 101.97%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.app-action {
|
||||
border-color: @solana-green;
|
||||
color: @solana-green;
|
||||
|
||||
padding: 20px 30px;
|
||||
line-height: 2px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
background-color: black;
|
||||
color: lightgray;
|
||||
|
@ -216,6 +248,10 @@ em {
|
|||
}
|
||||
}
|
||||
|
||||
.ant-layout {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.ant-layout-content {
|
||||
display: flex;
|
||||
overflow: auto;
|
||||
|
@ -270,21 +306,13 @@ em {
|
|||
margin: 20px 15px 40px 15px;
|
||||
}
|
||||
|
||||
.ant-pro-sider.ant-layout-sider-collapsed .ant-pro-sider-logo {
|
||||
padding: 8px 8px;
|
||||
}
|
||||
|
||||
.ant-pro-global-header {
|
||||
.ant-pro-global-header-logo a h1 {
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
background-color: black !important;
|
||||
color: white !important;
|
||||
|
||||
.ant-btn {
|
||||
color: white !important;
|
||||
}
|
||||
.ant-layout-header {
|
||||
align-items: center;
|
||||
height: 42px;
|
||||
padding: 0 20px;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
line-height: 64px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.ant-statistic {
|
||||
|
@ -315,6 +343,7 @@ em {
|
|||
color: @text-color-secondary;
|
||||
}
|
||||
|
||||
|
||||
[class="ant-layout-header"] {
|
||||
height: 16px !important;
|
||||
}
|
||||
|
@ -325,22 +354,22 @@ em {
|
|||
text-align: right;
|
||||
}
|
||||
|
||||
.small-statisitc {
|
||||
.home-statistic {
|
||||
font-family: "FF Oxide Solid";
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
letter-spacing: 0px;
|
||||
|
||||
.ant-statistic-title {
|
||||
font-size: 12px;
|
||||
display: inline-block;
|
||||
font-size: 48px !important;
|
||||
background: linear-gradient(254.55deg, @solana-green 10.02%, #DC1FFF 89.22%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.ant-statistic-content {
|
||||
max-height: 20px;
|
||||
line-height: 2px;;
|
||||
}
|
||||
|
||||
.ant-statistic-content-value-int {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.ant-statistic-content-value-decimal {
|
||||
font-size: 10px;
|
||||
font-size: 20px !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
@import '~antd/es/style/themes/dark.less';
|
||||
@import "~antd/dist/antd.dark.less";
|
||||
|
||||
@font-face {
|
||||
font-family: "FF Oxide Solid";
|
||||
src: url("//db.onlinewebfonts.com/t/bc30adee89b44a44a5cfdff676aef0fe.eot");
|
||||
src: url("//db.onlinewebfonts.com/t/bc30adee89b44a44a5cfdff676aef0fe.eot?#iefix") format("embedded-opentype"),
|
||||
url("//db.onlinewebfonts.com/t/bc30adee89b44a44a5cfdff676aef0fe.woff2") format("woff2"),
|
||||
url("//db.onlinewebfonts.com/t/bc30adee89b44a44a5cfdff676aef0fe.woff") format("woff"),
|
||||
url("//db.onlinewebfonts.com/t/bc30adee89b44a44a5cfdff676aef0fe.ttf") format("truetype"),
|
||||
url("//db.onlinewebfonts.com/t/bc30adee89b44a44a5cfdff676aef0fe.svg#FF Oxide Solid") format("svg");
|
||||
}
|
||||
|
||||
@primary-color: #ff00a8;
|
||||
@popover-background: #1a2029;
|
||||
|
|
|
@ -1,26 +1,13 @@
|
|||
import React from 'react';
|
||||
import './../../App.less';
|
||||
import { Menu } from 'antd';
|
||||
import {
|
||||
PieChartOutlined,
|
||||
GithubOutlined,
|
||||
BankOutlined,
|
||||
LogoutOutlined,
|
||||
ShoppingOutlined,
|
||||
HomeOutlined,
|
||||
RocketOutlined,
|
||||
ForkOutlined,
|
||||
// LineChartOutlined
|
||||
} from '@ant-design/icons';
|
||||
|
||||
import BasicLayout from '@ant-design/pro-layout';
|
||||
import { AppBar } from './../AppBar';
|
||||
import { Layout } from 'antd';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
|
||||
import { LABELS } from '../../constants';
|
||||
import config from './../../../package.json';
|
||||
import { contexts } from '@oyster/common';
|
||||
|
||||
|
||||
const { Header, Content } = Layout;
|
||||
const { useConnectionConfig } = contexts.Connection;
|
||||
|
||||
export const AppLayout = React.memo((props: any) => {
|
||||
|
@ -34,29 +21,18 @@ export const AppLayout = React.memo((props: any) => {
|
|||
const current =
|
||||
[...Object.keys(paths)].find(key => location.pathname.startsWith(key)) ||
|
||||
'';
|
||||
const defaultKey = paths[current] || '1';
|
||||
const theme = 'dark';
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
{/* <BasicLayout
|
||||
<div className="App wormhole-bg">
|
||||
<Layout
|
||||
title={LABELS.APP_TITLE}
|
||||
footerRender={() => (
|
||||
<div className="footer" title={LABELS.FOOTER}>
|
||||
{LABELS.FOOTER}
|
||||
</div>
|
||||
)}
|
||||
navTheme={theme}
|
||||
// headerTheme={theme}
|
||||
theme={theme}
|
||||
layout="top"
|
||||
primaryColor="#d83aeb"
|
||||
logo={<div className="App-logo" />}
|
||||
// rightContentRender={() => <AppBar />}
|
||||
links={[]}
|
||||
> */}
|
||||
{props.children}
|
||||
{/* </BasicLayout> */}
|
||||
>
|
||||
{location.pathname !== '/' && <Header className="header">
|
||||
<div className="App-logo" />
|
||||
</Header>}
|
||||
<Content style={{ padding: '0 50px' }}>
|
||||
{props.children}
|
||||
</Content>
|
||||
</Layout>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import { Button, Card } from 'antd';
|
||||
import { LAMPORTS_PER_SOL } from '@solana/web3.js';
|
||||
import { LABELS } from '../../constants';
|
||||
import { contexts, utils, ConnectButton } from '@oyster/common';
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
|
||||
import './style.less';
|
||||
|
||||
const { useConnection } = contexts.Connection;
|
||||
const { useWallet } = contexts.Wallet;
|
||||
const { notify } = utils;
|
||||
|
||||
export const Transfer = () => {
|
||||
const connection = useConnection();
|
||||
const { wallet } = useWallet();
|
||||
|
||||
const tabStyle: React.CSSProperties = { width: 120 };
|
||||
const tabList = [
|
||||
{
|
||||
key: "eth",
|
||||
tab: <div style={tabStyle}>Transfer</div>,
|
||||
render: () => {
|
||||
return <div>Bring assets to Solana</div>;
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "sol",
|
||||
tab: <div style={tabStyle}>Wrap</div>,
|
||||
render: () => {
|
||||
return <div>Bring assets to Solana</div>;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const location = useLocation();
|
||||
const history = useHistory();
|
||||
const activeTab = location.pathname.indexOf("eth") < 0 ? "sol" : "eth";
|
||||
|
||||
const handleTabChange = (key: any) => {
|
||||
if (activeTab !== key) {
|
||||
if (key === "sol") {
|
||||
history.push("/move/sol");
|
||||
} else {
|
||||
history.push("/move/eth");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="input-card">
|
||||
INPUT
|
||||
<Button type="primary" className="swap-button">
|
||||
⇅
|
||||
</Button>
|
||||
OUTPUT
|
||||
</div>
|
||||
<ConnectButton type="primary">
|
||||
Transfer
|
||||
</ConnectButton>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,12 @@
|
|||
.input-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.swap-button {
|
||||
border-radius: 2em;
|
||||
width: 32px;
|
||||
padding-left: 8px;
|
||||
}
|
|
@ -7,6 +7,7 @@ import { AppLayout } from './components/Layout';
|
|||
import {
|
||||
FaucetView,
|
||||
HomeView,
|
||||
TransferView,
|
||||
} from './views';
|
||||
const { WalletProvider } = contexts.Wallet;
|
||||
const { ConnectionProvider } = contexts.Connection;
|
||||
|
@ -23,6 +24,7 @@ export function Routes() {
|
|||
<AppLayout>
|
||||
<Switch>
|
||||
<Route exact path="/" component={() => <HomeView />} />
|
||||
<Route path="/move" children={<TransferView />} />
|
||||
<Route exact path="/faucet" children={<FaucetView />} />
|
||||
</Switch>
|
||||
</AppLayout>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { MintInfo } from '@solana/spl-token';
|
||||
import { Table, Tag, Space, Card, Col, Row, Statistic } from 'antd';
|
||||
import { Table, Tag, Space, Card, Col, Row, Statistic, Button } from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { GUTTER, LABELS } from '../../constants';
|
||||
import { contexts, ParsedAccount, utils } from '@oyster/common';
|
||||
|
@ -9,6 +9,7 @@ import { LendingReserveItem } from './item';
|
|||
import { AppBar } from './../../components/AppBar';
|
||||
import './itemStyle.less';
|
||||
import { Totals } from '../../models/totals';
|
||||
import { Link } from 'react-router-dom';
|
||||
const { fromLamports, getTokenName, wadToLamports } = utils;
|
||||
const { cache } = contexts.Accounts;
|
||||
const { useConnectionConfig } = contexts.Connection;
|
||||
|
@ -115,33 +116,21 @@ export const HomeView = () => {
|
|||
gutter={GUTTER}
|
||||
justify="center"
|
||||
align="middle"
|
||||
className="home-info-row wormhole-bg"
|
||||
className="home-info-row"
|
||||
>
|
||||
<Col xs={24} xl={8}>
|
||||
<Col xs={24} xl={12} className="app-title">
|
||||
<h1>Wormhole</h1>
|
||||
<h2>Ethereum and Solana Bridge</h2>
|
||||
<AppBar />
|
||||
<h2><span>Ethereum + Solana Bridge</span></h2>
|
||||
<Link to="/move">
|
||||
<Button className="app-action">Get Started</Button>
|
||||
</Link>
|
||||
</Col>
|
||||
<Col xs={24} xl={5}>
|
||||
<Card>
|
||||
<Statistic
|
||||
title="Current market size"
|
||||
value={totals.marketSize}
|
||||
precision={2}
|
||||
valueStyle={{ color: '#3fBB00' }}
|
||||
prefix="$"
|
||||
/>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col xs={24} xl={5}>
|
||||
<Card>
|
||||
<Statistic
|
||||
title="Assets"
|
||||
value={totals.numberOfAssets}
|
||||
precision={2}
|
||||
prefix="$"
|
||||
/>
|
||||
</Card>
|
||||
<Col xs={24} xl={12}>
|
||||
<Statistic
|
||||
className="home-statistic"
|
||||
title="$1,231"
|
||||
value="TOTAL VALUE LOCKED"
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Table dataSource={dataSource} columns={columns} />
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
export { HomeView } from "./home";
|
||||
export { FaucetView } from "./faucet";
|
||||
export { TransferView } from "./transfer";
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
import React, { useCallback } 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 { useHistory, useLocation } from "react-router-dom";
|
||||
const { useConnection } = contexts.Connection;
|
||||
const { useWallet } = contexts.Wallet;
|
||||
const { notify } = utils;
|
||||
|
||||
export const TransferView = () => {
|
||||
const connection = useConnection();
|
||||
const { wallet } = useWallet();
|
||||
|
||||
const tabStyle: React.CSSProperties = { width: 120 };
|
||||
const tabList = [
|
||||
{
|
||||
key: "eth",
|
||||
tab: <div style={tabStyle}>Transfer</div>,
|
||||
render: () => {
|
||||
return <div>Bring assets to Solana</div>;
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "sol",
|
||||
tab: <div style={tabStyle}>Wrap</div>,
|
||||
render: () => {
|
||||
return <div>Bring assets to Solana</div>;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const location = useLocation();
|
||||
const history = useHistory();
|
||||
const activeTab = location.pathname.indexOf("eth") < 0 ? "sol" : "eth";
|
||||
|
||||
const handleTabChange = (key: any) => {
|
||||
if (activeTab !== key) {
|
||||
if (key === "sol") {
|
||||
history.push("/move/sol");
|
||||
} else {
|
||||
history.push("/move/eth");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flexColumn" style={{ flex: 1 }}>
|
||||
<Card
|
||||
className="bridge-card"
|
||||
headStyle={{ padding: 0 }}
|
||||
bodyStyle={{ position: "relative" }}
|
||||
tabList={tabList}
|
||||
tabProps={{
|
||||
tabBarGutter: 0,
|
||||
}}
|
||||
activeTabKey={activeTab}
|
||||
onTabChange={(key) => {
|
||||
handleTabChange(key);
|
||||
}}>
|
||||
{tabList.find((t) => t.key === activeTab)?.render()}
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
Loading…
Reference in New Issue