move examples from wormhole repo

This commit is contained in:
Evan Gray 2022-07-15 12:27:45 -04:00
parent c664796d5d
commit 5c4d3d440f
120 changed files with 149498 additions and 0 deletions

39
EthSwapNGo/README.md Normal file
View File

@ -0,0 +1,39 @@
## Eth Swap & Go Example Program
This is a non-production example program.
The demonstrates a simple React program which can swap native EVM currencies, using only the wormhole bridge & a relayer.
It assumes that a fresh devnet is running in conjunction with the restRelayer example project.
### In order to run this project, you must first run npm ci in the following directories:
> /examples/core
>
> /examples/rest-relayer
>
> /examples/EthSwapNGo/swapPool
>
> /examples/EthSwapNGo/react
### Once you've installed all the dependencies, you can create the swap pools:
> cd /examples/EthSwapNGo/swapPool
>
> npm run deployAndSeed
### Then start the rest-relayer service:
> cd /examples/rest-relayer
>
> npm run start
### And lastly, start the react app:
> cd /examples/EthSwapNGo/react
>
> npm run start
```
```

26
EthSwapNGo/react/.gitignore vendored Normal file
View File

@ -0,0 +1,26 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# generated file from deployAndSeed
/src/swapPools.json

View File

@ -0,0 +1,28 @@
const { addBeforeLoader, loaderByName } = require("@craco/craco");
module.exports = {
webpack: {
configure: (webpackConfig) => {
const wasmExtensionRegExp = /\.wasm$/;
webpackConfig.resolve.extensions.push(".wasm");
webpackConfig.module.rules.forEach((rule) => {
(rule.oneOf || []).forEach((oneOf) => {
if (oneOf.loader && oneOf.loader.indexOf("file-loader") >= 0) {
oneOf.exclude.push(wasmExtensionRegExp);
}
});
});
const wasmLoader = {
test: /\.wasm$/,
include: /node_modules\/(bridge|token-bridge)/,
loaders: ["wasm-loader"],
};
addBeforeLoader(webpackConfig, loaderByName("file-loader"), wasmLoader);
return webpackConfig;
},
},
};

44202
EthSwapNGo/react/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,61 @@
{
"name": "Eth Swap & Go",
"version": "0.1.0",
"private": true,
"dependencies": {
"@certusone/wormhole-sdk": "^0.1.1",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.60",
"@metamask/detect-provider": "^1.2.0",
"@solana/spl-token": "^0.1.6",
"@solana/spl-token-registry": "^0.2.216",
"@solana/wallet-adapter-base": "^0.5.2",
"@solana/wallet-adapter-material-ui": "^0.11.0",
"@solana/wallet-adapter-phantom": "^0.5.3",
"@solana/wallet-adapter-react": "^0.11.0",
"@solana/wallet-adapter-wallets": "^0.9.0",
"@solana/wallet-base": "^0.0.1",
"@solana/web3.js": "^1.24.1",
"@terra-money/wallet-provider": "^2.2.0",
"@types/node": "^16.9.1",
"@types/react": "^17.0.20",
"@types/react-dom": "^17.0.9",
"axios": "^0.24.0",
"clsx": "^1.1.1",
"ethers": "^5.4.6",
"notistack": "^1.0.10",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "4.0.3",
"typescript": "^4.4.2"
},
"scripts": {
"start": "craco start",
"build": "craco build",
"test": "craco test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@craco/craco": "^6.3.0",
"wasm-loader": "^1.3.0"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1,55 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Sora:wght@200;300;400;500;700&display=swap"
rel="stylesheet"
/>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@500&display=swap"
rel="stylesheet"
/>
<title>Wormhole Converter</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View File

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View File

@ -0,0 +1,7 @@
import Home from "./views/Home";
function App() {
return <Home />;
}
export default App;

384
EthSwapNGo/react/src/abi/IERC20.d.ts vendored Normal file
View File

@ -0,0 +1,384 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import {
ethers,
EventFilter,
Signer,
BigNumber,
BigNumberish,
PopulatedTransaction,
BaseContract,
ContractTransaction,
Overrides,
CallOverrides,
} from "ethers";
import {BytesLike} from "@ethersproject/bytes";
import {Listener, Provider} from "@ethersproject/providers";
import {FunctionFragment, EventFragment, Result} from "@ethersproject/abi";
import {TypedEventFilter, TypedEvent, TypedListener} from "./commons";
interface IERC20Interface extends ethers.utils.Interface {
functions: {
"totalSupply()": FunctionFragment;
"balanceOf(address)": FunctionFragment;
"transfer(address,uint256)": FunctionFragment;
"allowance(address,address)": FunctionFragment;
"approve(address,uint256)": FunctionFragment;
"transferFrom(address,address,uint256)": FunctionFragment;
};
encodeFunctionData(
functionFragment: "totalSupply",
values?: undefined
): string;
encodeFunctionData(functionFragment: "balanceOf", values: [string]): string;
encodeFunctionData(
functionFragment: "transfer",
values: [string, BigNumberish]
): string;
encodeFunctionData(
functionFragment: "allowance",
values: [string, string]
): string;
encodeFunctionData(
functionFragment: "approve",
values: [string, BigNumberish]
): string;
encodeFunctionData(
functionFragment: "transferFrom",
values: [string, string, BigNumberish]
): string;
decodeFunctionResult(
functionFragment: "totalSupply",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "balanceOf", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "transfer", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "allowance", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "approve", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "transferFrom",
data: BytesLike
): Result;
events: {
"Approval(address,address,uint256)": EventFragment;
"Transfer(address,address,uint256)": EventFragment;
};
getEvent(nameOrSignatureOrTopic: "Approval"): EventFragment;
getEvent(nameOrSignatureOrTopic: "Transfer"): EventFragment;
}
export class IERC20 extends BaseContract {
connect(signerOrProvider: Signer | Provider | string): this;
attach(addressOrName: string): this;
deployed(): Promise<this>;
listeners<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter?: TypedEventFilter<EventArgsArray, EventArgsObject>
): Array<TypedListener<EventArgsArray, EventArgsObject>>;
off<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
listener: TypedListener<EventArgsArray, EventArgsObject>
): this;
on<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
listener: TypedListener<EventArgsArray, EventArgsObject>
): this;
once<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
listener: TypedListener<EventArgsArray, EventArgsObject>
): this;
removeListener<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
listener: TypedListener<EventArgsArray, EventArgsObject>
): this;
removeAllListeners<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>
): this;
listeners(eventName?: string): Array<Listener>;
off(eventName: string, listener: Listener): this;
on(eventName: string, listener: Listener): this;
once(eventName: string, listener: Listener): this;
removeListener(eventName: string, listener: Listener): this;
removeAllListeners(eventName?: string): this;
queryFilter<EventArgsArray extends Array<any>, EventArgsObject>(
event: TypedEventFilter<EventArgsArray, EventArgsObject>,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TypedEvent<EventArgsArray & EventArgsObject>>>;
interface: IERC20Interface;
functions: {
/**
* Returns the amount of tokens in existence.
*/
totalSupply(overrides?: CallOverrides): Promise<[BigNumber]>;
/**
* Returns the amount of tokens owned by `account`.
*/
balanceOf(account: string, overrides?: CallOverrides): Promise<[BigNumber]>;
/**
* Moves `amount` tokens from the caller's account to `recipient`. * Returns a boolean value indicating whether the operation succeeded. * Emits a {Transfer} event.
*/
transfer(
recipient: string,
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
/**
* Returns the remaining number of tokens that `spender` will be allowed to spend on behalf of `owner` through {transferFrom}. This is zero by default. * This value changes when {approve} or {transferFrom} are called.
*/
allowance(
owner: string,
spender: string,
overrides?: CallOverrides
): Promise<[BigNumber]>;
/**
* Sets `amount` as the allowance of `spender` over the caller's tokens. * Returns a boolean value indicating whether the operation succeeded. * IMPORTANT: Beware that changing an allowance with this method brings the risk that someone may use both the old and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * Emits an {Approval} event.
*/
approve(
spender: string,
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
/**
* Moves `amount` tokens from `sender` to `recipient` using the allowance mechanism. `amount` is then deducted from the caller's allowance. * Returns a boolean value indicating whether the operation succeeded. * Emits a {Transfer} event.
*/
transferFrom(
sender: string,
recipient: string,
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
};
/**
* Returns the amount of tokens in existence.
*/
totalSupply(overrides?: CallOverrides): Promise<BigNumber>;
/**
* Returns the amount of tokens owned by `account`.
*/
balanceOf(account: string, overrides?: CallOverrides): Promise<BigNumber>;
/**
* Moves `amount` tokens from the caller's account to `recipient`. * Returns a boolean value indicating whether the operation succeeded. * Emits a {Transfer} event.
*/
transfer(
recipient: string,
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
/**
* Returns the remaining number of tokens that `spender` will be allowed to spend on behalf of `owner` through {transferFrom}. This is zero by default. * This value changes when {approve} or {transferFrom} are called.
*/
allowance(
owner: string,
spender: string,
overrides?: CallOverrides
): Promise<BigNumber>;
/**
* Sets `amount` as the allowance of `spender` over the caller's tokens. * Returns a boolean value indicating whether the operation succeeded. * IMPORTANT: Beware that changing an allowance with this method brings the risk that someone may use both the old and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * Emits an {Approval} event.
*/
approve(
spender: string,
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
/**
* Moves `amount` tokens from `sender` to `recipient` using the allowance mechanism. `amount` is then deducted from the caller's allowance. * Returns a boolean value indicating whether the operation succeeded. * Emits a {Transfer} event.
*/
transferFrom(
sender: string,
recipient: string,
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
callStatic: {
/**
* Returns the amount of tokens in existence.
*/
totalSupply(overrides?: CallOverrides): Promise<BigNumber>;
/**
* Returns the amount of tokens owned by `account`.
*/
balanceOf(account: string, overrides?: CallOverrides): Promise<BigNumber>;
/**
* Moves `amount` tokens from the caller's account to `recipient`. * Returns a boolean value indicating whether the operation succeeded. * Emits a {Transfer} event.
*/
transfer(
recipient: string,
amount: BigNumberish,
overrides?: CallOverrides
): Promise<boolean>;
/**
* Returns the remaining number of tokens that `spender` will be allowed to spend on behalf of `owner` through {transferFrom}. This is zero by default. * This value changes when {approve} or {transferFrom} are called.
*/
allowance(
owner: string,
spender: string,
overrides?: CallOverrides
): Promise<BigNumber>;
/**
* Sets `amount` as the allowance of `spender` over the caller's tokens. * Returns a boolean value indicating whether the operation succeeded. * IMPORTANT: Beware that changing an allowance with this method brings the risk that someone may use both the old and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * Emits an {Approval} event.
*/
approve(
spender: string,
amount: BigNumberish,
overrides?: CallOverrides
): Promise<boolean>;
/**
* Moves `amount` tokens from `sender` to `recipient` using the allowance mechanism. `amount` is then deducted from the caller's allowance. * Returns a boolean value indicating whether the operation succeeded. * Emits a {Transfer} event.
*/
transferFrom(
sender: string,
recipient: string,
amount: BigNumberish,
overrides?: CallOverrides
): Promise<boolean>;
};
filters: {
Approval(
owner?: string | null,
spender?: string | null,
value?: null
): TypedEventFilter<
[string, string, BigNumber],
{owner: string; spender: string; value: BigNumber}
>;
Transfer(
from?: string | null,
to?: string | null,
value?: null
): TypedEventFilter<
[string, string, BigNumber],
{from: string; to: string; value: BigNumber}
>;
};
estimateGas: {
/**
* Returns the amount of tokens in existence.
*/
totalSupply(overrides?: CallOverrides): Promise<BigNumber>;
/**
* Returns the amount of tokens owned by `account`.
*/
balanceOf(account: string, overrides?: CallOverrides): Promise<BigNumber>;
/**
* Moves `amount` tokens from the caller's account to `recipient`. * Returns a boolean value indicating whether the operation succeeded. * Emits a {Transfer} event.
*/
transfer(
recipient: string,
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<BigNumber>;
/**
* Returns the remaining number of tokens that `spender` will be allowed to spend on behalf of `owner` through {transferFrom}. This is zero by default. * This value changes when {approve} or {transferFrom} are called.
*/
allowance(
owner: string,
spender: string,
overrides?: CallOverrides
): Promise<BigNumber>;
/**
* Sets `amount` as the allowance of `spender` over the caller's tokens. * Returns a boolean value indicating whether the operation succeeded. * IMPORTANT: Beware that changing an allowance with this method brings the risk that someone may use both the old and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * Emits an {Approval} event.
*/
approve(
spender: string,
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<BigNumber>;
/**
* Moves `amount` tokens from `sender` to `recipient` using the allowance mechanism. `amount` is then deducted from the caller's allowance. * Returns a boolean value indicating whether the operation succeeded. * Emits a {Transfer} event.
*/
transferFrom(
sender: string,
recipient: string,
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<BigNumber>;
};
populateTransaction: {
/**
* Returns the amount of tokens in existence.
*/
totalSupply(overrides?: CallOverrides): Promise<PopulatedTransaction>;
/**
* Returns the amount of tokens owned by `account`.
*/
balanceOf(
account: string,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
/**
* Moves `amount` tokens from the caller's account to `recipient`. * Returns a boolean value indicating whether the operation succeeded. * Emits a {Transfer} event.
*/
transfer(
recipient: string,
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<PopulatedTransaction>;
/**
* Returns the remaining number of tokens that `spender` will be allowed to spend on behalf of `owner` through {transferFrom}. This is zero by default. * This value changes when {approve} or {transferFrom} are called.
*/
allowance(
owner: string,
spender: string,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
/**
* Sets `amount` as the allowance of `spender` over the caller's tokens. * Returns a boolean value indicating whether the operation succeeded. * IMPORTANT: Beware that changing an allowance with this method brings the risk that someone may use both the old and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * Emits an {Approval} event.
*/
approve(
spender: string,
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<PopulatedTransaction>;
/**
* Moves `amount` tokens from `sender` to `recipient` using the allowance mechanism. `amount` is then deducted from the caller's allowance. * Returns a boolean value indicating whether the operation succeeded. * Emits a {Transfer} event.
*/
transferFrom(
sender: string,
recipient: string,
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<PopulatedTransaction>;
};
}

356
EthSwapNGo/react/src/abi/SimpleDex.d.ts vendored Normal file
View File

@ -0,0 +1,356 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import {
ethers,
EventFilter,
Signer,
BigNumber,
BigNumberish,
PopulatedTransaction,
BaseContract,
ContractTransaction,
Overrides,
PayableOverrides,
CallOverrides,
} from "ethers";
import {BytesLike} from "@ethersproject/bytes";
import {Listener, Provider} from "@ethersproject/providers";
import {FunctionFragment, EventFragment, Result} from "@ethersproject/abi";
import {TypedEventFilter, TypedEvent, TypedListener} from "./commons";
interface SimpleDexInterface extends ethers.utils.Interface {
functions: {
"liquidity(address)": FunctionFragment;
"purpose()": FunctionFragment;
"totalLiquidity()": FunctionFragment;
"init(uint256)": FunctionFragment;
"price(uint256,uint256,uint256)": FunctionFragment;
"ethToToken()": FunctionFragment;
"tokenToEth(uint256)": FunctionFragment;
"deposit()": FunctionFragment;
"withdraw(uint256)": FunctionFragment;
"swapNGo(address,uint16,bytes32,uint256,uint32)": FunctionFragment;
};
encodeFunctionData(functionFragment: "liquidity", values: [string]): string;
encodeFunctionData(functionFragment: "purpose", values?: undefined): string;
encodeFunctionData(
functionFragment: "totalLiquidity",
values?: undefined
): string;
encodeFunctionData(functionFragment: "init", values: [BigNumberish]): string;
encodeFunctionData(
functionFragment: "price",
values: [BigNumberish, BigNumberish, BigNumberish]
): string;
encodeFunctionData(
functionFragment: "ethToToken",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "tokenToEth",
values: [BigNumberish]
): string;
encodeFunctionData(functionFragment: "deposit", values?: undefined): string;
encodeFunctionData(
functionFragment: "withdraw",
values: [BigNumberish]
): string;
encodeFunctionData(
functionFragment: "swapNGo",
values: [string, BigNumberish, BytesLike, BigNumberish, BigNumberish]
): string;
decodeFunctionResult(functionFragment: "liquidity", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "purpose", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "totalLiquidity",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "init", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "price", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "ethToToken", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "tokenToEth", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "deposit", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "withdraw", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "swapNGo", data: BytesLike): Result;
events: {};
}
export class SimpleDex extends BaseContract {
connect(signerOrProvider: Signer | Provider | string): this;
attach(addressOrName: string): this;
deployed(): Promise<this>;
listeners<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter?: TypedEventFilter<EventArgsArray, EventArgsObject>
): Array<TypedListener<EventArgsArray, EventArgsObject>>;
off<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
listener: TypedListener<EventArgsArray, EventArgsObject>
): this;
on<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
listener: TypedListener<EventArgsArray, EventArgsObject>
): this;
once<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
listener: TypedListener<EventArgsArray, EventArgsObject>
): this;
removeListener<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
listener: TypedListener<EventArgsArray, EventArgsObject>
): this;
removeAllListeners<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>
): this;
listeners(eventName?: string): Array<Listener>;
off(eventName: string, listener: Listener): this;
on(eventName: string, listener: Listener): this;
once(eventName: string, listener: Listener): this;
removeListener(eventName: string, listener: Listener): this;
removeAllListeners(eventName?: string): this;
queryFilter<EventArgsArray extends Array<any>, EventArgsObject>(
event: TypedEventFilter<EventArgsArray, EventArgsObject>,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TypedEvent<EventArgsArray & EventArgsObject>>>;
interface: SimpleDexInterface;
functions: {
liquidity(arg0: string, overrides?: CallOverrides): Promise<[BigNumber]>;
purpose(overrides?: CallOverrides): Promise<[string]>;
totalLiquidity(overrides?: CallOverrides): Promise<[BigNumber]>;
init(
tokens: BigNumberish,
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
price(
input_amount: BigNumberish,
input_reserve: BigNumberish,
output_reserve: BigNumberish,
overrides?: CallOverrides
): Promise<[BigNumber]>;
ethToToken(
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
tokenToEth(
tokens: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
deposit(
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
withdraw(
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
swapNGo(
_address: string,
targetChain: BigNumberish,
targetAddress: BytesLike,
fee: BigNumberish,
nonce: BigNumberish,
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
};
liquidity(arg0: string, overrides?: CallOverrides): Promise<BigNumber>;
purpose(overrides?: CallOverrides): Promise<string>;
totalLiquidity(overrides?: CallOverrides): Promise<BigNumber>;
init(
tokens: BigNumberish,
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
price(
input_amount: BigNumberish,
input_reserve: BigNumberish,
output_reserve: BigNumberish,
overrides?: CallOverrides
): Promise<BigNumber>;
ethToToken(
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
tokenToEth(
tokens: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
deposit(
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
withdraw(
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
swapNGo(
_address: string,
targetChain: BigNumberish,
targetAddress: BytesLike,
fee: BigNumberish,
nonce: BigNumberish,
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
callStatic: {
liquidity(arg0: string, overrides?: CallOverrides): Promise<BigNumber>;
purpose(overrides?: CallOverrides): Promise<string>;
totalLiquidity(overrides?: CallOverrides): Promise<BigNumber>;
init(tokens: BigNumberish, overrides?: CallOverrides): Promise<BigNumber>;
price(
input_amount: BigNumberish,
input_reserve: BigNumberish,
output_reserve: BigNumberish,
overrides?: CallOverrides
): Promise<BigNumber>;
ethToToken(overrides?: CallOverrides): Promise<BigNumber>;
tokenToEth(
tokens: BigNumberish,
overrides?: CallOverrides
): Promise<BigNumber>;
deposit(overrides?: CallOverrides): Promise<BigNumber>;
withdraw(
amount: BigNumberish,
overrides?: CallOverrides
): Promise<[BigNumber, BigNumber]>;
swapNGo(
_address: string,
targetChain: BigNumberish,
targetAddress: BytesLike,
fee: BigNumberish,
nonce: BigNumberish,
overrides?: CallOverrides
): Promise<BigNumber>;
};
filters: {};
estimateGas: {
liquidity(arg0: string, overrides?: CallOverrides): Promise<BigNumber>;
purpose(overrides?: CallOverrides): Promise<BigNumber>;
totalLiquidity(overrides?: CallOverrides): Promise<BigNumber>;
init(
tokens: BigNumberish,
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<BigNumber>;
price(
input_amount: BigNumberish,
input_reserve: BigNumberish,
output_reserve: BigNumberish,
overrides?: CallOverrides
): Promise<BigNumber>;
ethToToken(
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<BigNumber>;
tokenToEth(
tokens: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<BigNumber>;
deposit(
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<BigNumber>;
withdraw(
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<BigNumber>;
swapNGo(
_address: string,
targetChain: BigNumberish,
targetAddress: BytesLike,
fee: BigNumberish,
nonce: BigNumberish,
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<BigNumber>;
};
populateTransaction: {
liquidity(
arg0: string,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
purpose(overrides?: CallOverrides): Promise<PopulatedTransaction>;
totalLiquidity(overrides?: CallOverrides): Promise<PopulatedTransaction>;
init(
tokens: BigNumberish,
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<PopulatedTransaction>;
price(
input_amount: BigNumberish,
input_reserve: BigNumberish,
output_reserve: BigNumberish,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
ethToToken(
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<PopulatedTransaction>;
tokenToEth(
tokens: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<PopulatedTransaction>;
deposit(
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<PopulatedTransaction>;
withdraw(
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<PopulatedTransaction>;
swapNGo(
_address: string,
targetChain: BigNumberish,
targetAddress: BytesLike,
fee: BigNumberish,
nonce: BigNumberish,
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<PopulatedTransaction>;
};
}

View File

@ -0,0 +1,149 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import {
ethers,
EventFilter,
Signer,
BigNumber,
BigNumberish,
PopulatedTransaction,
BaseContract,
ContractTransaction,
PayableOverrides,
CallOverrides,
} from "ethers";
import {BytesLike} from "@ethersproject/bytes";
import {Listener, Provider} from "@ethersproject/providers";
import {FunctionFragment, EventFragment, Result} from "@ethersproject/abi";
import {TypedEventFilter, TypedEvent, TypedListener} from "./commons";
interface TokenBridgeInterface extends ethers.utils.Interface {
functions: {
"transferTokens(address,uint256,uint16,bytes32,uint256,uint32)": FunctionFragment;
};
encodeFunctionData(
functionFragment: "transferTokens",
values: [
string,
BigNumberish,
BigNumberish,
BytesLike,
BigNumberish,
BigNumberish
]
): string;
decodeFunctionResult(
functionFragment: "transferTokens",
data: BytesLike
): Result;
events: {};
}
export class TokenBridge extends BaseContract {
connect(signerOrProvider: Signer | Provider | string): this;
attach(addressOrName: string): this;
deployed(): Promise<this>;
listeners<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter?: TypedEventFilter<EventArgsArray, EventArgsObject>
): Array<TypedListener<EventArgsArray, EventArgsObject>>;
off<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
listener: TypedListener<EventArgsArray, EventArgsObject>
): this;
on<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
listener: TypedListener<EventArgsArray, EventArgsObject>
): this;
once<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
listener: TypedListener<EventArgsArray, EventArgsObject>
): this;
removeListener<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
listener: TypedListener<EventArgsArray, EventArgsObject>
): this;
removeAllListeners<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>
): this;
listeners(eventName?: string): Array<Listener>;
off(eventName: string, listener: Listener): this;
on(eventName: string, listener: Listener): this;
once(eventName: string, listener: Listener): this;
removeListener(eventName: string, listener: Listener): this;
removeAllListeners(eventName?: string): this;
queryFilter<EventArgsArray extends Array<any>, EventArgsObject>(
event: TypedEventFilter<EventArgsArray, EventArgsObject>,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TypedEvent<EventArgsArray & EventArgsObject>>>;
interface: TokenBridgeInterface;
functions: {
transferTokens(
token: string,
amount: BigNumberish,
recipientChain: BigNumberish,
recipient: BytesLike,
arbiterFee: BigNumberish,
nonce: BigNumberish,
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
};
transferTokens(
token: string,
amount: BigNumberish,
recipientChain: BigNumberish,
recipient: BytesLike,
arbiterFee: BigNumberish,
nonce: BigNumberish,
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
callStatic: {
transferTokens(
token: string,
amount: BigNumberish,
recipientChain: BigNumberish,
recipient: BytesLike,
arbiterFee: BigNumberish,
nonce: BigNumberish,
overrides?: CallOverrides
): Promise<BigNumber>;
};
filters: {};
estimateGas: {
transferTokens(
token: string,
amount: BigNumberish,
recipientChain: BigNumberish,
recipient: BytesLike,
arbiterFee: BigNumberish,
nonce: BigNumberish,
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<BigNumber>;
};
populateTransaction: {
transferTokens(
token: string,
amount: BigNumberish,
recipientChain: BigNumberish,
recipient: BytesLike,
arbiterFee: BigNumberish,
nonce: BigNumberish,
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<PopulatedTransaction>;
};
}

View File

@ -0,0 +1,36 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import {EventFilter, Event} from "ethers";
import {Result} from "@ethersproject/abi";
export interface TypedEventFilter<_EventArgsArray, _EventArgsObject>
extends EventFilter {}
export interface TypedEvent<EventArgs extends Result> extends Event {
args: EventArgs;
}
export type TypedListener<
EventArgsArray extends Array<any>,
EventArgsObject
> = (
...listenerArg: [
...EventArgsArray,
TypedEvent<EventArgsArray & EventArgsObject>
]
) => void;
export type MinEthersFactory<C, ARGS> = {
deploy(...a: ARGS[]): Promise<C>;
};
export type GetContractTypeFromFactory<F> = F extends MinEthersFactory<
infer C,
any
>
? C
: never;
export type GetARGsTypeFromFactory<F> = F extends MinEthersFactory<any, any>
? Parameters<F["deploy"]>
: never;

View File

@ -0,0 +1,203 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import {Contract, Signer, utils} from "ethers";
import {Provider} from "@ethersproject/providers";
import type {IERC20, IERC20Interface} from "../IERC20";
const _abi = [
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "owner",
type: "address",
},
{
indexed: true,
internalType: "address",
name: "spender",
type: "address",
},
{
indexed: false,
internalType: "uint256",
name: "value",
type: "uint256",
},
],
name: "Approval",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "from",
type: "address",
},
{
indexed: true,
internalType: "address",
name: "to",
type: "address",
},
{
indexed: false,
internalType: "uint256",
name: "value",
type: "uint256",
},
],
name: "Transfer",
type: "event",
},
{
inputs: [],
name: "totalSupply",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "account",
type: "address",
},
],
name: "balanceOf",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "recipient",
type: "address",
},
{
internalType: "uint256",
name: "amount",
type: "uint256",
},
],
name: "transfer",
outputs: [
{
internalType: "bool",
name: "",
type: "bool",
},
],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "owner",
type: "address",
},
{
internalType: "address",
name: "spender",
type: "address",
},
],
name: "allowance",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "spender",
type: "address",
},
{
internalType: "uint256",
name: "amount",
type: "uint256",
},
],
name: "approve",
outputs: [
{
internalType: "bool",
name: "",
type: "bool",
},
],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "sender",
type: "address",
},
{
internalType: "address",
name: "recipient",
type: "address",
},
{
internalType: "uint256",
name: "amount",
type: "uint256",
},
],
name: "transferFrom",
outputs: [
{
internalType: "bool",
name: "",
type: "bool",
},
],
stateMutability: "nonpayable",
type: "function",
},
];
export class IERC20__factory {
static readonly abi = _abi;
static createInterface(): IERC20Interface {
return new utils.Interface(_abi) as IERC20Interface;
}
static connect(address: string, signerOrProvider: Signer | Provider): IERC20 {
return new Contract(address, _abi, signerOrProvider) as IERC20;
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,67 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import {Contract, Signer, utils} from "ethers";
import {Provider} from "@ethersproject/providers";
import type {TokenBridge, TokenBridgeInterface} from "../TokenBridge";
const _abi = [
{
inputs: [
{
internalType: "address",
name: "token",
type: "address",
},
{
internalType: "uint256",
name: "amount",
type: "uint256",
},
{
internalType: "uint16",
name: "recipientChain",
type: "uint16",
},
{
internalType: "bytes32",
name: "recipient",
type: "bytes32",
},
{
internalType: "uint256",
name: "arbiterFee",
type: "uint256",
},
{
internalType: "uint32",
name: "nonce",
type: "uint32",
},
],
name: "transferTokens",
outputs: [
{
internalType: "uint64",
name: "",
type: "uint64",
},
],
stateMutability: "payable",
type: "function",
},
];
export class TokenBridge__factory {
static readonly abi = _abi;
static createInterface(): TokenBridgeInterface {
return new utils.Interface(_abi) as TokenBridgeInterface;
}
static connect(
address: string,
signerOrProvider: Signer | Provider
): TokenBridge {
return new Contract(address, _abi, signerOrProvider) as TokenBridge;
}
}

View File

@ -0,0 +1,10 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
export type {IERC20} from "./IERC20";
export type {SimpleDex} from "./SimpleDex";
export type {TokenBridge} from "./TokenBridge";
export {IERC20__factory} from "./factories/IERC20__factory";
export {SimpleDex__factory} from "./factories/SimpleDex__factory";
export {TokenBridge__factory} from "./factories/TokenBridge__factory";

View File

@ -0,0 +1,74 @@
import {
Button,
CircularProgress,
makeStyles,
Typography,
} from "@material-ui/core";
import { ReactChild } from "react";
const useStyles = makeStyles((theme) => ({
root: {
position: "relative",
},
button: {
marginTop: theme.spacing(2),
textTransform: "none",
width: "100%",
},
loader: {
position: "absolute",
bottom: 0,
left: "50%",
marginLeft: -12,
marginBottom: 6,
},
error: {
marginTop: theme.spacing(1),
textAlign: "center",
},
}));
export default function ButtonWithLoader({
disabled,
onClick,
showLoader,
error,
children,
className,
}: {
disabled?: boolean;
onClick: () => void;
showLoader?: boolean;
error?: string;
children: ReactChild;
className?: string;
}) {
const classes = useStyles();
return (
<>
<div className={classes.root}>
<Button
color="primary"
variant="contained"
className={className || classes.button}
disabled={disabled}
onClick={onClick}
>
{children}
</Button>
{showLoader ? (
<CircularProgress
size={24}
color="inherit"
className={className || classes.loader}
/>
) : null}
</div>
{error ? (
<Typography color="error" className={classes.error}>
{error}
</Typography>
) : null}
</>
);
}

View File

@ -0,0 +1,48 @@
import {
ListItemIcon,
ListItemText,
makeStyles,
MenuItem,
OutlinedTextFieldProps,
TextField,
} from "@material-ui/core";
import clsx from "clsx";
import { ChainInfo, getDefaultNativeCurrencySymbol } from "../utils/consts";
const useStyles = makeStyles((theme) => ({
select: {
"& .MuiSelect-root": {
display: "flex",
alignItems: "center",
},
},
listItemIcon: {
minWidth: 40,
},
icon: {
height: 24,
maxWidth: 24,
},
}));
const createChainMenuItem = ({ id, name, logo }: ChainInfo, classes: any) => (
<MenuItem key={id} value={id}>
<ListItemIcon className={classes.listItemIcon}>
<img src={logo} alt={name} className={classes.icon} />
</ListItemIcon>
<ListItemText>{getDefaultNativeCurrencySymbol(id)}</ListItemText>
</MenuItem>
);
interface ChainSelectProps extends OutlinedTextFieldProps {
chains: ChainInfo[];
}
export default function ChainSelect({ chains, ...rest }: ChainSelectProps) {
const classes = useStyles();
return (
<TextField {...rest} className={clsx(classes.select, rest.className)}>
{chains.map((chain) => createChainMenuItem(chain, classes))}
</TextField>
);
}

View File

@ -0,0 +1,115 @@
import {
Card,
Dialog,
DialogContent,
ListItemIcon,
ListItemText,
makeStyles,
MenuItem,
Typography,
} from "@material-ui/core";
import { useCallback, useState } from "react";
import { ChainInfo, getDefaultNativeCurrencySymbol } from "../utils/consts";
const useStyles = makeStyles((theme) => ({
selectedCard: {
"&:hover": {
cursor: "pointer",
boxShadow: "inset 0 0 100px 100px rgba(255, 255, 255, 0.1)",
},
display: "flex",
alignItems: "center",
width: "max-content",
padding: "1rem",
background:
"linear-gradient(90deg, rgba(69,74,117,.2) 0%, rgba(138,146,178,.2) 33%, rgba(69,74,117,.5) 66%, rgba(98,104,143,.5) 100%), linear-gradient(45deg, rgba(153,69,255,.1) 0%, rgba(121,98,231,.1) 20%, rgba(0,209,140,.1) 100%)",
},
style2: {
background:
"linear-gradient(270deg, rgba(69,74,117,.2) 0%, rgba(138,146,178,.2) 33%, rgba(69,74,117,.5) 66%, rgba(98,104,143,.5) 100%), linear-gradient(45deg, rgba(153,69,255,.1) 0%, rgba(121,98,231,.1) 20%, rgba(0,209,140,.1) 100%)",
},
selectedSymbol: {
margin: "1rem",
},
listItemIcon: {
minWidth: 40,
},
icon: {
height: 50,
maxWidth: 50,
},
}));
const createChainMenuItem = (
{ id, name, logo }: ChainInfo,
classes: any,
handleSelect: any
) => {
return (
<MenuItem key={id} value={id} onClick={handleSelect}>
<ListItemIcon className={classes.listItemIcon}>
<img src={logo} alt={name} className={classes.icon} />
</ListItemIcon>
<ListItemText>{getDefaultNativeCurrencySymbol(id)}</ListItemText>
</MenuItem>
);
};
interface ChainSelectProps {
chains: ChainInfo[];
value: any;
onChange: any;
style2?: boolean;
}
export default function ChainSelect({
chains,
value,
onChange,
style2,
}: ChainSelectProps) {
const classes = useStyles();
const [open, setOpen] = useState(false);
const info = chains.find((x) => x.id === value);
// const handleClick = useCallback(() => {
// setOpen(true);
// }, []);
const handleClose = useCallback(() => {
setOpen(false);
}, []);
const handleSelect = useCallback(
(newValue) => {
onChange(newValue);
handleClose();
},
[handleClose, onChange]
);
return (
<>
<Card
//TODO re-enable
// onClick={handleClick}
raised
className={classes.selectedCard + (style2 ? " " + classes.style2 : "")}
>
<img
src={info && info.logo}
className={classes.icon}
alt={"coin logo"}
/>
<Typography variant="h6" className={classes.selectedSymbol}>
{info && getDefaultNativeCurrencySymbol(info.id)}
</Typography>
</Card>
<Dialog open={open} onClose={handleClose}>
<DialogContent>
{chains.map((chain) =>
createChainMenuItem(chain, classes, () => handleSelect(chain))
)}
</DialogContent>
</Dialog>
</>
);
}

View File

@ -0,0 +1,11 @@
import "../css/CircleLoader.css";
export default function CircleLoader() {
return (
<div id={"loaderContainer"}>
<div id={"circle"}>
<div id={"inner"}></div>
</div>
</div>
);
}

View File

@ -0,0 +1,29 @@
import { Typography } from "@material-ui/core";
import React from "react";
export default class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error(error, errorInfo);
}
render() {
if (this.state.hasError) {
return (
<Typography variant="h5" style={{ textAlign: "center", marginTop: 24 }}>
"An unexpected error has occurred. Please refresh the page."
</Typography>
);
}
return this.props.children;
}
}

View File

@ -0,0 +1,25 @@
import { Typography } from "@material-ui/core";
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
import ToggleConnectedButton from "./ToggleConnectedButton";
const EthereumSignerKey = () => {
const { connect, disconnect, signerAddress, providerError } =
useEthereumProvider();
return (
<>
<ToggleConnectedButton
connect={connect}
disconnect={disconnect}
connected={!!signerAddress}
pk={signerAddress || ""}
/>
{providerError ? (
<Typography variant="body2" color="error">
{providerError}
</Typography>
) : null}
</>
);
};
export default EthereumSignerKey;

View File

@ -0,0 +1,25 @@
import { IconButton } from "@material-ui/core";
import KeyboardArrowDownIcon from "@material-ui/icons/KeyboardArrowDown";
import SwapVertIcon from "@material-ui/icons/SwapVert";
import { useCallback, useState } from "react";
export default function HoverIcon({ onClick }: { onClick: () => void }) {
const [showSwap, setShowSwap] = useState(false);
const hovered = useCallback(() => {
setShowSwap(true);
}, []);
const unHovered = useCallback(() => {
setShowSwap(false);
}, []);
return (
<IconButton onClick={onClick} onMouseOver={hovered} onMouseOut={unHovered}>
{showSwap ? (
<SwapVertIcon fontSize={"large"} />
) : (
<KeyboardArrowDownIcon fontSize={"large"} />
)}
</IconButton>
);
}

View File

@ -0,0 +1,32 @@
import {
Button,
InputAdornment,
TextField,
TextFieldProps,
} from "@material-ui/core";
export default function NumberTextField({
onMaxClick,
...props
}: TextFieldProps & { onMaxClick?: () => void }) {
return (
<TextField
type="number"
{...props}
InputProps={{
endAdornment: onMaxClick ? (
<InputAdornment position="end">
<Button
onClick={onMaxClick}
disabled={props.disabled}
variant="outlined"
>
Max
</Button>
</InputAdornment>
) : undefined,
...(props?.InputProps || {}),
}}
></TextField>
);
}

View File

@ -0,0 +1,37 @@
import { makeStyles } from "@material-ui/core";
import DisconnectIcon from "@material-ui/icons/LinkOff";
import {
WalletDisconnectButton,
WalletMultiButton,
} from "@solana/wallet-adapter-material-ui";
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
const useStyles = makeStyles((theme) => ({
root: {
textAlign: "center",
margin: `${theme.spacing(1)}px auto`,
width: "100%",
maxWidth: 400,
},
disconnectButton: {
marginLeft: theme.spacing(1),
},
}));
const SolanaWalletKey = () => {
const classes = useStyles();
const wallet = useSolanaWallet();
return (
<div className={classes.root}>
<WalletMultiButton />
{wallet && (
<WalletDisconnectButton
startIcon={<DisconnectIcon />}
className={classes.disconnectButton}
/>
)}
</div>
);
};
export default SolanaWalletKey;

View File

@ -0,0 +1,51 @@
import { Button, makeStyles, Tooltip } from "@material-ui/core";
const useStyles = makeStyles((theme) => ({
button: {
display: "block",
margin: `${theme.spacing(1)}px auto`,
width: "100%",
maxWidth: 400,
},
}));
const ToggleConnectedButton = ({
connect,
disconnect,
connected,
pk,
}: {
connect(): any;
disconnect(): any;
connected: boolean;
pk: string;
}) => {
const classes = useStyles();
const is0x = pk.startsWith("0x");
return connected ? (
<Tooltip title={pk}>
<Button
color="secondary"
variant="contained"
size="small"
onClick={disconnect}
className={classes.button}
>
Disconnect {pk.substring(0, is0x ? 6 : 3)}...
{pk.substr(pk.length - (is0x ? 4 : 3))}
</Button>
</Tooltip>
) : (
<Button
color="primary"
variant="contained"
size="small"
onClick={connect}
className={classes.button}
>
Connect Wallet
</Button>
);
};
export default ToggleConnectedButton;

View File

@ -0,0 +1,158 @@
import detectEthereumProvider from "@metamask/detect-provider";
import { BigNumber, ethers } from "ethers";
import React, {
ReactChildren,
useCallback,
useContext,
useMemo,
useState,
} from "react";
export type Provider = ethers.providers.Web3Provider | undefined;
export type Signer = ethers.Signer | undefined;
interface IEthereumProviderContext {
connect(): void;
disconnect(): void;
provider: Provider;
chainId: number | undefined;
signer: Signer;
signerAddress: string | undefined;
providerError: string | null;
}
const EthereumProviderContext = React.createContext<IEthereumProviderContext>({
connect: () => {},
disconnect: () => {},
provider: undefined,
chainId: undefined,
signer: undefined,
signerAddress: undefined,
providerError: null,
});
export const EthereumProviderProvider = ({
children,
}: {
children: ReactChildren;
}) => {
const [providerError, setProviderError] = useState<string | null>(null);
const [provider, setProvider] = useState<Provider>(undefined);
const [chainId, setChainId] = useState<number | undefined>(undefined);
const [signer, setSigner] = useState<Signer>(undefined);
const [signerAddress, setSignerAddress] = useState<string | undefined>(
undefined
);
const connect = useCallback(() => {
setProviderError(null);
detectEthereumProvider()
.then((detectedProvider) => {
if (detectedProvider) {
const provider = new ethers.providers.Web3Provider(
// @ts-ignore
detectedProvider,
"any"
);
provider
.send("eth_requestAccounts", [])
.then(() => {
setProviderError(null);
setProvider(provider);
provider
.getNetwork()
.then((network) => {
setChainId(network.chainId);
})
.catch(() => {
setProviderError(
"An error occurred while getting the network"
);
});
const signer = provider.getSigner();
setSigner(signer);
signer
.getAddress()
.then((address) => {
setSignerAddress(address);
})
.catch(() => {
setProviderError(
"An error occurred while getting the signer address"
);
});
// TODO: try using ethers directly
// @ts-ignore
if (detectedProvider && detectedProvider.on) {
// @ts-ignore
detectedProvider.on("chainChanged", (chainId) => {
try {
setChainId(BigNumber.from(chainId).toNumber());
} catch (e) {}
});
// @ts-ignore
detectedProvider.on("accountsChanged", (accounts) => {
try {
const signer = provider.getSigner();
setSigner(signer);
signer
.getAddress()
.then((address) => {
setSignerAddress(address);
})
.catch(() => {
setProviderError(
"An error occurred while getting the signer address"
);
});
} catch (e) {}
});
}
})
.catch(() => {
setProviderError(
"An error occurred while requesting eth accounts"
);
});
} else {
setProviderError("Please install MetaMask");
}
})
.catch(() => {
setProviderError("Please install MetaMask");
});
}, []);
const disconnect = useCallback(() => {
setProviderError(null);
setProvider(undefined);
setChainId(undefined);
setSigner(undefined);
setSignerAddress(undefined);
}, []);
const contextValue = useMemo(
() => ({
connect,
disconnect,
provider,
chainId,
signer,
signerAddress,
providerError,
}),
[
connect,
disconnect,
provider,
chainId,
signer,
signerAddress,
providerError,
]
);
return (
<EthereumProviderContext.Provider value={contextValue}>
{children}
</EthereumProviderContext.Provider>
);
};
export const useEthereumProvider = () => {
return useContext(EthereumProviderContext);
};

View File

@ -0,0 +1,23 @@
import { WalletDialogProvider } from "@solana/wallet-adapter-material-ui";
import { useWallet, WalletProvider } from "@solana/wallet-adapter-react";
import {
getPhantomWallet,
getSolletWallet,
} from "@solana/wallet-adapter-wallets";
import React, { FC, useMemo } from "react";
export const SolanaWalletProvider: FC = (props) => {
// @solana/wallet-adapter-wallets includes all the adapters but supports tree shaking --
// Only the wallets you want to instantiate here will be compiled into your application
const wallets = useMemo(() => {
return [getPhantomWallet(), getSolletWallet()];
}, []);
return (
<WalletProvider wallets={wallets}>
<WalletDialogProvider>{props.children}</WalletDialogProvider>
</WalletProvider>
);
};
export const useSolanaWallet = useWallet;

View File

@ -0,0 +1,90 @@
:root {
--basis: linear-gradient(
160deg,
rgba(69, 74, 117, 0.473) 0%,
rgba(98, 104, 143, 0.445) 100%
),
linear-gradient(
45deg,
rgba(153, 69, 255, 0.411) 0%,
rgba(0, 209, 139, 0.404) 100%
);
/* --gradient1: rgb(26, 212, 150); */
/* --gradient2: rgb(176, 139, 221); */
--gradient1: rgb(117, 228, 187);
--gradient2: rgb(193, 164, 230);
}
#loaderContainer {
display: flex;
width: max-content;
height: max-content;
justify-content: center;
align-items: center;
}
@keyframes rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
@keyframes switch {
0% {
top: 50%;
transform: translateX(-50%) translateY(-50%);
width: 200px;
height: 200px;
box-shadow: 0 -130px 0 -75px var(--gradient1);
}
25% {
top: 50%;
transform: translateX(-50%) translateY(-50%);
width: 200px;
height: 200px;
box-shadow: 0 -130px 0 -75px var(--gradient1);
}
50% {
top: calc(100% - 55px);
width: 50px;
height: 50px;
box-shadow: 0 -130px 0 75px var(--gradient1);
transform: translateX(-50%) translateY(0);
}
75% {
top: calc(100% - 55px);
width: 50px;
height: 50px;
box-shadow: 0 -130px 0 75px var(--gradient1);
transform: translateX(-50%) translateY(0);
}
100% {
top: 50%;
transform: translateX(-50%) translateY(-50%);
width: 200px;
height: 200px;
box-shadow: 0 -130px 0 -75px var(--gradient1);
}
}
#circle {
width: 325px;
height: 325px;
display: block;
background: var(--basis);
border-radius: 500%;
position: relative;
animation: rotation 2s linear infinite;
}
#inner {
width: 200px;
height: 200px;
background: var(--gradient2);
position: absolute;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%);
border-radius: 100%;
box-shadow: 0 -130px 0 -75px #222;
animation: switch 8s ease-in-out infinite;
}

View File

@ -0,0 +1,101 @@
import {
approveEth,
ChainId,
getAllowanceEth,
isEVMChain,
} from "@certusone/wormhole-sdk";
import { BigNumber } from "ethers";
import { useEffect, useMemo, useState } from "react";
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
export default function useAllowance(
chainId: ChainId,
contractAddress?: string,
tokenAddress?: string,
transferAmount?: BigInt,
sourceIsNative?: boolean
) {
const [allowance, setAllowance] = useState<BigInt | null>(null);
const [isAllowanceFetching, setIsAllowanceFetching] = useState(false);
const [isApproveProcessing, setIsApproveProcessing] = useState(false);
const { signer } = useEthereumProvider();
const sufficientAllowance =
!isEVMChain(chainId) ||
sourceIsNative ||
(allowance && transferAmount && allowance >= transferAmount);
useEffect(() => {
let cancelled = false;
if (
isEVMChain(chainId) &&
tokenAddress &&
signer &&
!isApproveProcessing &&
contractAddress
) {
setIsAllowanceFetching(true);
getAllowanceEth(contractAddress, tokenAddress, signer).then(
(result) => {
if (!cancelled) {
setIsAllowanceFetching(false);
setAllowance(result.toBigInt());
}
},
(error) => {
if (!cancelled) {
console.error(error);
setIsAllowanceFetching(false);
//setError("Unable to retrieve allowance"); //TODO set an error
}
}
);
}
return () => {
cancelled = true;
};
}, [chainId, tokenAddress, signer, isApproveProcessing, contractAddress]);
const approveAmount: (amount: BigInt) => Promise<any> = useMemo(() => {
return !isEVMChain(chainId) || !tokenAddress || !signer || !contractAddress
? (amount: BigInt) => {
console.log(isEVMChain(chainId), tokenAddress, signer);
console.log("hit escape in approve amount");
return Promise.resolve();
}
: (amount: BigInt) => {
setIsApproveProcessing(true);
return approveEth(
contractAddress,
tokenAddress,
signer,
BigNumber.from(amount)
).then(
() => {
setIsApproveProcessing(false);
return Promise.resolve();
},
(error) => {
console.log(error);
setIsApproveProcessing(false);
return Promise.reject();
}
);
};
}, [chainId, tokenAddress, signer, contractAddress]);
return useMemo(
() => ({
sufficientAllowance,
approveAmount,
isAllowanceFetching,
isApproveProcessing,
}),
[
sufficientAllowance,
approveAmount,
isAllowanceFetching,
isApproveProcessing,
]
);
}

View File

@ -0,0 +1,103 @@
import { ChainId, CHAIN_ID_SOLANA, isEVMChain } from "@certusone/wormhole-sdk";
import { hexlify, hexStripZeros } from "@ethersproject/bytes";
import { useCallback, useMemo } from "react";
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
import { CLUSTER, getEvmChainId } from "../utils/consts";
const createWalletStatus = (
isReady: boolean,
statusMessage: string = "",
forceNetworkSwitch: () => void,
walletAddress?: string
) => ({
isReady,
statusMessage,
forceNetworkSwitch,
walletAddress,
});
function useIsWalletReady(
chainId: ChainId,
enableNetworkAutoswitch: boolean = true
): {
isReady: boolean;
statusMessage: string;
walletAddress?: string;
forceNetworkSwitch: () => void;
} {
const autoSwitch = enableNetworkAutoswitch;
const solanaWallet = useSolanaWallet();
const solPK = solanaWallet?.publicKey;
const {
provider,
signerAddress,
chainId: evmChainId,
} = useEthereumProvider();
const hasEthInfo = !!provider && !!signerAddress;
const correctEvmNetwork = getEvmChainId(chainId);
const hasCorrectEvmNetwork = evmChainId === correctEvmNetwork;
const forceNetworkSwitch = useCallback(() => {
if (provider && correctEvmNetwork) {
if (!isEVMChain(chainId)) {
return;
}
try {
provider.send("wallet_switchEthereumChain", [
{ chainId: hexStripZeros(hexlify(correctEvmNetwork)) },
]);
} catch (e) {}
}
}, [provider, correctEvmNetwork, chainId]);
return useMemo(() => {
if (chainId === CHAIN_ID_SOLANA && solPK) {
return createWalletStatus(
true,
undefined,
forceNetworkSwitch,
solPK.toString()
);
}
if (isEVMChain(chainId) && hasEthInfo && signerAddress) {
if (hasCorrectEvmNetwork) {
return createWalletStatus(
true,
undefined,
forceNetworkSwitch,
signerAddress
);
} else {
if (provider && correctEvmNetwork && autoSwitch) {
forceNetworkSwitch();
}
return createWalletStatus(
false,
`Wallet is not connected to ${CLUSTER}. Expected Chain ID: ${correctEvmNetwork}`,
forceNetworkSwitch,
undefined
);
}
}
return createWalletStatus(
false,
"Wallet not connected",
forceNetworkSwitch,
undefined
);
}, [
chainId,
autoSwitch,
forceNetworkSwitch,
solPK,
hasEthInfo,
correctEvmNetwork,
hasCorrectEvmNetwork,
provider,
signerAddress,
]);
}
export default useIsWalletReady;

View File

@ -0,0 +1,85 @@
import {
ChainId,
getEmitterAddressEth,
uint8ArrayToHex,
} from "@certusone/wormhole-sdk";
import getSignedVAAWithRetry from "@certusone/wormhole-sdk/lib/esm/rpc/getSignedVAAWithRetry";
import axios from "axios";
import { useEffect, useMemo, useState } from "react";
import {
getTokenBridgeAddressForChain,
WORMHOLE_RPC_HOSTS,
} from "../utils/consts";
const RELAYER_ENDPOINT_URL = "http://localhost:3111/relay";
export type RelayRequest = {
signedVaa: string;
chainId: ChainId; //This is the target chain
unwrapNative: boolean;
};
export default function useRestRelayer(
sourceChain: ChainId,
sourceSequence: string,
targetChain: ChainId
) {
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const [complete, setComplete] = useState(false);
useEffect(() => {
if (!sourceChain || !sourceSequence || !targetChain) {
setLoading(false);
setError("");
setComplete(false);
return;
}
let cancelled = false;
console.log(
"Relay action triggered: ",
sourceChain,
targetChain,
sourceSequence
);
setLoading(true);
setError("");
getSignedVAAWithRetry(
WORMHOLE_RPC_HOSTS,
sourceChain,
getEmitterAddressEth(getTokenBridgeAddressForChain(sourceChain)),
sourceSequence
)
.then((VAA) => {
const signedVaaHex = uint8ArrayToHex(VAA.vaaBytes);
console.log("got Vaa with retry.");
if (VAA) {
axios
.post(RELAYER_ENDPOINT_URL, {
signedVAA: signedVaaHex,
chainId: targetChain,
unwrapNative: true,
})
.then((result) => {
if (!cancelled) {
setComplete(true);
setLoading(false);
}
});
}
})
.catch((error) => {
if (!cancelled) {
console.error(error);
setError("Unable to relay the VAA");
setLoading(false);
}
});
}, [sourceChain, sourceSequence, targetChain]);
const output = useMemo(() => {
return { isLoading: loading, error, isComplete: complete };
}, [loading, error, complete]);
return output;
}

View File

@ -0,0 +1,3 @@
<svg width="26" height="19" viewBox="0 0 26 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M22.3967 2.10679C20.1472 0.316323 16.588 0.0128823 16.4354 0.00226469C16.3194 -0.00717259 16.2034 0.0192786 16.1034 0.0779529C16.0033 0.136627 15.9242 0.224619 15.877 0.329735C15.8091 0.48074 15.7515 0.636067 15.7046 0.794675C17.1928 1.04335 19.0207 1.5435 20.6745 2.55832C20.7376 2.59703 20.7924 2.64765 20.8357 2.70728C20.879 2.76692 20.91 2.8344 20.9269 2.90588C20.9438 2.97736 20.9464 3.05144 20.9343 3.12388C20.9223 3.19633 20.896 3.26572 20.8568 3.3281C20.8177 3.39047 20.7665 3.44462 20.7061 3.48743C20.6458 3.53025 20.5776 3.5609 20.5053 3.57763C20.433 3.59437 20.358 3.59686 20.2848 3.58497C20.2115 3.57307 20.1413 3.54703 20.0782 3.50832C17.2352 1.76311 13.6845 1.6765 13 1.6765C12.3161 1.6765 8.76313 1.76367 5.92178 3.50776C5.85869 3.54647 5.78851 3.57252 5.71523 3.58441C5.64196 3.5963 5.56704 3.59381 5.49474 3.57707C5.34872 3.54328 5.22227 3.45352 5.1432 3.32754C5.06412 3.20156 5.0389 3.04969 5.07309 2.90532C5.10727 2.76096 5.19806 2.63594 5.32548 2.55776C6.9793 1.54573 8.80722 1.04335 10.2954 0.79691C10.2095 0.519734 10.1315 0.344823 10.1259 0.329735C10.0788 0.223877 9.99935 0.135308 9.89863 0.0765457C9.79792 0.0177834 9.68107 -0.00820096 9.56461 0.00226469C9.412 0.0134411 5.85283 0.316323 3.57217 2.13082C2.38239 3.21941 0 9.58495 0 15.0882C0 15.1866 0.0265652 15.2805 0.0751739 15.3654C1.7177 18.221 6.20213 18.967 7.22404 19H7.24157C7.33075 19.0002 7.41872 18.9796 7.49829 18.9398C7.57786 18.8999 7.64677 18.8421 7.69939 18.7709L8.73204 17.3649C5.94383 16.6535 4.51948 15.4442 4.43809 15.3721C4.32566 15.274 4.25726 15.1358 4.24793 14.9878C4.2386 14.8398 4.28911 14.6942 4.38835 14.5831C4.48758 14.4719 4.62742 14.4043 4.77709 14.3951C4.92676 14.3858 5.07401 14.4358 5.18643 14.5339C5.21922 14.5646 7.84239 16.7647 13 16.7647C18.1661 16.7647 20.7893 14.5557 20.8158 14.5339C20.9286 14.4365 21.0759 14.3873 21.2253 14.3971C21.3747 14.407 21.5141 14.4751 21.6128 14.5864C21.6616 14.6413 21.6991 14.7052 21.723 14.7744C21.7468 14.8436 21.7567 14.9167 21.752 14.9897C21.7473 15.0627 21.728 15.134 21.6954 15.1996C21.6628 15.2652 21.6174 15.3238 21.5619 15.3721C21.4805 15.4442 20.0562 16.6535 17.268 17.3649L18.3012 18.7709C18.3537 18.842 18.4226 18.8998 18.502 18.9396C18.5815 18.9795 18.6693 19.0001 18.7584 19H18.7754C19.7984 18.967 24.2823 18.2204 25.9254 15.3654C25.9734 15.2805 26 15.1866 26 15.0882C26 9.58495 23.6176 3.21997 22.3967 2.10679V2.10679ZM9.32609 12.8529C8.23296 12.8529 7.34783 11.8527 7.34783 10.6177C7.34783 9.38266 8.23296 8.38237 9.32609 8.38237C10.4192 8.38237 11.3043 9.38266 11.3043 10.6177C11.3043 11.8527 10.4192 12.8529 9.32609 12.8529ZM16.6739 12.8529C15.5808 12.8529 14.6957 11.8527 14.6957 10.6177C14.6957 9.38266 15.5808 8.38237 16.6739 8.38237C17.767 8.38237 18.6522 9.38266 18.6522 10.6177C18.6522 11.8527 17.767 12.8529 16.6739 12.8529Z" fill="#5A5A66"/>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,3 @@
<svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19.5915 5.36061C18.6526 3.71151 17.3789 2.40591 15.7703 1.44353C14.1615 0.481094 12.4051 0 10.4999 0C8.59497 0 6.838 0.481241 5.22948 1.44353C3.62072 2.40586 2.34716 3.71151 1.40822 5.36061C0.469422 7.00966 0 8.81044 0 10.7629C0 13.1082 0.667524 15.2172 2.00291 17.0904C3.33815 18.9637 5.06308 20.26 7.17756 20.9794C7.42369 21.0262 7.6059 20.9933 7.72436 20.8814C7.84288 20.7694 7.90207 20.6291 7.90207 20.4611C7.90207 20.4331 7.89972 20.1809 7.89517 19.7043C7.89048 19.2278 7.88828 18.812 7.88828 18.4573L7.57381 18.513C7.37332 18.5507 7.12038 18.5666 6.81502 18.5621C6.5098 18.5577 6.19294 18.525 5.86487 18.464C5.53667 18.4036 5.2314 18.2634 4.94882 18.0439C4.6664 17.8243 4.4659 17.5369 4.34738 17.1821L4.21067 16.8596C4.11954 16.6449 3.97608 16.4064 3.78008 16.145C3.58409 15.8833 3.38589 15.7059 3.18539 15.6125L3.08967 15.5422C3.02589 15.4955 2.9667 15.4392 2.91197 15.3739C2.85728 15.3086 2.81634 15.2432 2.789 15.1777C2.76161 15.1122 2.7843 15.0584 2.85733 15.0163C2.93035 14.9741 3.06233 14.9536 3.25382 14.9536L3.52715 14.9955C3.70945 15.033 3.93494 15.1448 4.20392 15.3318C4.47275 15.5186 4.69374 15.7615 4.86694 16.0603C5.07668 16.4435 5.32937 16.7355 5.62573 16.9364C5.92186 17.1373 6.22042 17.2376 6.52115 17.2376C6.82187 17.2376 7.0816 17.2142 7.30043 17.1677C7.51903 17.121 7.72412 17.0507 7.91562 16.9574C7.99765 16.3311 8.22099 15.85 8.58544 15.5138C8.06598 15.4578 7.59895 15.3735 7.18412 15.2615C6.76953 15.1492 6.3411 14.9671 5.89911 14.7146C5.45689 14.4625 5.09004 14.1494 4.79846 13.7758C4.50684 13.4021 4.26751 12.9114 4.0808 12.3043C3.894 11.6969 3.80058 10.9962 3.80058 10.2021C3.80058 9.07138 4.16068 8.10919 4.88073 7.31498C4.54343 6.4649 4.57527 5.51194 4.97636 4.4562C5.24069 4.37201 5.63268 4.43519 6.15214 4.64533C6.6717 4.85556 7.0521 5.03566 7.29373 5.18498C7.53536 5.33425 7.72896 5.46075 7.87482 5.56334C8.72263 5.32051 9.59755 5.19907 10.4998 5.19907C11.4021 5.19907 12.2772 5.32051 13.125 5.56334L13.6445 5.22714C13.9998 5.00282 14.4193 4.79725 14.9021 4.61038C15.3852 4.4236 15.7546 4.37216 16.0099 4.45634C16.4199 5.51214 16.4564 6.46505 16.119 7.31513C16.839 8.10934 17.1993 9.07177 17.1993 10.2022C17.1993 10.9963 17.1055 11.6992 16.919 12.3113C16.7323 12.9234 16.4909 13.4136 16.1947 13.7828C15.8983 14.152 15.5291 14.4627 15.0871 14.7148C14.645 14.9671 14.2164 15.1492 13.8019 15.2614C13.3871 15.3736 12.92 15.4579 12.4006 15.514C12.8744 15.9343 13.1113 16.5977 13.1113 17.5039V20.4607C13.1113 20.6287 13.1683 20.7689 13.2823 20.881C13.3963 20.9929 13.5762 21.0258 13.8223 20.9789C15.9371 20.2597 17.662 18.9633 18.9972 17.09C20.3322 15.2168 21 13.1078 21 10.7625C20.9995 8.81029 20.5299 7.00966 19.5915 5.36061Z" fill="#5A5A66"/>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1 @@
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" fill="#5A5A66"><path d="M2.846 6.887c.03-.295-.083-.586-.303-.784l-2.24-2.7v-.403h6.958l5.378 11.795 4.728-11.795h6.633v.403l-1.916 1.837c-.165.126-.247.333-.213.538v13.498c-.034.204.048.411.213.537l1.871 1.837v.403h-9.412v-.403l1.939-1.882c.19-.19.19-.246.19-.537v-10.91l-5.389 13.688h-.728l-6.275-13.688v9.174c-.052.385.076.774.347 1.052l2.521 3.058v.404h-7.148v-.404l2.521-3.058c.27-.279.39-.67.325-1.052v-10.608z"/></svg>

After

Width:  |  Height:  |  Size: 509 B

View File

@ -0,0 +1,3 @@
<svg width="24px" height="24px" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
<path fill="#5A5A66" d="M12,0c-6.626,0 -12,5.372 -12,12c0,6.627 5.374,12 12,12c6.627,0 12,-5.373 12,-12c0,-6.628 -5.373,-12 -12,-12Zm3.224,17.871c0.188,0.133 0.43,0.166 0.646,0.085c0.215,-0.082 0.374,-0.267 0.422,-0.491c0.507,-2.382 1.737,-8.412 2.198,-10.578c0.035,-0.164 -0.023,-0.334 -0.151,-0.443c-0.129,-0.109 -0.307,-0.14 -0.465,-0.082c-2.446,0.906 -9.979,3.732 -13.058,4.871c-0.195,0.073 -0.322,0.26 -0.316,0.467c0.007,0.206 0.146,0.385 0.346,0.445c1.381,0.413 3.193,0.988 3.193,0.988c0,0 0.847,2.558 1.288,3.858c0.056,0.164 0.184,0.292 0.352,0.336c0.169,0.044 0.348,-0.002 0.474,-0.121c0.709,-0.669 1.805,-1.704 1.805,-1.704c0,0 2.084,1.527 3.266,2.369Zm-6.423,-5.062l0.98,3.231l0.218,-2.046c0,0 3.783,-3.413 5.941,-5.358c0.063,-0.057 0.071,-0.153 0.019,-0.22c-0.052,-0.067 -0.148,-0.083 -0.219,-0.037c-2.5,1.596 -6.939,4.43 -6.939,4.43Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,3 @@
<svg width="22" height="18" viewBox="0 0 22 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.8361 2.09102C21.0578 2.4457 20.2199 2.68554 19.3409 2.79271C20.2385 2.24157 20.9273 1.36765 21.2518 0.326603C20.4113 0.83692 19.4826 1.20818 18.4905 1.4072C17.6998 0.540936 16.5697 0 15.319 0C12.9207 0 10.975 1.99662 10.975 4.4589C10.975 4.80846 11.0123 5.14783 11.0869 5.47443C7.47527 5.28816 4.27389 3.51481 2.12928 0.813956C1.75506 1.47482 1.54122 2.24157 1.54122 3.05808C1.54122 4.60434 2.30831 5.96944 3.47448 6.76936C2.76334 6.74767 2.09199 6.54482 1.50517 6.21311V6.26797C1.50517 8.42916 3.00329 10.2319 4.99249 10.6401C4.62822 10.7447 4.24406 10.797 3.84746 10.797C3.56773 10.797 3.29421 10.7702 3.0294 10.7179C3.58265 12.4887 5.18644 13.7786 7.08862 13.813C5.60169 15.0097 3.72686 15.7229 1.69166 15.7229C1.34106 15.7229 0.994193 15.7025 0.654785 15.6616C2.5781 16.9247 4.8632 17.6634 7.31613 17.6634C15.3103 17.6634 19.6803 10.8698 19.6803 4.97687C19.6803 4.78295 19.6766 4.58903 19.6691 4.39893C20.5182 3.76997 21.2555 2.98536 21.8361 2.09102" fill="#5A5A66"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2500.01 2500"><defs><style>.cls-1{fill:#f3ba2f;}</style></defs><title>bi</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M764.48,1050.52,1250,565l485.75,485.73,282.5-282.5L1250,0,482,768l282.49,282.5M0,1250,282.51,967.45,565,1249.94,282.49,1532.45Zm764.48,199.51L1250,1935l485.74-485.72,282.65,282.35-.14.15L1250,2500,482,1732l-.4-.4,282.91-282.12M1935,1250.12l282.51-282.51L2500,1250.1,2217.5,1532.61Z"/><path class="cls-1" d="M1536.52,1249.85h.12L1250,963.19,1038.13,1175h0l-24.34,24.35-50.2,50.21-.4.39.4.41L1250,1536.81l286.66-286.66.14-.16-.26-.14"/></g></g></svg>

After

Width:  |  Height:  |  Size: 678 B

View File

@ -0,0 +1,12 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20.3025 0L9.67897 6.12683L13.5847 8.39024L20.3025 4.52683L27.0204 8.39024L30.9261 6.12683L20.3025 0Z" fill="#F0B90B"/>
<path d="M27.0204 11.5902L30.9261 13.8537V18.3805L24.2083 22.2439V29.9707L20.3025 32.2341L16.3968 29.9707V22.2439L9.67897 18.3805V13.8537L13.5847 11.5902L20.3025 15.4537L27.0204 11.5902Z" fill="#F0B90B"/>
<path d="M30.9261 21.5805V26.1073L27.0204 28.3707V23.8439L30.9261 21.5805Z" fill="#F0B90B"/>
<path d="M26.9814 31.5707L33.6992 27.7073V19.9805L37.605 17.7171V29.9707L26.9814 36.0976V31.5707Z" fill="#F0B90B"/>
<path d="M33.6992 12.2537L29.7935 9.99025L33.6992 7.72683L37.605 9.99025V14.5171L33.6992 16.7805V12.2537Z" fill="#F0B90B"/>
<path d="M16.3968 37.7366V33.2098L20.3025 35.4732L24.2083 33.2098V37.7366L20.3025 40L16.3968 37.7366Z" fill="#F0B90B"/>
<path d="M13.5847 28.3707L9.67897 26.1073V21.5805L13.5847 23.8439V28.3707Z" fill="#F0B90B"/>
<path d="M20.3025 12.2537L16.3968 9.99025L20.3025 7.72683L24.2083 9.99025L20.3025 12.2537Z" fill="#F0B90B"/>
<path d="M10.8116 9.99025L6.90586 12.2537V16.7805L3.00012 14.5171V9.99025L6.90586 7.72683L10.8116 9.99025Z" fill="#F0B90B"/>
<path d="M3.00012 17.7171L6.90586 19.9805V27.7073L13.6237 31.5707V36.0976L3.00012 29.9707V17.7171Z" fill="#F0B90B"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1920 1920" enable-background="new 0 0 1920 1920" xml:space="preserve">
<g>
<polygon fill="#8A92B2" points="959.8,80.7 420.1,976.3 959.8,731 "/>
<polygon fill="#62688F" points="959.8,731 420.1,976.3 959.8,1295.4 "/>
<polygon fill="#62688F" points="1499.6,976.3 959.8,80.7 959.8,731 "/>
<polygon fill="#454A75" points="959.8,1295.4 1499.6,976.3 959.8,731 "/>
<polygon fill="#8A92B2" points="420.1,1078.7 959.8,1839.3 959.8,1397.6 "/>
<polygon fill="#62688F" points="959.8,1397.6 959.8,1839.3 1499.9,1078.7 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 807 B

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 38.4 33.5" style="enable-background:new 0 0 38.4 33.5;" xml:space="preserve">
<style type="text/css">
.st0{fill:#8247E5;}
</style>
<g>
<path class="st0" d="M29,10.2c-0.7-0.4-1.6-0.4-2.4,0L21,13.5l-3.8,2.1l-5.5,3.3c-0.7,0.4-1.6,0.4-2.4,0L5,16.3
c-0.7-0.4-1.2-1.2-1.2-2.1v-5c0-0.8,0.4-1.6,1.2-2.1l4.3-2.5c0.7-0.4,1.6-0.4,2.4,0L16,7.2c0.7,0.4,1.2,1.2,1.2,2.1v3.3l3.8-2.2V7
c0-0.8-0.4-1.6-1.2-2.1l-8-4.7c-0.7-0.4-1.6-0.4-2.4,0L1.2,5C0.4,5.4,0,6.2,0,7v9.4c0,0.8,0.4,1.6,1.2,2.1l8.1,4.7
c0.7,0.4,1.6,0.4,2.4,0l5.5-3.2l3.8-2.2l5.5-3.2c0.7-0.4,1.6-0.4,2.4,0l4.3,2.5c0.7,0.4,1.2,1.2,1.2,2.1v5c0,0.8-0.4,1.6-1.2,2.1
L29,28.8c-0.7,0.4-1.6,0.4-2.4,0l-4.3-2.5c-0.7-0.4-1.2-1.2-1.2-2.1V21l-3.8,2.2v3.3c0,0.8,0.4,1.6,1.2,2.1l8.1,4.7
c0.7,0.4,1.6,0.4,2.4,0l8.1-4.7c0.7-0.4,1.2-1.2,1.2-2.1V17c0-0.8-0.4-1.6-1.2-2.1L29,10.2z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,15 @@
<svg width="96" height="84" viewBox="0 0 96 84" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.368 64.052C17.669 63.7209 18.0359 63.4563 18.445 63.2752C18.8541 63.094 19.2966 63.0003 19.744 63L93.056 63.06C93.3682 63.0606 93.6735 63.1519 93.9349 63.3228C94.1962 63.4936 94.4023 63.7367 94.5281 64.0224C94.6539 64.3081 94.694 64.6242 94.6436 64.9323C94.5932 65.2404 94.4544 65.5273 94.244 65.758L78.632 82.948C78.3308 83.2793 77.9637 83.5441 77.5542 83.7252C77.1447 83.9064 76.7018 84 76.254 84L2.94405 83.94C2.63185 83.9394 2.32654 83.8481 2.06523 83.6772C1.80391 83.5064 1.59783 83.2634 1.47202 82.9776C1.3462 82.6919 1.30607 82.3758 1.35649 82.0677C1.40691 81.7596 1.54572 81.4727 1.75605 81.242L17.368 64.052ZM94.244 49.742C94.4544 49.9727 94.5932 50.2596 94.6436 50.5677C94.694 50.8758 94.6539 51.1919 94.5281 51.4776C94.4023 51.7634 94.1962 52.0064 93.9349 52.1772C93.6735 52.3481 93.3682 52.4394 93.056 52.44L19.746 52.5C19.2983 52.5 18.8554 52.4064 18.4459 52.2252C18.0364 52.0441 17.6693 51.7793 17.368 51.448L1.75605 34.248C1.54572 34.0173 1.40691 33.7304 1.35649 33.4223C1.30607 33.1142 1.3462 32.7981 1.47202 32.5124C1.59783 32.2266 1.80391 31.9836 2.06523 31.8128C2.32654 31.6419 2.63185 31.5506 2.94405 31.55L76.256 31.49C76.7035 31.4903 77.146 31.584 77.5551 31.7652C77.9642 31.9463 78.3311 32.2109 78.632 32.542L94.244 49.742ZM17.368 1.052C17.669 0.720916 18.0359 0.456328 18.445 0.275176C18.8541 0.0940234 19.2966 0.000298083 19.744 0L93.056 0.06C93.3682 0.0606347 93.6735 0.151917 93.9349 0.322758C94.1962 0.493599 94.4023 0.736647 94.5281 1.02238C94.6539 1.30811 94.694 1.62423 94.6436 1.93234C94.5932 2.24044 94.4544 2.52728 94.244 2.758L78.632 19.948C78.3308 20.2793 77.9637 20.5441 77.5542 20.7252C77.1447 20.9064 76.7018 21 76.254 21L2.94405 20.94C2.63185 20.9394 2.32654 20.8481 2.06523 20.6772C1.80391 20.5064 1.59783 20.2634 1.47202 19.9776C1.3462 19.6919 1.30607 19.3758 1.35649 19.0677C1.40691 18.7596 1.54572 18.4727 1.75605 18.242L17.368 1.052Z" fill="url(#paint0_linear)"/>
</g>
<defs>
<linearGradient id="paint0_linear" x1="4.16805" y1="85.832" x2="91.8321" y2="-1.832" gradientUnits="userSpaceOnUse">
<stop stop-color="#9945FF"/>
<stop offset="0.2" stop-color="#7962E7"/>
<stop offset="1" stop-color="#00D18C"/>
</linearGradient>
<clipPath id="clip0">
<rect width="96" height="84" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 288.9 274" style="enable-background:new 0 0 288.9 274;" xml:space="preserve">
<style type="text/css">
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#2849A9;}
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#5795ED;}
</style>
<path class="st0" d="M151.1,0.3c33.7,0,64.9,12.1,88.7,32.9c31.8,24.5,22.6,113.9-9.6,90.3c-70.8-0.3-202.4-38.2-163.2-90.3
c4-5.3,9-9.6,14.5-13.7h-0.3c0.9-0.5,1.9-1,2.8-1.6c0.9-0.5,1.9-1.1,2.8-1.6h0c2.8-1.6,5.6-3.1,8.7-4.3
C112.5,4.6,131.3,0.3,151.1,0.3z M174.9,272.8c-14.2,0.9-42.6-21.4-50.7-50.9c-15.1-55.9,107.2-84.4,118.7-85.4
c31.2,0.9,38.9,38.2,16.1,76.7C229.3,262.6,175.5,272.8,174.9,272.8z"/>
<path class="st1" d="M14.8,77.9c9.9,2.8,70.5-16.5,88.4-43.8c0.3-0.3,14.2-21.7-12.7-22c-3.1,0-11.7,0.3-20.1,5.3
c-4,2.5-7.7,5-11.4,7.8c-5.8,4.3-11.3,9.5-16.5,14.4h0l-0.2,0.2c-5.3,5-10.2,10.9-14.5,16.8c-4.3,5.9-8.3,12.4-11.7,18.9
c-0.2,0.5-0.4,0.9-0.6,1.2C15.2,77,15,77.4,14.8,77.9z M86.5,272.8c1.9-2.8,3.1-36.6,1.9-45.3c-1.2-8.7-4-26.4-20.7-55.6
c-2.8-4.7-16.1-26.4-26-39.7c-5.6-7.8-11.7-15-17.8-22.3h0c-5.1-6-10.2-12.1-15-18.4c-0.3,0.8-0.5,1.5-0.8,2.2s-0.5,1.4-0.8,2.2
c-2.5,7.1-4.3,14.6-5.6,22.4S0,133.8,0,141.8c0,8.1,0.6,15.8,1.9,23.6s3.4,15.2,5.6,22.4c2.2,7.1,5.3,14.3,8.7,20.8
s7.4,13,11.7,18.9c4.3,5.9,9.3,11.5,14.5,16.8c4.9,5.3,10.8,10.2,16.7,14.6h0h0c4.6,3.1,9.3,6.2,13.9,9c8.5,5,11.7,5,13.4,5
C86.4,272.8,86.4,272.8,86.5,272.8z M288.9,141.8c0,18.9-3.7,36.9-10.2,53.4c-15.7,17-115.3-20.7-130.8-26.6c-1.2-0.5-2-0.7-2-0.8
c-15.8-6.8-63.3-27.9-67.7-60.8c-6.2-47.5,89.6-80.7,131.9-82c4.9,0,20.4,0.3,29.4,7.5C269.8,59.2,288.9,98.4,288.9,141.8z
M188.8,260.1c-3.7,12.1,10.2,16.5,22.6,10.6c24.7-13,45.1-33.2,59-57.1c0.9-1.2,0-2.5-1.5-2.2C255.6,212.6,195.6,236.5,188.8,260.1
z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 34 34" style="enable-background:new 0 0 34 34;" xml:space="preserve">
<style type="text/css">
.st0{fill:#00EFD8;}
.st1{fill:#FD3503;}
.st2{fill:#0074FF;}
</style>
<g>
<path class="st0" d="M17,15.6c-5.2,0-10.1,2-13.8,5.6C3,21.3,3,21.5,3,21.7v4.6c0,0.2,0,0.4,0.2,0.5C3.3,26.9,3.5,27,3.7,27h4.8
c0.2,0,0.3-0.1,0.5-0.2c0-0.1,0.1-0.3,0.1-0.4c0.2-2,1.1-3.8,2.5-5.2c1.5-1.3,3.4-2.1,5.4-2.1s3.9,0.7,5.4,2.1
c1.5,1.3,2.4,3.2,2.5,5.2c0,0.2,0.1,0.3,0.2,0.5c0.1,0,0.3,0.1,0.5,0.1h4.8c0.1,0,0.2,0,0.3-0.1c0.1,0,0.2-0.1,0.2-0.1
c0-0.1,0.1-0.1,0.1-0.2s0-0.2,0-0.3v-4.6c0-0.2-0.1-0.4-0.2-0.5C27.1,17.6,22.2,15.6,17,15.6z"/>
<path class="st1" d="M3.9,7C3.9,7,3.7,7,3.7,7.1C3.6,7.1,3.6,7.1,3.5,7.2S3.4,7.4,3.4,7.5c0,0.1,0,0.2,0,0.2c1.1,3,1.7,6.1,1.7,9.3
c0,3.2-0.6,6.3-1.7,9.3c0,0.1-0.1,0.1,0,0.2c0,0.1,0.1,0.1,0.1,0.2c0,0.1,0.1,0.1,0.2,0.2C3.7,27,3.8,27,3.9,27h4.2
c0.1,0,0.2,0,0.2-0.1c0.1-0.1,0.1-0.1,0.2-0.2c0.1-0.1,0.1-0.1,0.1-0.2c0-0.1,0-0.2-0.1-0.2c-1.1-3-1.7-6.1-1.7-9.3
c0-3.2,0.6-6.3,1.7-9.3c0-0.1,0.1-0.1,0.1-0.2c0-0.1-0.1-0.1-0.1-0.2C8.5,7.2,8.4,7.1,8.4,7.1C8.3,7,8.3,7,8.1,7C8.1,7,3.9,7,3.9,7
z"/>
<path class="st2" d="M25.9,7c-0.1,0-0.2,0-0.2,0.1c-0.1,0.1-0.1,0.1-0.2,0.2s-0.1,0.1-0.1,0.2c0,0.1,0,0.2,0,0.2
c1.1,3,1.7,6.1,1.7,9.3c0,3.2-0.6,6.3-1.7,9.3c0,0.1-0.1,0.1,0,0.2c0,0.1,0.1,0.1,0.1,0.2c0,0.1,0.1,0.1,0.2,0.2s0.1,0.1,0.2,0.1
h4.2c0.1,0,0.2,0,0.2-0.1c0.1-0.1,0.1-0.1,0.2-0.2c0.1-0.1,0.1-0.1,0.1-0.2c0-0.1,0-0.2-0.1-0.2c-1.1-3-1.7-6.1-1.7-9.3
c0-3.2,0.6-6.3,1.7-9.3c0-0.1,0.1-0.1,0.1-0.2c0-0.1-0.1-0.1-0.1-0.2c-0.1-0.1-0.1-0.1-0.2-0.2C30.3,7,30.2,7,30.1,7
C30.1,7,25.9,7,25.9,7z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,8 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 73.18 86.09">
<defs>
<style>.cls-1{fill:red;}.cls-2{fill:#0073ff;}.cls-3{fill:#00f3d7;}</style>
</defs>
<path class="cls-1" d="M30.29,43.05A47.76,47.76,0,0,0,16.72,9.63a1.7,1.7,0,0,0-1.2-.5H4.34A1.67,1.67,0,0,0,2.67,10.8V22.29A1.69,1.69,0,0,0,4.24,24a19.15,19.15,0,0,1,0,38.18A1.68,1.68,0,0,0,2.67,63.8V75.29A1.68,1.68,0,0,0,4.34,77H15.52a1.66,1.66,0,0,0,1.2-.51A47.75,47.75,0,0,0,30.29,43.05Z"/>
<path class="cls-2" d="M70.51,63.8a1.68,1.68,0,0,0-1.57-1.66,19.15,19.15,0,0,1,0-38.18,1.69,1.69,0,0,0,1.57-1.67V10.8a1.67,1.67,0,0,0-1.67-1.67H57.66a1.7,1.7,0,0,0-1.2.5,47.93,47.93,0,0,0,0,66.83,1.66,1.66,0,0,0,1.2.51H68.84a1.68,1.68,0,0,0,1.67-1.68Z"/>
<path class="cls-3" d="M28.06,3.14a1.89,1.89,0,0,0-1.75,2.58,102.89,102.89,0,0,1,7.05,37.33,102.87,102.87,0,0,1-7,37.32A1.89,1.89,0,0,0,28.06,83h17a1.88,1.88,0,0,0,1.74-2.58,102.33,102.33,0,0,1,0-74.65,1.88,1.88,0,0,0-1.74-2.58Z"/>
</svg>

After

Width:  |  Height:  |  Size: 993 B

View File

@ -0,0 +1,148 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 351.6 87.2" style="enable-background:new 0 0 351.6 87.2;" xml:space="preserve">
<style type="text/css">
.st0{fill:#00EFD8;}
.st1{fill:#FD3503;}
.st2{fill:#0074FF;}
.st3{enable-background:new ;}
.st4{fill:#FFFFFF;}
.st5{fill:#FF0000;}
.st6{fill:#0073FF;}
.st7{fill:#00F3D7;}
</style>
<g>
<path class="st0" d="M39.7,39.9c-12.6,0-24.5,4.8-33.4,13.6c-0.5,0.2-0.5,0.7-0.5,1.2v11.1c0,0.5,0,1,0.5,1.2
c0.2,0.2,0.7,0.5,1.2,0.5h11.6c0.5,0,0.7-0.2,1.2-0.5c0-0.2,0.2-0.7,0.2-1c0.5-4.8,2.7-9.2,6.1-12.6c3.6-3.1,8.2-5.1,13.1-5.1
s9.4,1.7,13.1,5.1c3.6,3.1,5.8,7.8,6.1,12.6c0,0.5,0.2,0.7,0.5,1.2c0.2,0,0.7,0.2,1.2,0.2h11.6c0.2,0,0.5,0,0.7-0.2
c0.2,0,0.5-0.2,0.5-0.2c0-0.2,0.2-0.2,0.2-0.5s0-0.5,0-0.7V54.7c0-0.5-0.2-1-0.5-1.2C64.1,44.8,52.2,39.9,39.7,39.9z"/>
<path class="st1" d="M8,19.1c-0.1,0-0.4,0-0.6,0.1c-0.1,0.1-0.3,0.1-0.4,0.4C6.8,20,6.8,20,6.8,20.3c0,0.1,0,0.4,0,0.6
C9.6,28,11,35.8,11,43.3c0,7.7-1.5,15.3-4.2,22.5c0,0.1-0.1,0.3,0,0.6c0,0.1,0.1,0.3,0.1,0.6c0,0.3,0.3,0.3,0.4,0.4
s0.3,0.1,0.6,0.1h10.2c0.1,0,0.4,0,0.6-0.1c0.1-0.1,0.3-0.1,0.4-0.4c0.1-0.1,0.1-0.3,0.1-0.6c0-0.1,0-0.4-0.1-0.6
C16.4,58.7,15,50.9,15,43.3c0-7.7,1.5-15.3,4.2-22.5c0-0.1,0.1-0.3,0.1-0.6c0-0.1-0.1-0.3-0.1-0.6c-0.1-0.1-0.3-0.3-0.4-0.4
s-0.3-0.1-0.6-0.1C18.2,19.1,8,19.1,8,19.1z"/>
<path class="st2" d="M61.1,19.1c-0.1,0-0.4,0-0.6,0.1c-0.1,0.1-0.3,0.1-0.4,0.4C60,19.9,60,19.9,60,20.2c0,0.1,0,0.4,0,0.6
c2.8,7.2,4.2,14.9,4.2,22.5c0,7.7-1.5,15.3-4.2,22.5c0,0.1-0.1,0.3,0,0.6c0,0.1,0.1,0.3,0.1,0.6c0,0.3,0.3,0.3,0.4,0.4
c0.1,0.1,0.3,0.1,0.6,0.1h10.2c0.1,0,0.4,0,0.6-0.1c0.1-0.1,0.3-0.1,0.4-0.4c0.1-0.1,0.1-0.3,0.1-0.6c0-0.1,0-0.4-0.1-0.6
c-2.8-7.2-4.2-14.9-4.2-22.5c0-7.7,1.5-15.3,4.2-22.5c0-0.1,0.1-0.3,0.1-0.6c0-0.1-0.1-0.3-0.1-0.6c-0.1-0.1-0.3-0.3-0.4-0.4
s-0.3-0.1-0.6-0.1C71.3,19.1,61.1,19.1,61.1,19.1z"/>
</g>
<g>
<g class="st3">
<path class="st4" d="M112.6,38.6c-0.4,1-1,1.8-1.7,2.5c-0.8,0.7-1.5,1.3-2.6,1.7c-1,0.4-2,0.7-3.2,0.7c-1.1,0-2-0.2-2.9-0.6
c-1-0.4-1.7-0.9-2.4-1.4c-0.7,0.6-1.5,1.2-2.4,1.4c-1,0.4-1.9,0.6-2.9,0.6c-1.1,0-2.1-0.2-3.2-0.7c-1-0.4-1.8-1-2.6-1.7
c-0.8-0.7-1.3-1.5-1.7-2.5c-0.4-1-0.7-2-0.7-3.1V17.1c0-0.3,0.1-0.6,0.3-0.8c0.2-0.2,0.5-0.3,0.8-0.3h3.2c0.3,0,0.6,0.1,0.8,0.3
c0.2,0.2,0.3,0.5,0.3,0.8v18.5c0,0.9,0.3,1.6,0.8,2.2c0.6,0.6,1.2,0.9,1.9,0.9c0.8,0,1.3-0.3,1.9-0.9s0.8-1.3,0.8-2.2V33
c0-0.3,0.1-0.6,0.3-0.8c0.2-0.2,0.5-0.3,0.8-0.3h3.4c0.3,0,0.6,0.1,0.8,0.3c0.2,0.2,0.3,0.5,0.3,0.8v2.6c0,0.9,0.3,1.6,0.8,2.2
c0.6,0.6,1.2,0.9,1.9,0.9c0.8,0,1.4-0.3,1.9-0.9c0.6-0.6,0.8-1.3,0.8-2.2V17.1c0-0.3,0.1-0.6,0.3-0.8c0.2-0.2,0.5-0.3,0.8-0.3h3.2
c0.3,0,0.6,0.1,0.8,0.3c0.2,0.2,0.3,0.5,0.3,0.8v18.5C113.2,36.7,113,37.7,112.6,38.6z"/>
<path class="st4" d="M150.9,35.1c0,1.2-0.2,2.1-0.7,3.1c-0.4,1-1,1.8-1.7,2.5c-0.8,0.7-1.5,1.3-2.6,1.6c-1,0.4-2,0.7-3.2,0.7
h-10.8c-1.2,0-2.1-0.2-3.2-0.7c-1-0.4-1.8-1-2.6-1.6c-0.8-0.7-1.3-1.5-1.7-2.5c-0.4-1-0.7-2-0.7-3.1V23.9c0-1.1,0.2-2.1,0.7-3.1
c0.4-1,1.1-1.8,1.7-2.5c0.8-0.7,1.5-1.3,2.6-1.6c1-0.4,2-0.7,3.1-0.7h10.8c1.2,0,2.1,0.2,3.1,0.7c1,0.4,1.8,1,2.6,1.6
s1.3,1.5,1.7,2.5s0.7,2,0.7,3.1V24v11.1H150.9z M145.5,24c0-0.9-0.3-1.6-0.8-2.2s-1.2-0.9-1.9-0.9H132c-0.8,0-1.3,0.3-1.9,0.9
c-0.5,0.6-0.8,1.3-0.8,2.2v11.1c0,0.9,0.3,1.6,0.8,2.2c0.5,0.6,1.2,0.9,1.9,0.9h10.8c0.8,0,1.3-0.3,1.9-0.9
c0.5-0.6,0.8-1.3,0.8-2.2V24z"/>
<path class="st4" d="M189.4,29.1c-0.2,0.2-0.5,0.3-0.8,0.3h-3.2c-0.3,0-0.6-0.1-0.8-0.3c-0.2-0.2-0.3-0.5-0.3-0.8v-4.4
c0-0.9-0.3-1.6-0.8-2.2s-1.2-0.9-1.9-0.9h-10.8c-0.8,0-1.3,0.3-1.9,0.9c-0.5,0.6-0.8,1.3-0.8,2.2v17.9c0,0.3-0.1,0.6-0.3,0.8
s-0.5,0.3-0.8,0.3h-3c-0.3,0-0.6-0.1-0.8-0.3s-0.3-0.5-0.3-0.8V24c0-1.1,0.2-2.1,0.7-3.1c0.4-1,1-1.8,1.7-2.5
c0.8-0.7,1.5-1.3,2.6-1.7c1-0.4,2-0.7,3.2-0.7h10.8c1.2,0,2.1,0.2,3.2,0.6c1,0.4,1.8,1,2.6,1.7c0.8,0.8,1.3,1.5,1.7,2.5
s0.7,2,0.7,3.1v4.4C189.8,28.7,189.7,28.9,189.4,29.1z"/>
<path class="st4" d="M225.9,42.6c-0.2,0.2-0.5,0.3-0.8,0.3H222c-0.3,0-0.6-0.1-0.8-0.3s-0.3-0.5-0.3-0.8V23.5
c0-0.9-0.3-1.6-0.8-2.2c-0.6-0.6-1.2-0.9-1.9-0.9s-1.4,0.3-1.9,0.9c-0.6,0.6-0.8,1.3-0.8,2.2v7.6c0,0.3-0.1,0.6-0.3,0.8
s-0.5,0.3-0.8,0.3h-3.2c-0.3,0-0.6-0.1-0.8-0.3s-0.3-0.5-0.3-0.8v-7.6c0-0.9-0.3-1.6-0.8-2.2c-0.6-0.6-1.2-0.9-1.9-0.9
s-1.3,0.3-1.9,0.9c-0.6,0.6-0.8,1.3-0.8,2.2v18.4c0,0.3-0.1,0.6-0.3,0.8s-0.5,0.3-0.8,0.3h-3.3c-0.3,0-0.6-0.1-0.8-0.3
s-0.3-0.5-0.3-0.8V23.5c0-1.1,0.2-2.1,0.7-3.1c0.4-1,1-1.8,1.7-2.5c0.8-0.7,1.5-1.3,2.5-1.7c1-0.4,2-0.7,3.1-0.7s2,0.2,2.9,0.6
c1,0.4,1.7,0.9,2.4,1.4c0.7-0.6,1.5-1.2,2.4-1.4c1-0.4,1.9-0.6,2.9-0.6c1.1,0,2.1,0.2,3.1,0.7c1,0.4,1.8,1,2.5,1.7
s1.3,1.5,1.7,2.5s0.7,2,0.7,3.1v18.4C226.2,42.1,226.1,42.4,225.9,42.6z"/>
<path class="st4" d="M254.3,42.6c-0.2,0.2-0.5,0.3-0.8,0.3h-6.1c-0.3,0-0.6-0.1-0.8-0.3s-0.3-0.5-0.3-0.8v-2.7
c0-0.3,0.1-0.6,0.3-0.8s0.5-0.3,0.8-0.3h6.1c0.3,0,0.6,0.1,0.8,0.3s0.3,0.5,0.3,0.8v2.7C254.6,42.1,254.5,42.4,254.3,42.6z"/>
<path class="st4" d="M301.4,38c-0.4,1-1,1.8-1.7,2.5c-0.8,0.7-1.5,1.3-2.6,1.7c-1,0.4-2,0.7-3.2,0.7h-7.6c-1.1,0-2.1-0.2-3.1-0.7
c-1-0.4-1.8-1-2.5-1.7c-0.8-0.7-1.3-1.5-1.7-2.5c-0.4-1-0.7-2-0.7-3.1v-10h-2.8c-0.3,0-0.6-0.1-0.8-0.3s-0.3-0.5-0.3-0.8v-2.7
c0-0.3,0.1-0.6,0.3-0.8c0.2-0.2,0.5-0.3,0.8-0.3h2.8v-2.9c0-0.3,0.1-0.6,0.3-0.8c0.2-0.2,0.5-0.3,0.8-0.3h3.2
c0.3,0,0.6,0.1,0.8,0.3c0.2,0.2,0.3,0.5,0.3,0.8V20H301c0.3,0,0.6,0.1,0.8,0.3c0.2,0.2,0.3,0.5,0.3,0.8v2.7c0,0.3-0.1,0.6-0.3,0.8
c-0.2,0.2-0.5,0.3-0.8,0.3h-17.3v10.2c0,0.9,0.3,1.6,0.8,2.2s1.2,0.9,1.9,0.9h7.6c0.8,0,1.4-0.3,1.9-0.9c0.5-0.6,0.8-1.3,0.8-2.2
v-1.6c0-0.3,0.1-0.6,0.3-0.8s0.5-0.3,0.8-0.3h3.2c0.3,0,0.6,0.1,0.8,0.3c0.2,0.2,0.3,0.5,0.3,0.8v1.6
C301.9,36.1,301.8,37.1,301.4,38z"/>
<path class="st4" d="M339.3,35.1c0,1.2-0.2,2.1-0.7,3.1c-0.4,1-1,1.8-1.7,2.5c-0.8,0.7-1.5,1.3-2.6,1.6c-1,0.4-2,0.7-3.2,0.7
h-10.8c-1.2,0-2.1-0.2-3.2-0.7c-1-0.4-1.8-1-2.6-1.6c-0.8-0.7-1.3-1.5-1.7-2.5c-0.4-1-0.7-2-0.7-3.1V23.9c0-1.1,0.2-2.1,0.7-3.1
c0.4-1,1.1-1.8,1.7-2.5c0.8-0.7,1.5-1.3,2.6-1.6c1-0.4,2-0.7,3.1-0.7H331c1.2,0,2.1,0.2,3.1,0.7c1,0.4,1.8,1,2.6,1.6
c0.8,0.7,1.3,1.5,1.7,2.5c0.4,1,0.7,2,0.7,3.1V24v11.1H339.3z M333.9,24c0-0.9-0.3-1.6-0.8-2.2s-1.2-0.9-1.9-0.9h-10.8
c-0.8,0-1.3,0.3-1.9,0.9c-0.5,0.6-0.8,1.3-0.8,2.2v11.1c0,0.9,0.3,1.6,0.8,2.2c0.5,0.6,1.2,0.9,1.9,0.9h10.8
c0.8,0,1.3-0.3,1.9-0.9c0.5-0.6,0.8-1.3,0.8-2.2V24z"/>
</g>
<g>
<g>
<g>
<polygon class="st4" points="227.9,70.1 227.5,70.1 225.5,62.1 223.1,62.1 221.3,70.1 221,70.1 218.8,61.8 217.2,61.8
219.9,71.4 220.3,71.4 221.2,71.4 221.6,71.4 222.2,71.4 222.7,71.4 224.3,63.4 226.3,71.4 226.9,71.4 227.5,71.4 227.9,71.4
228.8,71.4 229.1,71.4 231.5,61.8 230,61.8 "/>
<path class="st4" d="M241.9,63.1c-0.6-0.6-1-0.9-1.6-1.2c-1.3-0.6-3-0.6-4.3,0c-1.2,0.6-2.2,1.6-2.7,2.8
c-0.1,0.6-0.3,1.2-0.3,1.9V67c0,0.7,0.1,1.2,0.3,1.8c0.1,0.6,0.6,1.2,1,1.6c0.6,0.6,1,0.9,1.6,1.2c1.3,0.6,3.1,0.6,4.5,0
c1.2-0.6,2.1-1.6,2.7-2.8c0.1-0.6,0.3-1.2,0.3-1.8v-0.3c0-0.7-0.1-1.2-0.3-1.9C242.8,64,242.5,63.4,241.9,63.1z M241.3,68.5
c-0.3,0.6-0.7,1-1.2,1.2c-1.2,0.7-2.7,0.7-3.9,0c-0.6-0.3-0.9-0.7-1.2-1.2c-0.3-0.6-0.3-1.2-0.3-1.9s0.1-1.2,0.3-1.9
c0.3-0.6,0.7-1,1.2-1.2c1.2-0.7,2.7-0.7,3.7,0c0.6,0.3,0.9,0.7,1.2,1.2C241.8,65.9,241.8,67.3,241.3,68.5L241.3,68.5z"/>
<path class="st4" d="M248.3,62.7c-0.6,0.6-0.9,1.2-0.9,2.1v-3h-1.3v9.7h1.8v-5.2c0-0.9,0.1-1.6,0.9-2.2c0.7-0.6,1.3-0.9,2.2-0.7
h0.7v-1.5h-0.3C249.8,61.8,248.9,62.1,248.3,62.7z"/>
<path class="st4" d="M266.5,62.1c-0.6-0.3-1.2-0.6-1.9-0.6l0,0c-0.7,0-1.3,0.1-1.9,0.6c-0.6,0.3-0.9,0.9-1.2,1.6
c0,0.1,0,0.1-0.1,0.3c0-0.1,0-0.1-0.1-0.3c-0.1-0.7-0.6-1.2-1-1.6c-0.6-0.3-1.2-0.6-1.9-0.6l0,0c-1.3-0.1-2.7,0.7-3,2.1
c0,0.1-0.1,0.3-0.1,0.6V62H254v9.7h1.8v-6c0-0.7,0.1-1.3,0.7-1.9c0.6-0.6,1-0.7,1.8-0.7c0.7,0,1.2,0.1,1.6,0.7
c0.3,0.6,0.7,1.2,0.6,1.9v6h1.8v-6c0-0.7,0.1-1.3,0.7-1.9s1-0.7,1.8-0.7c0.7,0,1.2,0.1,1.6,0.7c0.3,0.6,0.7,1.2,0.6,1.9v6h1.8
v-5.5c0-0.9-0.1-1.6-0.3-2.4C267.4,63,267.1,62.4,266.5,62.1z"/>
<path class="st4" d="M276.2,61.6L276.2,61.6c-0.9,0-1.6,0.1-2.1,0.6c-0.6,0.3-0.9,0.9-1,1.3v-5.4h-1.8v13.4h1.8V66
c0-0.7,0.1-1.3,0.7-2.1c0.6-0.6,1.2-0.9,1.9-0.7c0.7,0,1.2,0.1,1.8,0.7c0.6,0.6,0.7,1.2,0.7,1.9v6h1.8v-5.5
c0-1.3-0.3-2.7-0.9-3.3C278.2,61.9,277.3,61.6,276.2,61.6z"/>
<path class="st4" d="M291.2,63.1c-0.6-0.6-1-0.9-1.6-1.2c-1.3-0.6-3-0.6-4.3,0c-1.2,0.6-2.2,1.6-2.7,2.8
c-0.1,0.6-0.3,1.2-0.3,1.9V67c0,0.7,0.1,1.2,0.3,1.8c0.1,0.6,0.6,1.2,1,1.6c0.6,0.6,1,0.9,1.6,1.2c1.3,0.6,3.1,0.6,4.5,0
c1.2-0.6,2.1-1.6,2.7-2.8c0.1-0.6,0.3-1.2,0.3-1.8v-0.3c0-0.7-0.1-1.2-0.3-1.9C292.1,64,291.8,63.4,291.2,63.1z M290.5,68.5
c-0.3,0.6-0.7,1-1.2,1.2c-1.2,0.7-2.7,0.7-3.9,0c-0.6-0.3-0.9-0.7-1.2-1.2c-0.3-0.4-0.3-1.2-0.3-1.9s0.1-1.2,0.3-1.9
c0.3-0.6,0.7-1,1.2-1.2c1.2-0.7,2.7-0.7,3.7,0c0.6,0.3,0.9,0.7,1.2,1.2C291,65.9,291,67.3,290.5,68.5L290.5,68.5z"/>
<polygon class="st4" points="294,58.2 294,59.7 295.4,59.7 295.4,71.4 297.2,71.4 297.2,59.7 297.2,58.2 295.4,58.2 "/>
<path class="st4" d="M309.2,66.2c0-0.9-0.1-1.6-0.6-2.2c-0.3-0.7-0.9-1.3-1.6-1.8c-0.9-0.6-1.8-0.7-2.7-0.7
c-0.7,0-1.3,0.1-2.1,0.3c-0.6,0.3-1,0.7-1.6,1.2c-0.3,0.6-0.7,1-0.9,1.6c-0.1,0.6-0.3,1.2-0.3,1.9v0.3c0,0.7,0.1,1.2,0.3,1.8
c0.1,0.6,0.6,1.2,0.9,1.6c0.3,0.6,0.9,0.9,1.6,1.2c0.7,0.3,1.3,0.6,2.2,0.6c0.7,0,1.3-0.1,2.1-0.3c1.2-0.6,2.1-1.6,2.2-2.8h-1.6
c-0.1,0.6-0.6,0.9-0.9,1.2c-1.2,0.7-2.7,0.7-3.7,0c-0.6-0.3-0.9-0.7-1-1.3c-0.1-0.6-0.3-1-0.3-1.6h7.6L309.2,66.2z M302.8,63.4
c0.6-0.3,1-0.6,1.8-0.6c0.6,0,1.2,0.1,1.8,0.3c0.6,0.3,0.9,0.7,1,1.2c0.1,0.3,0.1,0.7,0.3,1h-6c0-0.3,0.1-0.7,0.1-0.9
C302.1,64.1,302.4,63.8,302.8,63.4z"/>
</g>
<g>
<path class="st5" d="M200,66.5c0-4.3-1.8-8.5-4.8-11.6c-0.1-0.1-0.3-0.1-0.3-0.1H191c-0.3,0-0.6,0.1-0.6,0.6v4
c0,0.3,0.1,0.6,0.6,0.6c3.7,0.3,6.4,3.4,6.1,7.1c-0.1,3.3-2.8,6-6.1,6.1c-0.3,0-0.6,0.3-0.6,0.6v4c0,0.3,0.3,0.6,0.6,0.6h3.9
c0.1,0,0.3,0,0.3-0.1C198.4,75.2,200,71,200,66.5z"/>
<path class="st6" d="M214,74c0-0.3-0.1-0.6-0.6-0.6c-3.7-0.3-6.4-3.4-6.1-7.1c0.1-3.3,2.8-6,6.1-6.1c0.3,0,0.6-0.3,0.6-0.6v-4
c0-0.3-0.1-0.6-0.6-0.6h-3.8c-0.1,0-0.3,0-0.3,0.1c-6.3,6.4-6.3,16.8,0,23.4c0.1,0.1,0.3,0.1,0.3,0.1h3.9c0.3,0,0.6-0.1,0.6-0.6
v-4C214.1,74,214,74,214,74z"/>
<path class="st7" d="M199.3,52.7c-0.3,0-0.7,0.3-0.7,0.7v0.1c1.6,4.2,2.4,8.5,2.4,13.1s-0.9,8.9-2.4,13.1
c-0.1,0.3,0,0.7,0.3,0.9h0.1h6c0.3,0,0.7-0.3,0.7-0.7v-0.3c-3.3-8.3-3.3-17.7,0-26.1c0.1-0.3,0-0.7-0.3-0.9h-0.1h-6V52.7z"/>
</g>
</g>
<g>
<g>
<path class="st4" d="M121.6,72c-0.8,0-1.6-0.2-2.2-0.6s-1.2-0.9-1.5-1.6c-0.4-0.7-0.6-1.5-0.6-2.5h0.5v4.4h-1.4V57.8h1.8v6.7
l-0.6,1.4c0.1-1.1,0.3-2,0.7-2.7s0.9-1.2,1.5-1.5s1.3-0.5,2.1-0.5c0.7,0,1.4,0.1,2,0.4s1.1,0.6,1.5,1.1s0.8,1,1,1.7
c0.2,0.6,0.3,1.3,0.3,2v0.3c0,0.7-0.1,1.4-0.4,2c-0.2,0.6-0.6,1.2-1,1.7s-1,0.9-1.6,1.1S122.4,72,121.6,72z M121.4,70.4
c0.7,0,1.3-0.2,1.8-0.5s0.9-0.8,1.2-1.4s0.4-1.2,0.4-2s-0.1-1.4-0.4-2s-0.7-1-1.2-1.3s-1.1-0.5-1.8-0.5c-0.6,0-1.2,0.1-1.7,0.4
c-0.5,0.3-0.9,0.7-1.2,1.2s-0.5,1.1-0.5,1.9v0.9c0,0.7,0.2,1.3,0.5,1.8s0.7,0.9,1.3,1.1C120.3,70.3,120.8,70.4,121.4,70.4z"/>
<path class="st4" d="M132.9,72c-1.2,0-2.1-0.4-2.8-1.2s-1-1.9-1-3.4v-5.9h1.8v6.3c0,0.8,0.2,1.4,0.6,1.9c0.4,0.5,1,0.7,1.7,0.7
c0.8,0,1.4-0.2,1.9-0.7s0.7-1.2,0.7-2v-6.1h1.8v10.2h-1.4v-4.4h0.2c0,1-0.1,1.8-0.4,2.5c-0.3,0.7-0.7,1.2-1.2,1.6
C134.4,71.8,133.8,72,132.9,72L132.9,72z"/>
<path class="st4" d="M140.2,63v-1.4h3.4V63H140.2z M142.3,59.9c-0.4,0-0.7-0.1-0.9-0.3s-0.3-0.5-0.3-0.8s0.1-0.6,0.3-0.8
s0.5-0.3,0.9-0.3s0.7,0.1,0.9,0.3s0.3,0.5,0.3,0.8s-0.1,0.6-0.3,0.8C143,59.8,142.7,59.9,142.3,59.9z M141.8,71.7V61.6h1.8v10.1
H141.8z"/>
<path class="st4" d="M145.9,59.3v-1.4h3.2v1.4H145.9z M147.3,71.7V57.8h1.8v13.9H147.3z"/>
<path class="st4" d="M151.1,62.9v-1.4h7v1.4H151.1z M156.5,71.8c-0.8,0-1.4-0.1-2-0.3c-0.6-0.2-1-0.6-1.3-1.1s-0.4-1.2-0.4-2.1
v-9.8h1.7v10c0,0.5,0.1,1,0.4,1.3c0.3,0.3,0.7,0.4,1.3,0.4h1.8v1.6H156.5z"/>
<path class="st4" d="M169.7,72c-0.9,0-1.7-0.2-2.3-0.5c-0.7-0.3-1.2-0.7-1.7-1.2s-0.8-1.1-1-1.7s-0.4-1.3-0.4-1.9v-0.3
c0-0.6,0.1-1.3,0.4-1.9c0.2-0.6,0.6-1.2,1.1-1.7s1-0.9,1.7-1.2s1.4-0.4,2.3-0.4s1.6,0.1,2.3,0.4c0.7,0.3,1.2,0.7,1.7,1.2
s0.8,1.1,1.1,1.7c0.2,0.6,0.4,1.3,0.4,1.9v0.3c0,0.6-0.1,1.3-0.4,1.9c-0.2,0.6-0.6,1.2-1,1.7s-1,0.9-1.7,1.2
C171.3,71.9,170.6,72,169.7,72z M169.7,70.4c0.8,0,1.4-0.2,1.9-0.5s0.9-0.8,1.2-1.4s0.4-1.2,0.4-1.9c0-0.7-0.1-1.4-0.4-1.9
c-0.3-0.6-0.7-1-1.2-1.3s-1.2-0.5-1.9-0.5s-1.4,0.2-1.9,0.5s-1,0.8-1.3,1.3c-0.3,0.6-0.4,1.2-0.4,1.9c0,0.7,0.1,1.3,0.4,1.9
c0.3,0.6,0.7,1,1.2,1.4C168.3,70.2,168.9,70.4,169.7,70.4z"/>
<path class="st4" d="M177.9,71.7V61.6h1.4V66h-0.2c0-1,0.1-1.8,0.4-2.5c0.3-0.7,0.7-1.2,1.2-1.6s1.3-0.5,2.1-0.5h0.1
c1.3,0,2.2,0.4,2.8,1.2s0.9,2,0.9,3.5v5.8h-1.8v-6.2c0-0.8-0.2-1.4-0.7-1.9c-0.4-0.5-1.1-0.7-1.8-0.7c-0.8,0-1.4,0.2-1.9,0.7
s-0.7,1.2-0.7,2v6.1h-1.8V71.7z"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,24 @@
import { CssBaseline } from "@material-ui/core";
import { ThemeProvider } from "@material-ui/core/styles";
import { SnackbarProvider } from "notistack";
import ReactDOM from "react-dom";
import App from "./App";
import ErrorBoundary from "./components/ErrorBoundary";
import { EthereumProviderProvider } from "./contexts/EthereumProviderContext";
import { SolanaWalletProvider } from "./contexts/SolanaWalletContext";
import { theme } from "./muiTheme";
ReactDOM.render(
<ErrorBoundary>
<ThemeProvider theme={theme}>
<CssBaseline />
<SolanaWalletProvider>
<EthereumProviderProvider>
<SnackbarProvider maxSnack={3}>
<App />
</SnackbarProvider>
</EthereumProviderProvider>
</SolanaWalletProvider>
</ThemeProvider>
</ErrorBoundary>,
document.getElementById("root")
);

View File

@ -0,0 +1,160 @@
import { createTheme, responsiveFontSizes } from "@material-ui/core";
export const COLORS = {
blue: "#1975e6",
blueWithTransparency: "rgba(25, 117, 230, 0.8)",
gray: "#4e4e54",
green: "#0ac2af",
greenWithTransparency: "rgba(10, 194, 175, 0.8)",
lightGreen: "rgba(51, 242, 223, 1)",
lightBlue: "#83b9fc",
nearBlack: "#000008",
nearBlackWithMinorTransparency: "rgba(0,0,0,.25)",
red: "#aa0818",
darkRed: "#810612",
};
export const theme = responsiveFontSizes(
createTheme({
palette: {
type: "dark",
background: {
default: COLORS.nearBlack,
paper: COLORS.nearBlack,
},
divider: COLORS.gray,
text: {
primary: "rgba(255,255,255,0.98)",
},
primary: {
main: COLORS.blueWithTransparency, // #0074FF
light: COLORS.lightBlue,
},
secondary: {
main: COLORS.greenWithTransparency, // #00EFD8
light: COLORS.lightGreen,
},
error: {
main: COLORS.red,
},
},
typography: {
fontFamily: "'Sora', sans-serif",
h1: {
fontWeight: "200",
},
h2: {
fontWeight: "200",
},
h4: {
fontWeight: "500",
},
},
overrides: {
MuiCssBaseline: {
"@global": {
"*": {
scrollbarWidth: "thin",
scrollbarColor: `${COLORS.gray} ${COLORS.nearBlackWithMinorTransparency}`,
},
"*::-webkit-scrollbar": {
width: "8px",
height: "8px",
backgroundColor: COLORS.nearBlackWithMinorTransparency,
},
"*::-webkit-scrollbar-thumb": {
backgroundColor: COLORS.gray,
borderRadius: "4px",
},
"*::-webkit-scrollbar-corner": {
// this hides an annoying white box which appears when both scrollbars are present
backgroundColor: "transparent",
},
},
},
MuiAccordion: {
root: {
backgroundColor: COLORS.nearBlackWithMinorTransparency,
"&:before": {
display: "none",
},
},
rounded: {
"&:first-child": {
borderTopLeftRadius: "16px",
borderTopRightRadius: "16px",
},
"&:last-child": {
borderBottomLeftRadius: "16px",
borderBottomRightRadius: "16px",
},
},
},
MuiAlert: {
root: {
borderRadius: "8px",
border: "1px solid",
},
},
MuiButton: {
root: {
borderRadius: "5px",
textTransform: "none",
},
},
MuiLink: {
root: {
color: COLORS.lightBlue,
},
},
MuiPaper: {
rounded: {
borderRadius: "16px",
},
},
MuiStepper: {
root: {
backgroundColor: "transparent",
padding: 0,
},
},
MuiStep: {
root: {
backgroundColor: COLORS.nearBlackWithMinorTransparency,
borderRadius: "16px",
padding: 16,
},
},
MuiStepConnector: {
lineVertical: {
borderLeftWidth: 0,
},
},
MuiStepContent: {
root: {
borderLeftWidth: 0,
},
},
MuiStepLabel: {
label: {
fontSize: 16,
fontWeight: "300",
"&.MuiStepLabel-active": {
fontWeight: "300",
},
"&.MuiStepLabel-completed": {
fontWeight: "300",
},
},
},
MuiTab: {
root: {
fontSize: 18,
fontWeight: "300",
padding: 12,
textTransform: "none",
},
},
},
})
);

View File

@ -0,0 +1 @@
/// <reference types="react-scripts" />

View File

@ -0,0 +1,730 @@
import {
ChainId,
CHAIN_ID_BSC,
CHAIN_ID_ETH,
CHAIN_ID_ETHEREUM_ROPSTEN,
CHAIN_ID_POLYGON,
CHAIN_ID_SOLANA,
CHAIN_ID_TERRA,
} from "@certusone/wormhole-sdk";
import { clusterApiUrl } from "@solana/web3.js";
import { getAddress } from "ethers/lib/utils";
import bscIcon from "../icons/bsc.svg";
import ethIcon from "../icons/eth.svg";
import polygonIcon from "../icons/polygon.svg";
import solanaIcon from "../icons/solana.svg";
import terraIcon from "../icons/terra.svg";
export type Cluster = "devnet" | "testnet" | "mainnet";
export const CLUSTER: Cluster =
process.env.REACT_APP_CLUSTER === "mainnet"
? "mainnet"
: process.env.REACT_APP_CLUSTER === "testnet"
? "testnet"
: "devnet";
export interface ChainInfo {
id: ChainId;
name: string;
logo: string;
}
export const CHAINS =
CLUSTER === "mainnet"
? [
{
id: CHAIN_ID_BSC,
name: "Binance Smart Chain",
logo: bscIcon,
},
{
id: CHAIN_ID_ETH,
name: "Ethereum",
logo: ethIcon,
},
{
id: CHAIN_ID_POLYGON,
name: "Polygon",
logo: polygonIcon,
},
{
id: CHAIN_ID_SOLANA,
name: "Solana",
logo: solanaIcon,
},
{
id: CHAIN_ID_TERRA,
name: "Terra",
logo: terraIcon,
},
]
: CLUSTER === "testnet"
? [
{
id: CHAIN_ID_BSC,
name: "Binance Smart Chain",
logo: bscIcon,
},
{
id: CHAIN_ID_ETH,
name: "Ethereum (Goerli)",
logo: ethIcon,
},
{
id: CHAIN_ID_ETHEREUM_ROPSTEN,
name: "Ethereum (Ropsten)",
logo: ethIcon,
},
{
id: CHAIN_ID_POLYGON,
name: "Polygon",
logo: polygonIcon,
},
{
id: CHAIN_ID_SOLANA,
name: "Solana",
logo: solanaIcon,
},
{
id: CHAIN_ID_TERRA,
name: "Terra",
logo: terraIcon,
},
]
: [
{
id: CHAIN_ID_BSC,
name: "Binance Smart Chain",
logo: bscIcon,
},
{
id: CHAIN_ID_ETH,
name: "Ethereum",
logo: ethIcon,
},
// {
// id: CHAIN_ID_SOLANA,
// name: "Solana",
// logo: solanaIcon,
// },
// {
// id: CHAIN_ID_TERRA,
// name: "Terra",
// logo: terraIcon,
// },
];
export const BETA_CHAINS: ChainId[] = CLUSTER === "mainnet" ? [] : [];
export const CHAINS_WITH_NFT_SUPPORT = CHAINS.filter(
({ id }) =>
id === CHAIN_ID_BSC ||
id === CHAIN_ID_ETH ||
id === CHAIN_ID_ETHEREUM_ROPSTEN ||
id === CHAIN_ID_POLYGON ||
id === CHAIN_ID_SOLANA
);
export type ChainsById = { [key in ChainId]: ChainInfo };
export const CHAINS_BY_ID: ChainsById = CHAINS.reduce((obj, chain) => {
obj[chain.id] = chain;
return obj;
}, {} as ChainsById);
export const getDefaultNativeCurrencySymbol = (chainId: ChainId) =>
chainId === CHAIN_ID_SOLANA
? "SOL"
: chainId === CHAIN_ID_ETH || chainId === CHAIN_ID_ETHEREUM_ROPSTEN
? "ETH"
: chainId === CHAIN_ID_BSC
? "BNB"
: chainId === CHAIN_ID_TERRA
? "LUNA"
: chainId === CHAIN_ID_POLYGON
? "MATIC"
: "";
export const getExplorerName = (chainId: ChainId) =>
chainId === CHAIN_ID_ETH || chainId === CHAIN_ID_ETHEREUM_ROPSTEN
? "Etherscan"
: chainId === CHAIN_ID_BSC
? "BscScan"
: chainId === CHAIN_ID_TERRA
? "Finder"
: chainId === CHAIN_ID_POLYGON
? "Polygonscan"
: "Explorer";
export const WORMHOLE_RPC_HOSTS =
CLUSTER === "mainnet"
? [
"https://wormhole-v2-mainnet-api.certus.one",
"https://wormhole.inotel.ro",
"https://wormhole-v2-mainnet-api.mcf.rocks",
"https://wormhole-v2-mainnet-api.chainlayer.network",
"https://wormhole-v2-mainnet-api.staking.fund",
"https://wormhole-v2-mainnet.01node.com",
]
: CLUSTER === "testnet"
? ["https://wormhole-v2-testnet-api.certus.one"]
: ["http://localhost:7071"];
export const ETH_NETWORK_CHAIN_ID =
CLUSTER === "mainnet" ? 1 : CLUSTER === "testnet" ? 5 : 1337;
export const ROPSTEN_ETH_NETWORK_CHAIN_ID =
CLUSTER === "mainnet" ? 1 : CLUSTER === "testnet" ? 3 : 1337;
export const BSC_NETWORK_CHAIN_ID =
CLUSTER === "mainnet" ? 56 : CLUSTER === "testnet" ? 97 : 1397;
export const POLYGON_NETWORK_CHAIN_ID =
CLUSTER === "mainnet" ? 137 : CLUSTER === "testnet" ? 80001 : 1381;
export const getEvmChainId = (chainId: ChainId) =>
chainId === CHAIN_ID_ETH
? ETH_NETWORK_CHAIN_ID
: chainId === CHAIN_ID_ETHEREUM_ROPSTEN
? ROPSTEN_ETH_NETWORK_CHAIN_ID
: chainId === CHAIN_ID_BSC
? BSC_NETWORK_CHAIN_ID
: chainId === CHAIN_ID_POLYGON
? POLYGON_NETWORK_CHAIN_ID
: undefined;
export const SOLANA_HOST = process.env.REACT_APP_SOLANA_API_URL
? process.env.REACT_APP_SOLANA_API_URL
: CLUSTER === "mainnet"
? clusterApiUrl("mainnet-beta")
: CLUSTER === "testnet"
? clusterApiUrl("devnet")
: "http://localhost:8899";
export const TERRA_HOST =
CLUSTER === "mainnet"
? {
URL: "https://lcd.terra.dev",
chainID: "columbus-5",
name: "mainnet",
}
: CLUSTER === "testnet"
? {
URL: "https://bombay-lcd.terra.dev",
chainID: "bombay-12",
name: "testnet",
}
: {
URL: "http://localhost:1317",
chainID: "columbus-5",
name: "localterra",
};
export const ETH_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B"
: CLUSTER === "testnet"
? "0x706abc4E45D419950511e474C7B9Ed348A4a716c"
: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
);
export const ETH_NFT_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x6FFd7EdE62328b3Af38FCD61461Bbfc52F5651fE"
: CLUSTER === "testnet"
? "0xD8E4C2DbDd2e2bd8F1336EA691dBFF6952B1a6eB"
: "0x26b4afb60d6c903165150c6f0aa14f8016be4aec"
);
export const ETH_TOKEN_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x3ee18B2214AFF97000D974cf647E7C347E8fa585"
: CLUSTER === "testnet"
? "0xF890982f9310df57d00f659cf4fd87e65adEd8d7"
: "0x0290FB167208Af455bB137780163b7B7a9a10C16"
);
export const BSC_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B"
: CLUSTER === "testnet"
? "0x68605AD7b15c732a30b1BbC62BE8F2A509D74b4D"
: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
);
export const BSC_NFT_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE"
: CLUSTER === "testnet"
? "0xcD16E5613EF35599dc82B24Cb45B5A93D779f1EE"
: "0x26b4afb60d6c903165150c6f0aa14f8016be4aec"
);
export const BSC_TOKEN_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0xB6F6D86a8f9879A9c87f643768d9efc38c1Da6E7"
: CLUSTER === "testnet"
? "0x9dcF9D205C9De35334D646BeE44b2D2859712A09"
: "0x0290FB167208Af455bB137780163b7B7a9a10C16"
);
export const POLYGON_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x7A4B5a56256163F07b2C80A7cA55aBE66c4ec4d7"
: CLUSTER === "testnet"
? "0x0CBE91CF822c73C2315FB05100C2F714765d5c20"
: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
);
export const POLYGON_NFT_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x90BBd86a6Fe93D3bc3ed6335935447E75fAb7fCf"
: CLUSTER === "testnet"
? "0x51a02d0dcb5e52F5b92bdAA38FA013C91c7309A9"
: "0x26b4afb60d6c903165150c6f0aa14f8016be4aec"
);
export const POLYGON_TOKEN_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE"
: CLUSTER === "testnet"
? "0x377D55a7928c046E18eEbb61977e714d2a76472a"
: "0x0290FB167208Af455bB137780163b7B7a9a10C16"
);
export const SOL_BRIDGE_ADDRESS =
CLUSTER === "mainnet"
? "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth"
: CLUSTER === "testnet"
? "3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5"
: "Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o";
export const SOL_NFT_BRIDGE_ADDRESS =
CLUSTER === "mainnet"
? "WnFt12ZrnzZrFZkt2xsNsaNWoQribnuQ5B5FrDbwDhD"
: CLUSTER === "testnet"
? "2rHhojZ7hpu1zA91nvZmT8TqWWvMcKmmNBCr2mKTtMq4"
: "NFTWqJR8YnRVqPDvTJrYuLrQDitTG5AScqbeghi4zSA";
export const SOL_TOKEN_BRIDGE_ADDRESS =
CLUSTER === "mainnet"
? "wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb"
: CLUSTER === "testnet"
? "DZnkkTmCiFWfYTfT41X3Rd1kDgozqzxWaHqsw6W4x2oe"
: "B6RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE";
export const ROPSTEN_ETH_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B"
: CLUSTER === "testnet"
? "0x210c5F5e2AF958B4defFe715Dc621b7a3BA888c5"
: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
);
export const ROPSTEN_ETH_NFT_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x6FFd7EdE62328b3Af38FCD61461Bbfc52F5651fE"
: CLUSTER === "testnet"
? "0x2b048Da40f69c8dc386a56705915f8E966fe1eba"
: "0x26b4afb60d6c903165150c6f0aa14f8016be4aec"
);
export const ROPSTEN_ETH_TOKEN_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x3ee18B2214AFF97000D974cf647E7C347E8fa585"
: CLUSTER === "testnet"
? "0xF174F9A837536C449321df1Ca093Bb96948D5386"
: "0x0290FB167208Af455bB137780163b7B7a9a10C16"
);
export const SOL_CUSTODY_ADDRESS =
"GugU1tP7doLeTw9hQP51xRJyS8Da1fWxuiy2rVrnMD2m";
export const SOL_NFT_CUSTODY_ADDRESS =
"D63bhHo634eXSj4Jq3xgu2fjB5XKc8DFHzDY9iZk7fv1";
export const TERRA_TEST_TOKEN_ADDRESS =
"terra13nkgqrfymug724h8pprpexqj9h629sa3ncw7sh";
export const TERRA_BRIDGE_ADDRESS =
CLUSTER === "mainnet"
? "terra1dq03ugtd40zu9hcgdzrsq6z2z4hwhc9tqk2uy5"
: CLUSTER === "testnet"
? "terra1pd65m0q9tl3v8znnz5f5ltsfegyzah7g42cx5v"
: "terra18vd8fpwxzck93qlwghaj6arh4p7c5n896xzem5";
export const TERRA_TOKEN_BRIDGE_ADDRESS =
CLUSTER === "mainnet"
? "terra10nmmwe8r3g99a9newtqa7a75xfgs2e8z87r2sf"
: CLUSTER === "testnet"
? "terra1pseddrv0yfsn76u4zxrjmtf45kdlmalswdv39a"
: "terra10pyejy66429refv3g35g2t7am0was7ya7kz2a4";
export const getBridgeAddressForChain = (chainId: ChainId) =>
chainId === CHAIN_ID_SOLANA
? SOL_BRIDGE_ADDRESS
: chainId === CHAIN_ID_ETH
? ETH_BRIDGE_ADDRESS
: chainId === CHAIN_ID_BSC
? BSC_BRIDGE_ADDRESS
: chainId === CHAIN_ID_TERRA
? TERRA_BRIDGE_ADDRESS
: chainId === CHAIN_ID_POLYGON
? POLYGON_BRIDGE_ADDRESS
: chainId === CHAIN_ID_ETHEREUM_ROPSTEN
? ROPSTEN_ETH_BRIDGE_ADDRESS
: "";
export const getNFTBridgeAddressForChain = (chainId: ChainId) =>
chainId === CHAIN_ID_SOLANA
? SOL_NFT_BRIDGE_ADDRESS
: chainId === CHAIN_ID_ETH
? ETH_NFT_BRIDGE_ADDRESS
: chainId === CHAIN_ID_BSC
? BSC_NFT_BRIDGE_ADDRESS
: chainId === CHAIN_ID_POLYGON
? POLYGON_NFT_BRIDGE_ADDRESS
: chainId === CHAIN_ID_ETHEREUM_ROPSTEN
? ROPSTEN_ETH_NFT_BRIDGE_ADDRESS
: "";
export const getTokenBridgeAddressForChain = (chainId: ChainId) =>
chainId === CHAIN_ID_SOLANA
? SOL_TOKEN_BRIDGE_ADDRESS
: chainId === CHAIN_ID_ETH
? ETH_TOKEN_BRIDGE_ADDRESS
: chainId === CHAIN_ID_BSC
? BSC_TOKEN_BRIDGE_ADDRESS
: chainId === CHAIN_ID_TERRA
? TERRA_TOKEN_BRIDGE_ADDRESS
: chainId === CHAIN_ID_POLYGON
? POLYGON_TOKEN_BRIDGE_ADDRESS
: chainId === CHAIN_ID_ETHEREUM_ROPSTEN
? ROPSTEN_ETH_TOKEN_BRIDGE_ADDRESS
: "";
export const COVALENT_API_KEY = process.env.REACT_APP_COVALENT_API_KEY
? process.env.REACT_APP_COVALENT_API_KEY
: "";
export const COVALENT_ETHEREUM = 1; // Covalent only supports mainnet and Kovan
export const COVALENT_BSC = CLUSTER === "devnet" ? 56 : BSC_NETWORK_CHAIN_ID;
export const COVALENT_POLYGON =
CLUSTER === "devnet" ? 137 : POLYGON_NETWORK_CHAIN_ID;
export const COVALENT_GET_TOKENS_URL = (
chainId: ChainId,
walletAddress: string,
nft?: boolean,
noNftMetadata?: boolean
) => {
const chainNum =
chainId === CHAIN_ID_ETH || chainId === CHAIN_ID_ETHEREUM_ROPSTEN
? COVALENT_ETHEREUM
: chainId === CHAIN_ID_BSC
? COVALENT_BSC
: chainId === CHAIN_ID_POLYGON
? COVALENT_POLYGON
: "";
// https://www.covalenthq.com/docs/api/#get-/v1/{chain_id}/address/{address}/balances_v2/
return `https://api.covalenthq.com/v1/${chainNum}/address/${walletAddress}/balances_v2/?key=${COVALENT_API_KEY}${
nft ? "&nft=true" : ""
}${noNftMetadata ? "&no-nft-fetch=true" : ""}`;
};
export const TERRA_SWAPRATE_URL =
"https://fcd.terra.dev/v1/market/swaprate/uusd";
export const WETH_ADDRESS =
CLUSTER === "mainnet"
? "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
: CLUSTER === "testnet"
? "0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6"
: "0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E";
export const WETH_DECIMALS = 18;
export const WBNB_ADDRESS =
CLUSTER === "mainnet"
? "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"
: CLUSTER === "testnet"
? "0xae13d989dac2f0debff460ac112a837c89baa7cd"
: "0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E";
export const WBNB_DECIMALS = 18;
export const WMATIC_ADDRESS =
CLUSTER === "mainnet"
? "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270"
: CLUSTER === "testnet"
? "0x9c3c9283d3e44854697cd22d3faa240cfb032889"
: "0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E";
export const WMATIC_DECIMALS = 18;
export const ROPSTEN_WETH_ADDRESS =
CLUSTER === "mainnet"
? "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
: CLUSTER === "testnet"
? "0xc778417e063141139fce010982780140aa0cd5ab"
: "0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E";
export const ROPSTEN_WETH_DECIMALS = 18;
export const WORMHOLE_V1_ETH_ADDRESS =
CLUSTER === "mainnet"
? "0xf92cD566Ea4864356C5491c177A430C222d7e678"
: CLUSTER === "testnet"
? "0xdae0Cba01eFc4bfEc1F7Fece73Fe8b8d2Eda65B0"
: "0xf92cD566Ea4864356C5491c177A430C222d7e678"; //TODO something that doesn't explode in localhost
export const WORMHOLE_V1_SOLANA_ADDRESS =
CLUSTER === "mainnet"
? "WormT3McKhFJ2RkiGpdw9GKvNCrB2aB54gb2uV9MfQC"
: CLUSTER === "testnet"
? "BrdgiFmZN3BKkcY3danbPYyxPKwb8RhQzpM2VY5L97ED"
: "";
export const TERRA_TOKEN_METADATA_URL =
"https://assets.terra.money/cw20/tokens.json";
export const WORMHOLE_V1_MINT_AUTHORITY =
CLUSTER === "mainnet"
? "9zyPU1mjgzaVyQsYwKJJ7AhVz5bgx5uc1NPABvAcUXsT"
: CLUSTER === "testnet"
? "BJa7dq3bRP216zaTdw4cdcV71WkPc1HXvmnGeFVDi5DC"
: "";
// hardcoded addresses for warnings
export const SOLANA_TOKENS_THAT_EXIST_ELSEWHERE = [
"SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt", // SRM
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC
"kinXdEcpDQeHPEuQnqmUgtYykqKGVFq6CeVX5iAHJq6", // KIN
"CDJWUqTcYTVAKXAVXoQZFes5JUFc7owSeq7eMQcDSbo5", // renBTC
"8wv2KAykQstNAj2oW6AHANGBiFKVFhvMiyyzzjhkmGvE", // renLUNA
"G1a6jxYz3m8DVyMqYnuV7s86wD4fvuXYneWSpLJkmsXj", // renBCH
"FKJvvVJ242tX7zFtzTmzqoA631LqHh4CdgcN8dcfFSju", // renDGB
"ArUkYE2XDKzqy77PRRGjo4wREWwqk6RXTfM9NeqzPvjU", // renDOGE
"E99CQ2gFMmbiyK2bwiaFNWUUmwz4r8k2CVEFxwuvQ7ue", // renZEC
"De2bU64vsXKU9jq4bCjeDxNRGPn8nr3euaTK8jBYmD3J", // renFIL
"Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", // USDT
];
export const ETH_TOKENS_THAT_EXIST_ELSEWHERE = [
getAddress("0x476c5E26a75bd202a9683ffD34359C0CC15be0fF"), // SRM
getAddress("0x818fc6c2ec5986bc6e2cbf00939d90556ab12ce5"), // KIN
getAddress("0xeb4c2781e4eba804ce9a9803c67d0893436bb27d"), // renBTC
getAddress("0x52d87F22192131636F93c5AB18d0127Ea52CB641"), // renLUNA
getAddress("0x459086f2376525bdceba5bdda135e4e9d3fef5bf"), // renBCH
getAddress("0xe3cb486f3f5c639e98ccbaf57d95369375687f80"), // renDGB
getAddress("0x3832d2F059E55934220881F831bE501D180671A7"), // renDOGE
getAddress("0x1c5db575e2ff833e46a2e9864c22f4b22e0b37c2"), // renZEC
getAddress("0xD5147bc8e386d91Cc5DBE72099DAC6C9b99276F5"), // renFIL
];
export const ETH_TOKENS_THAT_CAN_BE_SWAPPED_ON_SOLANA = [
getAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"), // USDC
getAddress("0xdac17f958d2ee523a2206206994597c13d831ec7"), // USDT
];
export const BSC_MARKET_WARNINGS = [
getAddress(WBNB_ADDRESS),
getAddress("0xe9e7cea3dedca5984780bafc599bd69add087d56"), // BUSD
getAddress("0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d"), // USDC
getAddress("0x55d398326f99059ff775485246999027b3197955"), // BSC-USD
];
export const MIGRATION_PROGRAM_ADDRESS =
CLUSTER === "mainnet"
? "whmRZnmyxdr2TkHXcZoFdtvNYRLQ5Jtbkf6ZbGkJjdk"
: CLUSTER === "testnet"
? ""
: "Ex9bCdVMSfx7EzB3pgSi2R4UHwJAXvTw18rBQm5YQ8gK";
export const MIGRATION_ASSET_MAP = new Map<string, string>(
CLUSTER === "mainnet"
? [
[
// HUSD
"BybpSTBoZHsmKnfxYG47GDhVPKrnEKX31CScShbrzUhX",
"7VQo3HFLNH5QqGtM8eC3XQbPkJUu7nS9LeGWjerRh5Sw",
],
[
// BUSD
"AJ1W9A9N9dEMdVyoDiam2rV44gnBm2csrPDP7xqcapgX",
"33fsBLA8djQm82RpHmE3SuVrPGtZBWNYExsEUeKX1HXX",
],
[
// HBTC
"8pBc4v9GAwCBNWPB5XKA93APexMGAS4qMr37vNke9Ref",
"7dVH61ChzgmN9BwG4PkzwRP8PbYwPJ7ZPNF2vamKT2H8",
],
[
// DAI
"FYpdBuyAHSbdaAyD1sKkxyLWbAP8uUW9h6uvdhK74ij1",
"EjmyN6qEC1Tf1JxiG1ae7UTJhUxSwk1TCWNWqxWV4J6o",
],
[
// FRAX
"8L8pDf3jutdpdr4m3np68CL9ZroLActrqwxi6s9Ah5xU",
"FR87nWEUxVgerFGhZM8Y4AggKGLnaXswr1Pd8wZ4kZcp",
],
[
// USDK
"2kycGCD8tJbrjJJqWN2Qz5ysN9iB4Bth3Uic4mSB7uak",
"43m2ewFV5nDepieFjT9EmAQnc1HRtAF247RBpLGFem5F",
],
// [
// // UST
// "CXLBjMMcwkc17GfJtBos6rQCo1ypeH6eDbB82Kby4MRm",
// "5Un6AdG9GBjxVhTSvvt2x6X6vtN1zrDxkkDpDcShnHfF",
// ],
[
// UST
"CXLBjMMcwkc17GfJtBos6rQCo1ypeH6eDbB82Kby4MRm",
"9vMJfxuKxXBoEa7rM12mYLMwTacLMLDJqHozw96WQL8i",
],
// [
// // Wrapped LUNA
// "2Xf2yAXJfg82sWwdLUo2x9mZXy6JCdszdMZkcF1Hf4KV",
// "EQTV1LW23Mgtjb5LXSg9NGw1J32oqTV4HCPmHCVSGmqD",
// ],
[
// Wrapped LUNA
"2Xf2yAXJfg82sWwdLUo2x9mZXy6JCdszdMZkcF1Hf4KV",
"F6v4wfAdJB8D8p77bMXZgYt8TDKsYxLYxH5AFhUkYx9W",
],
[
// FTT
"GbBWwtYTMPis4VHb8MrBbdibPhn28TSrLB53KvUmb7Gi",
"EzfgjvkSwthhgHaceR3LnKXUoRkP6NUhfghdaHAj1tUv",
],
[
// SRM
"2jXy799YnEcRXneFo2GEAB6SDRsAa767HpWmktRr1DaP",
"xnorPhAzWXUczCP3KjU5yDxmKKZi5cSbxytQ1LgE3kG",
],
[
// FTT (Sollet)
"AGFEad2et2ZJif9jaGpdMixQqvW5i81aBdvKe7PHNfz3",
"EzfgjvkSwthhgHaceR3LnKXUoRkP6NUhfghdaHAj1tUv",
],
[
// WETH (Sollet)
"2FPyTwcZLUg1MDrwsyoP4D6s1tM7hAkHYRjkNb5w6Pxk",
"7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs",
],
[
// UNI (Sollet)
"DEhAasscXF4kEGxFgJ3bq4PpVGp5wyUxMRvn6TzGVHaw",
"8FU95xFJhUUkyyCLU13HSzDLs7oC4QZdXQHL6SCeab36",
],
[
// HXRO (Sollet)
"DJafV9qemGp7mLMEn5wrfqaFwxsbLgUsGVS16zKRk9kc",
"HxhWkVpk5NS4Ltg5nij2G671CKXFRKPK8vy271Ub4uEK",
],
[
// ALEPH (Sollet)
"CsZ5LZkDS7h9TDKjrbL7VAwQZ9nsRu8vJLhRYfmGaN8K",
"3UCMiSnkcnkPE1pgQ5ggPCBv6dXgVUy16TmMUe1WpG9x",
],
[
// TOMOE (Sollet)
"GXMvfY2jpQctDqZ9RoU3oWPhufKiCcFEfchvYumtX7jd",
"46AiRdka3HYGkhV6r9gyS6Teo9cojfGXfK8oniALYMZx",
],
]
: CLUSTER === "testnet"
? []
: [
// [
// "2WDq7wSs9zYrpx2kbHDA4RUTRch2CCTP6ZWaH4GNfnQQ",
// "GcdupcwxkmVGM6s9F8bHSjNoznXAb3hRJTioABNYkn31",
// ],
]
);
export const ETH_MIGRATION_ASSET_MAP = new Map<string, string>(
CLUSTER === "mainnet"
? [
[
// LUNA
getAddress("0xd2877702675e6cEb975b4A1dFf9fb7BAF4C91ea9"),
getAddress("0xe76820F1DB773B1d62a3D22F47259705cC5fA4E6"),
],
[
// UST
getAddress("0xa47c8bf37f92aBed4A126BDA807A7b7498661acD"),
getAddress("0xF39C29d8f6851d87c40c83b61078EB7384f7Cb51"),
],
]
: CLUSTER === "testnet"
? []
: [
// [
// "0x2D8BE6BF0baA74e0A907016679CaE9190e80dD0A",
// "0xFcCeD5E997E7fb1D0594518D3eD57245bB8ed17E",
// ],
]
);
export const BSC_MIGRATION_ASSET_MAP = new Map<string, string>(
CLUSTER === "mainnet"
? [
[
// LUNA
getAddress("0xECCF35F941Ab67FfcAA9A1265C2fF88865caA005"),
getAddress("0x355A116ef1Cf566B12Ef3a8e409A64e303c53740"),
],
[
// UST
getAddress("0x23396cF899Ca06c4472205fC903bDB4de249D6fC"),
getAddress("0x0F98AB919D04a291838B3b075c57181057D4CF75"),
],
]
: CLUSTER === "testnet"
? []
: [
// [
// "0x2D8BE6BF0baA74e0A907016679CaE9190e80dD0A",
// "0xFcCeD5E997E7fb1D0594518D3eD57245bB8ed17E",
// ],
]
);
export const getMigrationAssetMap = (chainId: ChainId) => {
if (chainId === CHAIN_ID_BSC) {
return BSC_MIGRATION_ASSET_MAP;
} else if (chainId === CHAIN_ID_ETH) {
return ETH_MIGRATION_ASSET_MAP;
} else if (chainId === CHAIN_ID_SOLANA) {
return MIGRATION_ASSET_MAP;
} else {
return new Map<string, string>();
}
};
export const SUPPORTED_TERRA_TOKENS = ["uluna", "uusd"];
export const TERRA_FCD_BASE =
CLUSTER === "mainnet"
? "https://fcd.terra.dev"
: CLUSTER === "testnet"
? "https://bombay-fcd.terra.dev"
: "http://localhost:3060";
export const TERRA_GAS_PRICES_URL = `${TERRA_FCD_BASE}/v1/txs/gas_prices`;
export const TOTAL_TRANSACTIONS_WORMHOLE = `https://europe-west3-wormhole-315720.cloudfunctions.net/mainnet/totals?groupBy=address`;
export const RECENT_TRANSACTIONS_WORMHOLE = `https://europe-west3-wormhole-315720.cloudfunctions.net/mainnet/recent?groupBy=address&numRows=2`;
export const VAA_EMITTER_ADDRESSES = [
`${CHAIN_ID_SOLANA}:ec7372995d5cc8732397fb0ad35c0121e0eaa90d26f828a534cab54391b3a4f5`, //SOLANA TOKEN
`${CHAIN_ID_SOLANA}:0def15a24423e1edd1a5ab16f557b9060303ddbab8c803d2ee48f4b78a1cfd6b`, //SOLAN NFT
`${CHAIN_ID_ETH}:0000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa585`, //ETH token
`${CHAIN_ID_ETH}:0000000000000000000000006ffd7ede62328b3af38fcd61461bbfc52f5651fe`, //ETH NFT
`${CHAIN_ID_TERRA}:0000000000000000000000007cf7b764e38a0a5e967972c1df77d432510564e2`, //terra
`${CHAIN_ID_BSC}:000000000000000000000000b6f6d86a8f9879a9c87f643768d9efc38c1da6e7`, //bsc
`${CHAIN_ID_BSC}:0000000000000000000000005a58505a96d1dbf8df91cb21b54419fc36e93fde`, //bsc nft
`${CHAIN_ID_POLYGON}:0000000000000000000000005a58505a96d1dbf8df91cb21b54419fc36e93fde`, //Polygon
`${CHAIN_ID_POLYGON}:00000000000000000000000090bbd86a6fe93d3bc3ed6335935447e75fab7fcf`, //Polygon nft
];
export const WORMHOLE_EXPLORER_BASE = "https://wormholenetwork.com/en/explorer";
export type MultiChainInfo = {
[key in ChainId]: { [address: string]: string };
};
export const MULTI_CHAIN_TOKENS: MultiChainInfo =
//EVM chains should format the addresses to all lowercase
CLUSTER === "mainnet"
? ({
[CHAIN_ID_SOLANA]: {
EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v: "USDC",
Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB: "USDT",
},
[CHAIN_ID_ETH]: {
"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": "USDC",
"0xdac17f958d2ee523a2206206994597c13d831ec7": "USDT",
},
[CHAIN_ID_TERRA]: {},
[CHAIN_ID_BSC]: {
"0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d": "USDC",
"0x55d398326f99059ff775485246999027b3197955": "USDT",
},
[CHAIN_ID_POLYGON]: {
"0x2791bca1f2de4661ed88a30c99a7a9449aa84174": "USDC",
"0xc2132d05d31c914a87c6611c10748aeb04b58e8f": "USDT",
},
} as MultiChainInfo)
: ({
[CHAIN_ID_SOLANA]: {
"2WDq7wSs9zYrpx2kbHDA4RUTRch2CCTP6ZWaH4GNfnQQ": "SOLT",
},
[CHAIN_ID_ETH]: {},
[CHAIN_ID_TERRA]: {},
[CHAIN_ID_BSC]: {},
[CHAIN_ID_POLYGON]: {},
} as MultiChainInfo);
export const AVAILABLE_MARKETS_URL =
"https://docs.wormholenetwork.com/wormhole/overview-liquid-markets";
export const SOLANA_SYSTEM_PROGRAM_ADDRESS = "11111111111111111111111111111111";
export const FEATURED_MARKETS_JSON_URL =
"https://raw.githubusercontent.com/certusone/wormhole-token-list/main/src/markets.json";

View File

@ -0,0 +1,12 @@
import { ChainId } from "@certusone/wormhole-sdk";
import swapPools from "../swapPools.json";
export default function getSwapPool(
sourceChain: ChainId,
targetChain: ChainId
) {
const map = swapPools as any;
const holder = map[sourceChain]?.[targetChain];
console.log("read swap pool", holder);
return holder;
}

View File

@ -0,0 +1,531 @@
import {
CHAIN_ID_BSC,
CHAIN_ID_ETH,
createNonce,
hexToUint8Array,
nativeToHexString,
parseSequenceFromLogEth,
TokenImplementation__factory,
} from "@certusone/wormhole-sdk";
import { formatUnits, parseUnits } from "@ethersproject/units";
import {
Button,
Container,
makeStyles,
Paper,
Typography,
} from "@material-ui/core";
import Collapse from "@material-ui/core/Collapse";
import CheckCircleOutlineRoundedIcon from "@material-ui/icons/CheckCircleOutlineRounded";
import { ethers } from "ethers";
import { useCallback, useEffect, useMemo, useState } from "react";
import { SimpleDex__factory } from "../abi/factories/SimpleDex__factory";
import ButtonWithLoader from "../components/ButtonWithLoader";
import ChainSelectDialog from "../components/ChainSelectDialog";
import CircleLoader from "../components/CircleLoader";
import EthereumSignerKey from "../components/EthereumSignerKey";
import HoverIcon from "../components/HoverIcon";
import NumberTextField from "../components/NumberTextField";
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
import useAllowance from "../hooks/useAllowance";
import useIsWalletReady from "../hooks/useIsWalletReady";
import useRestRelayer from "../hooks/useRestRelayer";
import Wormhole from "../icons/wormhole-network.svg";
import { COLORS } from "../muiTheme";
import {
CHAINS,
getBridgeAddressForChain,
getDefaultNativeCurrencySymbol,
getTokenBridgeAddressForChain,
} from "../utils/consts";
import getSwapPool from "../utils/getSwapPoolAddress";
const useStyles = makeStyles((theme) => ({
numberField: {
flexGrow: 1,
"& > * > .MuiInputBase-input": {
textAlign: "right",
height: "100%",
flexGrow: "1",
fontSize: "3rem",
fontFamily: "Roboto Mono, monospace",
caretShape: "block",
width: "0",
"&::-webkit-outer-spin-button, &::-webkit-inner-spin-button": {
"-webkit-appearance": "none",
"-moz-appearance": "none",
margin: 0,
},
"&[type=number]": {
"-webkit-appearance": "textfield",
"-moz-appearance": "textfield",
},
},
"& > * > input::-webkit-inner-spin-button": {
webkitAppearance: "none",
margin: "0",
},
},
sourceContainer: {
display: "flex",
justifyContent: "space-between",
alignItems: "center",
border: "3px solid #333333",
padding: ".6rem",
borderRadius: "30px",
"& > *": {
margin: ".5rem",
},
margin: "1rem 0rem 1rem 0rem",
},
centeredContainer: {
textAlign: "center",
width: "100%",
},
stepNum: {},
explainerText: {
marginBottom: "1rem",
},
spacer: {
height: "1rem",
},
mainPaper: {
padding: "2rem",
backgroundColor: COLORS.nearBlackWithMinorTransparency,
},
chainSelectorContainer: {},
downArrow: {
height: "5rem",
},
titleBar: {
marginTop: "10rem",
"& > *": {
margin: ".5rem",
alignSelf: "flex-end",
},
},
appBar: {
background: COLORS.nearBlackWithMinorTransparency,
"& > .MuiToolbar-root": {
margin: "auto",
width: "100%",
maxWidth: 1100,
},
},
link: {
...theme.typography.body1,
color: theme.palette.text.primary,
marginLeft: theme.spacing(6),
[theme.breakpoints.down("sm")]: {
marginLeft: theme.spacing(2.5),
},
[theme.breakpoints.down("xs")]: {
marginLeft: theme.spacing(1),
},
"&.active": {
color: theme.palette.primary.light,
},
},
bg: {
background:
"linear-gradient(160deg, rgba(69,74,117,.1) 0%, rgba(138,146,178,.1) 33%, rgba(69,74,117,.1) 66%, rgba(98,104,143,.1) 100%), linear-gradient(45deg, rgba(153,69,255,.1) 0%, rgba(121,98,231,.1) 20%, rgba(0,209,140,.1) 100%)",
display: "flex",
flexDirection: "column",
minHeight: "100vh",
},
actionArea: {
display: "grid",
placeItems: "center",
height: "10rem",
width: "100%",
},
content: {
margin: theme.spacing(2, 0),
[theme.breakpoints.up("md")]: {
margin: theme.spacing(4, 0),
},
},
brandLink: {
display: "inline-flex",
alignItems: "center",
"&:hover": {
textDecoration: "none",
},
},
iconButton: {
[theme.breakpoints.up("md")]: {
marginRight: theme.spacing(2.5),
},
[theme.breakpoints.down("sm")]: {
marginRight: theme.spacing(2.5),
},
[theme.breakpoints.down("xs")]: {
marginRight: theme.spacing(1),
},
},
gradientButton: {
backgroundImage: `linear-gradient(45deg, ${COLORS.blue} 0%, ${COLORS.nearBlack}20 50%, ${COLORS.blue}30 62%, ${COLORS.nearBlack}50 120%)`,
transition: "0.75s",
backgroundSize: "200% auto",
boxShadow: "0 0 20px #222",
"&:hover": {
backgroundPosition:
"right center" /* change the direction of the change here */,
},
width: "100%",
height: "3rem",
marginTop: "1rem",
},
disabled: {
background: COLORS.gray,
},
betaBanner: {
background: `linear-gradient(to left, ${COLORS.blue}40, ${COLORS.green}40);`,
padding: theme.spacing(1, 0),
},
loaderHolder: {
display: "flex",
justifyContent: "center",
flexDirection: "column",
alignItems: "center",
},
wormholeIcon: {
height: 60,
filter: "contrast(0)",
transition: "filter 0.5s",
"&:hover": {
filter: "contrast(1)",
},
verticalAlign: "middle",
margin: "1rem",
display: "inline-block",
},
successIcon: {
color: COLORS.green,
fontSize: "200px",
},
}));
function Home() {
const classes = useStyles();
const [transferHolderString, setTransferHolderString] = useState<string>("");
const [price, setPrice] = useState<BigInt | null>(null);
const [sequence, setSequence] = useState<string>("");
const [sourceChain, setSourceChain] = useState(CHAIN_ID_ETH);
const [targetChain, setTargetChain] = useState(CHAIN_ID_BSC);
const { swapPoolAddress, targetAsset } = useMemo(() => {
const holder = getSwapPool(sourceChain, targetChain) as any;
return {
swapPoolAddress: holder.poolAddress,
targetAsset: holder.tokenAddress,
};
}, [sourceChain, targetChain]);
const relayInfo = useRestRelayer(sourceChain, sequence, targetChain);
const allowanceInfo = useAllowance(
sourceChain,
swapPoolAddress,
targetAsset,
BigInt(100000000000000000000000),
false
);
console.log("allowance info", allowanceInfo);
console.log("relay info", relayInfo);
console.log("sequence", sequence);
console.log("price", price);
const { isReady: isEthWalletReady, walletAddress } = useIsWalletReady(
sourceChain,
true
);
const ethWallet = useEthereumProvider();
const handleSourceChange = useCallback(
(event) => {
const newSourceChain = event.target.value;
if (newSourceChain === targetChain) {
const newTargetChain = CHAINS.find(
(chain) => chain.id !== newSourceChain
);
setTargetChain(newTargetChain?.id || CHAIN_ID_ETH);
}
setSourceChain(newSourceChain);
setTransferHolderString("");
},
[targetChain]
);
const handleTargetChange = useCallback(
(event) => {
console.log(event.target.value, "value");
const newTargetChain = event.target.value;
if (newTargetChain === sourceChain) {
const newSourceChain = CHAINS.find(
(chain) => chain.id !== newTargetChain
);
setSourceChain(newSourceChain?.id || CHAIN_ID_ETH);
}
setTargetChain(newTargetChain);
setTransferHolderString("");
},
[sourceChain]
);
const swapChains = useCallback(() => {
setSourceChain(targetChain);
setTargetChain(sourceChain);
setTransferHolderString("");
}, [targetChain, sourceChain]);
const calcPrice = useCallback(async () => {
if (
!isEthWalletReady ||
!transferHolderString ||
!parseUnits(transferHolderString, 18)
) {
setPrice(null);
return;
}
const dex = SimpleDex__factory.connect(
swapPoolAddress,
ethWallet.provider as any
);
const token = TokenImplementation__factory.connect(
targetAsset,
ethWallet.provider as any
);
const tokensInPool = await token.balanceOf(swapPoolAddress);
const nativeInPool = await dex.totalLiquidity();
const price = await dex.price(
parseUnits(transferHolderString, 18),
nativeInPool,
tokensInPool
);
setPrice(price.toBigInt());
}, [
isEthWalletReady,
swapPoolAddress,
ethWallet.provider,
targetAsset,
transferHolderString,
]);
//calcPrice
//TODO debounce
useEffect(() => {
calcPrice();
}, [calcPrice]);
//TODO check that the user has enough balance
const sufficientPoolBalance =
price && transferHolderString && parseUnits(transferHolderString, "18");
const readyToGo =
isEthWalletReady &&
walletAddress &&
!isNaN(parseFloat(transferHolderString)) &&
parseFloat(transferHolderString) > 0 &&
allowanceInfo.sufficientAllowance &&
sufficientPoolBalance; //TODO check pool balances
const handleTransfer = useCallback(async () => {
if (!readyToGo || !walletAddress) {
return;
}
console.log(swapPoolAddress);
const dex = SimpleDex__factory.connect(
swapPoolAddress,
ethWallet.signer as any
);
const nonce = createNonce();
const receipt = await (
await dex.swapNGo(
getTokenBridgeAddressForChain(sourceChain),
targetChain,
hexToUint8Array(nativeToHexString(walletAddress, targetChain) as any),
"0",
nonce,
{ value: parseUnits(transferHolderString, 18) }
)
).wait();
console.log("transaction receipt", receipt);
const sequence = parseSequenceFromLogEth(
receipt,
getBridgeAddressForChain(sourceChain)
);
setSequence(sequence);
}, [
ethWallet.signer,
readyToGo,
sourceChain,
swapPoolAddress,
targetChain,
transferHolderString,
walletAddress,
]);
const handleAllowanceIncrease = useCallback(() => {
allowanceInfo.approveAmount(ethers.constants.MaxUint256.toBigInt());
}, [allowanceInfo]);
const handleAmountChange = useCallback((event) => {
setTransferHolderString(event.target.value);
}, []);
const handleReset = useCallback(() => {
setTransferHolderString("");
setSequence("");
}, []);
const sourceContent = (
<div className={classes.sourceContainer}>
<ChainSelectDialog
value={sourceChain}
onChange={handleSourceChange}
chains={CHAINS}
style2={true}
/>
<NumberTextField
className={classes.numberField}
value={transferHolderString}
onChange={handleAmountChange}
autoFocus={true}
InputProps={{ disableUnderline: true }}
/>
</div>
);
const middleButton = <HoverIcon onClick={swapChains} />; //TODO onclick
const targetContent = (
<div className={classes.sourceContainer}>
<ChainSelectDialog
value={targetChain}
onChange={handleTargetChange}
chains={CHAINS}
/>
<NumberTextField
className={classes.numberField}
value={
!!price
? parseFloat(formatUnits(price.toString(), 18)).toFixed(2)
: "0"
}
InputProps={{ disableUnderline: true }}
disabled={true}
/>
</div>
);
const walletButton = <EthereumSignerKey />;
const allowanceButton = (
<>
<ButtonWithLoader
onClick={handleAllowanceIncrease}
showLoader={allowanceInfo.isApproveProcessing}
disabled={allowanceInfo.isApproveProcessing || !isEthWalletReady}
>
Allow Wormhole Transfers
</ButtonWithLoader>
</>
);
const convertButton = (
<ButtonWithLoader
disabled={!readyToGo}
onClick={handleTransfer}
className={
classes.gradientButton + (!readyToGo ? " " + classes.disabled : "")
}
>
Convert
</ButtonWithLoader>
);
const buttonContent = !allowanceInfo.sufficientAllowance
? allowanceButton
: convertButton;
return (
<div className={classes.bg}>
<Container className={classes.centeredContainer} maxWidth="sm">
<div className={classes.titleBar}></div>
<Typography variant="h4" color="textSecondary">
{"Crypto Converter"}
</Typography>
<div className={classes.spacer} />
<Paper className={classes.mainPaper}>
<Collapse in={!!relayInfo.isComplete}>
<>
<CheckCircleOutlineRoundedIcon
fontSize={"inherit"}
className={classes.successIcon}
/>
<Typography>All Set!</Typography>
<Typography variant="h5">
{"You now have " +
parseFloat(formatUnits((price || 0).toString(), 18)).toFixed(
2
) +
" " +
getDefaultNativeCurrencySymbol(targetChain)}
</Typography>
<div className={classes.spacer} />
<div className={classes.spacer} />
<Button onClick={handleReset} variant="contained" color="primary">
Convert More Coins
</Button>
</>
</Collapse>
<div className={classes.loaderHolder}>
<Collapse in={!!relayInfo.isLoading && !relayInfo.isComplete}>
<div className={classes.loaderHolder}>
<CircleLoader />
<div className={classes.spacer} />
<div className={classes.spacer} />
<Typography variant="h5">
{"Your " +
getDefaultNativeCurrencySymbol(sourceChain) +
" is being converted to " +
getDefaultNativeCurrencySymbol(targetChain)}
</Typography>
<div className={classes.spacer} />
<Typography>{"Please wait a moment"}</Typography>
</div>
</Collapse>
</div>
<div className={classes.chainSelectorContainer}>
<Collapse
in={
!!ethWallet.provider &&
!relayInfo.isLoading &&
!relayInfo.isComplete &&
!sequence
}
>
{ethWallet.provider ? (
<>
{sourceContent}
{middleButton}
{targetContent}
<div className={classes.spacer} />
<div className={classes.spacer} />
</>
) : null}
{buttonContent}
</Collapse>
{!ethWallet.provider && walletButton}
</div>
</Paper>
<div className={classes.spacer} />
<Typography variant="subtitle1" color="textSecondary">
{"powered by wormhole"}
</Typography>
<img src={Wormhole} alt="Wormhole" className={classes.wormholeIcon} />
</Container>
</div>
);
}
export default Home;

View File

@ -0,0 +1,27 @@
{
"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-jsx"
},
"include": [
"src"
]
}

View File

@ -0,0 +1,85 @@
pragma solidity ^0.6.7;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
interface TokenBridge {
function transferTokens(address token, uint256 amount, uint16 recipientChain, bytes32 recipient, uint256 arbiterFee, uint32 nonce) external payable returns (uint64);
}
//https://github.com/scaffold-eth/scaffold-eth/blob/mvp-dex/packages/hardhat/contracts/YourDEX.sol
contract SimpleDex {
using SafeMath for uint256;
string public purpose = "Swapping Rabbits";
IERC20 rabbits;
constructor(address tokenAddress) public {
rabbits = IERC20(tokenAddress);
}
uint256 public totalLiquidity;
mapping (address => uint256) public liquidity;
function init(uint256 tokens) public payable returns (uint256) {
require(totalLiquidity==0,"DEX:init - already has liquidity");
totalLiquidity = address(this).balance;
liquidity[msg.sender] = totalLiquidity;
require(rabbits.transferFrom(msg.sender, address(this), tokens));
return totalLiquidity;
}
function price(uint256 input_amount, uint256 input_reserve, uint256 output_reserve) public pure returns (uint256) {
uint256 input_amount_with_fee = input_amount.mul(997);
uint256 numerator = input_amount_with_fee.mul(output_reserve);
uint256 denominator = input_reserve.mul(1000).add(input_amount_with_fee);
return numerator / denominator;
}
function ethToToken() public payable returns (uint256) {
uint256 token_reserve = rabbits.balanceOf(address(this));
uint256 tokens_bought = price(msg.value, address(this).balance.sub(msg.value), token_reserve);
require(rabbits.transfer(msg.sender, tokens_bought));
return tokens_bought;
}
function tokenToEth(uint256 tokens) public returns (uint256) {
uint256 token_reserve = rabbits.balanceOf(address(this));
uint256 eth_bought = price(tokens, token_reserve, address(this).balance);
msg.sender.transfer(eth_bought);
require(rabbits.transferFrom(msg.sender, address(this), tokens));
return eth_bought;
}
function deposit() public payable returns (uint256) {
uint256 eth_reserve = address(this).balance.sub(msg.value);
uint256 token_reserve = rabbits.balanceOf(address(this));
uint256 token_amount = (msg.value.mul(token_reserve) / eth_reserve).add(1);
uint256 liquidity_minted = msg.value.mul(totalLiquidity) / eth_reserve;
liquidity[msg.sender] = liquidity[msg.sender].add(liquidity_minted);
totalLiquidity = totalLiquidity.add(liquidity_minted);
require(rabbits.transferFrom(msg.sender, address(this), token_amount));
return liquidity_minted;
}
function withdraw(uint256 amount) public returns (uint256, uint256) {
uint256 token_reserve = rabbits.balanceOf(address(this));
uint256 eth_amount = amount.mul(address(this).balance) / totalLiquidity;
uint256 token_amount = amount.mul(token_reserve) / totalLiquidity;
liquidity[msg.sender] = liquidity[msg.sender].sub(eth_amount);
totalLiquidity = totalLiquidity.sub(eth_amount);
msg.sender.transfer(eth_amount);
require(rabbits.transfer(msg.sender, token_amount));
return (eth_amount, token_amount);
}
function swapNGo(address _address, uint16 targetChain, bytes32 targetAddress, uint256 fee, uint32 nonce) public payable returns (uint64) {
uint256 token_reserve = rabbits.balanceOf(address(this));
uint256 tokens_bought = price(msg.value, address(this).balance.sub(msg.value), token_reserve);
rabbits.approve(_address, tokens_bought);
return TokenBridge(_address).transferTokens(address(rabbits), tokens_bought, targetChain, targetAddress, fee, nonce);
}
}

View File

@ -0,0 +1,384 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import {
ethers,
EventFilter,
Signer,
BigNumber,
BigNumberish,
PopulatedTransaction,
BaseContract,
ContractTransaction,
Overrides,
CallOverrides,
} from "ethers";
import {BytesLike} from "@ethersproject/bytes";
import {Listener, Provider} from "@ethersproject/providers";
import {FunctionFragment, EventFragment, Result} from "@ethersproject/abi";
import {TypedEventFilter, TypedEvent, TypedListener} from "./commons";
interface IERC20Interface extends ethers.utils.Interface {
functions: {
"totalSupply()": FunctionFragment;
"balanceOf(address)": FunctionFragment;
"transfer(address,uint256)": FunctionFragment;
"allowance(address,address)": FunctionFragment;
"approve(address,uint256)": FunctionFragment;
"transferFrom(address,address,uint256)": FunctionFragment;
};
encodeFunctionData(
functionFragment: "totalSupply",
values?: undefined
): string;
encodeFunctionData(functionFragment: "balanceOf", values: [string]): string;
encodeFunctionData(
functionFragment: "transfer",
values: [string, BigNumberish]
): string;
encodeFunctionData(
functionFragment: "allowance",
values: [string, string]
): string;
encodeFunctionData(
functionFragment: "approve",
values: [string, BigNumberish]
): string;
encodeFunctionData(
functionFragment: "transferFrom",
values: [string, string, BigNumberish]
): string;
decodeFunctionResult(
functionFragment: "totalSupply",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "balanceOf", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "transfer", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "allowance", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "approve", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "transferFrom",
data: BytesLike
): Result;
events: {
"Approval(address,address,uint256)": EventFragment;
"Transfer(address,address,uint256)": EventFragment;
};
getEvent(nameOrSignatureOrTopic: "Approval"): EventFragment;
getEvent(nameOrSignatureOrTopic: "Transfer"): EventFragment;
}
export class IERC20 extends BaseContract {
connect(signerOrProvider: Signer | Provider | string): this;
attach(addressOrName: string): this;
deployed(): Promise<this>;
listeners<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter?: TypedEventFilter<EventArgsArray, EventArgsObject>
): Array<TypedListener<EventArgsArray, EventArgsObject>>;
off<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
listener: TypedListener<EventArgsArray, EventArgsObject>
): this;
on<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
listener: TypedListener<EventArgsArray, EventArgsObject>
): this;
once<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
listener: TypedListener<EventArgsArray, EventArgsObject>
): this;
removeListener<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
listener: TypedListener<EventArgsArray, EventArgsObject>
): this;
removeAllListeners<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>
): this;
listeners(eventName?: string): Array<Listener>;
off(eventName: string, listener: Listener): this;
on(eventName: string, listener: Listener): this;
once(eventName: string, listener: Listener): this;
removeListener(eventName: string, listener: Listener): this;
removeAllListeners(eventName?: string): this;
queryFilter<EventArgsArray extends Array<any>, EventArgsObject>(
event: TypedEventFilter<EventArgsArray, EventArgsObject>,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TypedEvent<EventArgsArray & EventArgsObject>>>;
interface: IERC20Interface;
functions: {
/**
* Returns the amount of tokens in existence.
*/
totalSupply(overrides?: CallOverrides): Promise<[BigNumber]>;
/**
* Returns the amount of tokens owned by `account`.
*/
balanceOf(account: string, overrides?: CallOverrides): Promise<[BigNumber]>;
/**
* Moves `amount` tokens from the caller's account to `recipient`. * Returns a boolean value indicating whether the operation succeeded. * Emits a {Transfer} event.
*/
transfer(
recipient: string,
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
/**
* Returns the remaining number of tokens that `spender` will be allowed to spend on behalf of `owner` through {transferFrom}. This is zero by default. * This value changes when {approve} or {transferFrom} are called.
*/
allowance(
owner: string,
spender: string,
overrides?: CallOverrides
): Promise<[BigNumber]>;
/**
* Sets `amount` as the allowance of `spender` over the caller's tokens. * Returns a boolean value indicating whether the operation succeeded. * IMPORTANT: Beware that changing an allowance with this method brings the risk that someone may use both the old and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * Emits an {Approval} event.
*/
approve(
spender: string,
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
/**
* Moves `amount` tokens from `sender` to `recipient` using the allowance mechanism. `amount` is then deducted from the caller's allowance. * Returns a boolean value indicating whether the operation succeeded. * Emits a {Transfer} event.
*/
transferFrom(
sender: string,
recipient: string,
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
};
/**
* Returns the amount of tokens in existence.
*/
totalSupply(overrides?: CallOverrides): Promise<BigNumber>;
/**
* Returns the amount of tokens owned by `account`.
*/
balanceOf(account: string, overrides?: CallOverrides): Promise<BigNumber>;
/**
* Moves `amount` tokens from the caller's account to `recipient`. * Returns a boolean value indicating whether the operation succeeded. * Emits a {Transfer} event.
*/
transfer(
recipient: string,
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
/**
* Returns the remaining number of tokens that `spender` will be allowed to spend on behalf of `owner` through {transferFrom}. This is zero by default. * This value changes when {approve} or {transferFrom} are called.
*/
allowance(
owner: string,
spender: string,
overrides?: CallOverrides
): Promise<BigNumber>;
/**
* Sets `amount` as the allowance of `spender` over the caller's tokens. * Returns a boolean value indicating whether the operation succeeded. * IMPORTANT: Beware that changing an allowance with this method brings the risk that someone may use both the old and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * Emits an {Approval} event.
*/
approve(
spender: string,
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
/**
* Moves `amount` tokens from `sender` to `recipient` using the allowance mechanism. `amount` is then deducted from the caller's allowance. * Returns a boolean value indicating whether the operation succeeded. * Emits a {Transfer} event.
*/
transferFrom(
sender: string,
recipient: string,
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
callStatic: {
/**
* Returns the amount of tokens in existence.
*/
totalSupply(overrides?: CallOverrides): Promise<BigNumber>;
/**
* Returns the amount of tokens owned by `account`.
*/
balanceOf(account: string, overrides?: CallOverrides): Promise<BigNumber>;
/**
* Moves `amount` tokens from the caller's account to `recipient`. * Returns a boolean value indicating whether the operation succeeded. * Emits a {Transfer} event.
*/
transfer(
recipient: string,
amount: BigNumberish,
overrides?: CallOverrides
): Promise<boolean>;
/**
* Returns the remaining number of tokens that `spender` will be allowed to spend on behalf of `owner` through {transferFrom}. This is zero by default. * This value changes when {approve} or {transferFrom} are called.
*/
allowance(
owner: string,
spender: string,
overrides?: CallOverrides
): Promise<BigNumber>;
/**
* Sets `amount` as the allowance of `spender` over the caller's tokens. * Returns a boolean value indicating whether the operation succeeded. * IMPORTANT: Beware that changing an allowance with this method brings the risk that someone may use both the old and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * Emits an {Approval} event.
*/
approve(
spender: string,
amount: BigNumberish,
overrides?: CallOverrides
): Promise<boolean>;
/**
* Moves `amount` tokens from `sender` to `recipient` using the allowance mechanism. `amount` is then deducted from the caller's allowance. * Returns a boolean value indicating whether the operation succeeded. * Emits a {Transfer} event.
*/
transferFrom(
sender: string,
recipient: string,
amount: BigNumberish,
overrides?: CallOverrides
): Promise<boolean>;
};
filters: {
Approval(
owner?: string | null,
spender?: string | null,
value?: null
): TypedEventFilter<
[string, string, BigNumber],
{owner: string; spender: string; value: BigNumber}
>;
Transfer(
from?: string | null,
to?: string | null,
value?: null
): TypedEventFilter<
[string, string, BigNumber],
{from: string; to: string; value: BigNumber}
>;
};
estimateGas: {
/**
* Returns the amount of tokens in existence.
*/
totalSupply(overrides?: CallOverrides): Promise<BigNumber>;
/**
* Returns the amount of tokens owned by `account`.
*/
balanceOf(account: string, overrides?: CallOverrides): Promise<BigNumber>;
/**
* Moves `amount` tokens from the caller's account to `recipient`. * Returns a boolean value indicating whether the operation succeeded. * Emits a {Transfer} event.
*/
transfer(
recipient: string,
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<BigNumber>;
/**
* Returns the remaining number of tokens that `spender` will be allowed to spend on behalf of `owner` through {transferFrom}. This is zero by default. * This value changes when {approve} or {transferFrom} are called.
*/
allowance(
owner: string,
spender: string,
overrides?: CallOverrides
): Promise<BigNumber>;
/**
* Sets `amount` as the allowance of `spender` over the caller's tokens. * Returns a boolean value indicating whether the operation succeeded. * IMPORTANT: Beware that changing an allowance with this method brings the risk that someone may use both the old and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * Emits an {Approval} event.
*/
approve(
spender: string,
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<BigNumber>;
/**
* Moves `amount` tokens from `sender` to `recipient` using the allowance mechanism. `amount` is then deducted from the caller's allowance. * Returns a boolean value indicating whether the operation succeeded. * Emits a {Transfer} event.
*/
transferFrom(
sender: string,
recipient: string,
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<BigNumber>;
};
populateTransaction: {
/**
* Returns the amount of tokens in existence.
*/
totalSupply(overrides?: CallOverrides): Promise<PopulatedTransaction>;
/**
* Returns the amount of tokens owned by `account`.
*/
balanceOf(
account: string,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
/**
* Moves `amount` tokens from the caller's account to `recipient`. * Returns a boolean value indicating whether the operation succeeded. * Emits a {Transfer} event.
*/
transfer(
recipient: string,
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<PopulatedTransaction>;
/**
* Returns the remaining number of tokens that `spender` will be allowed to spend on behalf of `owner` through {transferFrom}. This is zero by default. * This value changes when {approve} or {transferFrom} are called.
*/
allowance(
owner: string,
spender: string,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
/**
* Sets `amount` as the allowance of `spender` over the caller's tokens. * Returns a boolean value indicating whether the operation succeeded. * IMPORTANT: Beware that changing an allowance with this method brings the risk that someone may use both the old and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * Emits an {Approval} event.
*/
approve(
spender: string,
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<PopulatedTransaction>;
/**
* Moves `amount` tokens from `sender` to `recipient` using the allowance mechanism. `amount` is then deducted from the caller's allowance. * Returns a boolean value indicating whether the operation succeeded. * Emits a {Transfer} event.
*/
transferFrom(
sender: string,
recipient: string,
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<PopulatedTransaction>;
};
}

View File

@ -0,0 +1,356 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import {
ethers,
EventFilter,
Signer,
BigNumber,
BigNumberish,
PopulatedTransaction,
BaseContract,
ContractTransaction,
Overrides,
PayableOverrides,
CallOverrides,
} from "ethers";
import {BytesLike} from "@ethersproject/bytes";
import {Listener, Provider} from "@ethersproject/providers";
import {FunctionFragment, EventFragment, Result} from "@ethersproject/abi";
import {TypedEventFilter, TypedEvent, TypedListener} from "./commons";
interface SimpleDexInterface extends ethers.utils.Interface {
functions: {
"liquidity(address)": FunctionFragment;
"purpose()": FunctionFragment;
"totalLiquidity()": FunctionFragment;
"init(uint256)": FunctionFragment;
"price(uint256,uint256,uint256)": FunctionFragment;
"ethToToken()": FunctionFragment;
"tokenToEth(uint256)": FunctionFragment;
"deposit()": FunctionFragment;
"withdraw(uint256)": FunctionFragment;
"swapNGo(address,uint16,bytes32,uint256,uint32)": FunctionFragment;
};
encodeFunctionData(functionFragment: "liquidity", values: [string]): string;
encodeFunctionData(functionFragment: "purpose", values?: undefined): string;
encodeFunctionData(
functionFragment: "totalLiquidity",
values?: undefined
): string;
encodeFunctionData(functionFragment: "init", values: [BigNumberish]): string;
encodeFunctionData(
functionFragment: "price",
values: [BigNumberish, BigNumberish, BigNumberish]
): string;
encodeFunctionData(
functionFragment: "ethToToken",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "tokenToEth",
values: [BigNumberish]
): string;
encodeFunctionData(functionFragment: "deposit", values?: undefined): string;
encodeFunctionData(
functionFragment: "withdraw",
values: [BigNumberish]
): string;
encodeFunctionData(
functionFragment: "swapNGo",
values: [string, BigNumberish, BytesLike, BigNumberish, BigNumberish]
): string;
decodeFunctionResult(functionFragment: "liquidity", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "purpose", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "totalLiquidity",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "init", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "price", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "ethToToken", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "tokenToEth", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "deposit", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "withdraw", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "swapNGo", data: BytesLike): Result;
events: {};
}
export class SimpleDex extends BaseContract {
connect(signerOrProvider: Signer | Provider | string): this;
attach(addressOrName: string): this;
deployed(): Promise<this>;
listeners<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter?: TypedEventFilter<EventArgsArray, EventArgsObject>
): Array<TypedListener<EventArgsArray, EventArgsObject>>;
off<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
listener: TypedListener<EventArgsArray, EventArgsObject>
): this;
on<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
listener: TypedListener<EventArgsArray, EventArgsObject>
): this;
once<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
listener: TypedListener<EventArgsArray, EventArgsObject>
): this;
removeListener<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
listener: TypedListener<EventArgsArray, EventArgsObject>
): this;
removeAllListeners<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>
): this;
listeners(eventName?: string): Array<Listener>;
off(eventName: string, listener: Listener): this;
on(eventName: string, listener: Listener): this;
once(eventName: string, listener: Listener): this;
removeListener(eventName: string, listener: Listener): this;
removeAllListeners(eventName?: string): this;
queryFilter<EventArgsArray extends Array<any>, EventArgsObject>(
event: TypedEventFilter<EventArgsArray, EventArgsObject>,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TypedEvent<EventArgsArray & EventArgsObject>>>;
interface: SimpleDexInterface;
functions: {
liquidity(arg0: string, overrides?: CallOverrides): Promise<[BigNumber]>;
purpose(overrides?: CallOverrides): Promise<[string]>;
totalLiquidity(overrides?: CallOverrides): Promise<[BigNumber]>;
init(
tokens: BigNumberish,
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
price(
input_amount: BigNumberish,
input_reserve: BigNumberish,
output_reserve: BigNumberish,
overrides?: CallOverrides
): Promise<[BigNumber]>;
ethToToken(
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
tokenToEth(
tokens: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
deposit(
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
withdraw(
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
swapNGo(
_address: string,
targetChain: BigNumberish,
targetAddress: BytesLike,
fee: BigNumberish,
nonce: BigNumberish,
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
};
liquidity(arg0: string, overrides?: CallOverrides): Promise<BigNumber>;
purpose(overrides?: CallOverrides): Promise<string>;
totalLiquidity(overrides?: CallOverrides): Promise<BigNumber>;
init(
tokens: BigNumberish,
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
price(
input_amount: BigNumberish,
input_reserve: BigNumberish,
output_reserve: BigNumberish,
overrides?: CallOverrides
): Promise<BigNumber>;
ethToToken(
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
tokenToEth(
tokens: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
deposit(
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
withdraw(
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
swapNGo(
_address: string,
targetChain: BigNumberish,
targetAddress: BytesLike,
fee: BigNumberish,
nonce: BigNumberish,
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
callStatic: {
liquidity(arg0: string, overrides?: CallOverrides): Promise<BigNumber>;
purpose(overrides?: CallOverrides): Promise<string>;
totalLiquidity(overrides?: CallOverrides): Promise<BigNumber>;
init(tokens: BigNumberish, overrides?: CallOverrides): Promise<BigNumber>;
price(
input_amount: BigNumberish,
input_reserve: BigNumberish,
output_reserve: BigNumberish,
overrides?: CallOverrides
): Promise<BigNumber>;
ethToToken(overrides?: CallOverrides): Promise<BigNumber>;
tokenToEth(
tokens: BigNumberish,
overrides?: CallOverrides
): Promise<BigNumber>;
deposit(overrides?: CallOverrides): Promise<BigNumber>;
withdraw(
amount: BigNumberish,
overrides?: CallOverrides
): Promise<[BigNumber, BigNumber]>;
swapNGo(
_address: string,
targetChain: BigNumberish,
targetAddress: BytesLike,
fee: BigNumberish,
nonce: BigNumberish,
overrides?: CallOverrides
): Promise<BigNumber>;
};
filters: {};
estimateGas: {
liquidity(arg0: string, overrides?: CallOverrides): Promise<BigNumber>;
purpose(overrides?: CallOverrides): Promise<BigNumber>;
totalLiquidity(overrides?: CallOverrides): Promise<BigNumber>;
init(
tokens: BigNumberish,
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<BigNumber>;
price(
input_amount: BigNumberish,
input_reserve: BigNumberish,
output_reserve: BigNumberish,
overrides?: CallOverrides
): Promise<BigNumber>;
ethToToken(
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<BigNumber>;
tokenToEth(
tokens: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<BigNumber>;
deposit(
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<BigNumber>;
withdraw(
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<BigNumber>;
swapNGo(
_address: string,
targetChain: BigNumberish,
targetAddress: BytesLike,
fee: BigNumberish,
nonce: BigNumberish,
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<BigNumber>;
};
populateTransaction: {
liquidity(
arg0: string,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
purpose(overrides?: CallOverrides): Promise<PopulatedTransaction>;
totalLiquidity(overrides?: CallOverrides): Promise<PopulatedTransaction>;
init(
tokens: BigNumberish,
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<PopulatedTransaction>;
price(
input_amount: BigNumberish,
input_reserve: BigNumberish,
output_reserve: BigNumberish,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
ethToToken(
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<PopulatedTransaction>;
tokenToEth(
tokens: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<PopulatedTransaction>;
deposit(
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<PopulatedTransaction>;
withdraw(
amount: BigNumberish,
overrides?: Overrides & {from?: string | Promise<string>}
): Promise<PopulatedTransaction>;
swapNGo(
_address: string,
targetChain: BigNumberish,
targetAddress: BytesLike,
fee: BigNumberish,
nonce: BigNumberish,
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<PopulatedTransaction>;
};
}

View File

@ -0,0 +1,149 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import {
ethers,
EventFilter,
Signer,
BigNumber,
BigNumberish,
PopulatedTransaction,
BaseContract,
ContractTransaction,
PayableOverrides,
CallOverrides,
} from "ethers";
import {BytesLike} from "@ethersproject/bytes";
import {Listener, Provider} from "@ethersproject/providers";
import {FunctionFragment, EventFragment, Result} from "@ethersproject/abi";
import {TypedEventFilter, TypedEvent, TypedListener} from "./commons";
interface TokenBridgeInterface extends ethers.utils.Interface {
functions: {
"transferTokens(address,uint256,uint16,bytes32,uint256,uint32)": FunctionFragment;
};
encodeFunctionData(
functionFragment: "transferTokens",
values: [
string,
BigNumberish,
BigNumberish,
BytesLike,
BigNumberish,
BigNumberish
]
): string;
decodeFunctionResult(
functionFragment: "transferTokens",
data: BytesLike
): Result;
events: {};
}
export class TokenBridge extends BaseContract {
connect(signerOrProvider: Signer | Provider | string): this;
attach(addressOrName: string): this;
deployed(): Promise<this>;
listeners<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter?: TypedEventFilter<EventArgsArray, EventArgsObject>
): Array<TypedListener<EventArgsArray, EventArgsObject>>;
off<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
listener: TypedListener<EventArgsArray, EventArgsObject>
): this;
on<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
listener: TypedListener<EventArgsArray, EventArgsObject>
): this;
once<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
listener: TypedListener<EventArgsArray, EventArgsObject>
): this;
removeListener<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
listener: TypedListener<EventArgsArray, EventArgsObject>
): this;
removeAllListeners<EventArgsArray extends Array<any>, EventArgsObject>(
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>
): this;
listeners(eventName?: string): Array<Listener>;
off(eventName: string, listener: Listener): this;
on(eventName: string, listener: Listener): this;
once(eventName: string, listener: Listener): this;
removeListener(eventName: string, listener: Listener): this;
removeAllListeners(eventName?: string): this;
queryFilter<EventArgsArray extends Array<any>, EventArgsObject>(
event: TypedEventFilter<EventArgsArray, EventArgsObject>,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TypedEvent<EventArgsArray & EventArgsObject>>>;
interface: TokenBridgeInterface;
functions: {
transferTokens(
token: string,
amount: BigNumberish,
recipientChain: BigNumberish,
recipient: BytesLike,
arbiterFee: BigNumberish,
nonce: BigNumberish,
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
};
transferTokens(
token: string,
amount: BigNumberish,
recipientChain: BigNumberish,
recipient: BytesLike,
arbiterFee: BigNumberish,
nonce: BigNumberish,
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<ContractTransaction>;
callStatic: {
transferTokens(
token: string,
amount: BigNumberish,
recipientChain: BigNumberish,
recipient: BytesLike,
arbiterFee: BigNumberish,
nonce: BigNumberish,
overrides?: CallOverrides
): Promise<BigNumber>;
};
filters: {};
estimateGas: {
transferTokens(
token: string,
amount: BigNumberish,
recipientChain: BigNumberish,
recipient: BytesLike,
arbiterFee: BigNumberish,
nonce: BigNumberish,
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<BigNumber>;
};
populateTransaction: {
transferTokens(
token: string,
amount: BigNumberish,
recipientChain: BigNumberish,
recipient: BytesLike,
arbiterFee: BigNumberish,
nonce: BigNumberish,
overrides?: PayableOverrides & {from?: string | Promise<string>}
): Promise<PopulatedTransaction>;
};
}

View File

@ -0,0 +1,36 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import {EventFilter, Event} from "ethers";
import {Result} from "@ethersproject/abi";
export interface TypedEventFilter<_EventArgsArray, _EventArgsObject>
extends EventFilter {}
export interface TypedEvent<EventArgs extends Result> extends Event {
args: EventArgs;
}
export type TypedListener<
EventArgsArray extends Array<any>,
EventArgsObject
> = (
...listenerArg: [
...EventArgsArray,
TypedEvent<EventArgsArray & EventArgsObject>
]
) => void;
export type MinEthersFactory<C, ARGS> = {
deploy(...a: ARGS[]): Promise<C>;
};
export type GetContractTypeFromFactory<F> = F extends MinEthersFactory<
infer C,
any
>
? C
: never;
export type GetARGsTypeFromFactory<F> = F extends MinEthersFactory<any, any>
? Parameters<F["deploy"]>
: never;

View File

@ -0,0 +1,203 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import {Contract, Signer, utils} from "ethers";
import {Provider} from "@ethersproject/providers";
import type {IERC20, IERC20Interface} from "../IERC20";
const _abi = [
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "owner",
type: "address",
},
{
indexed: true,
internalType: "address",
name: "spender",
type: "address",
},
{
indexed: false,
internalType: "uint256",
name: "value",
type: "uint256",
},
],
name: "Approval",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "from",
type: "address",
},
{
indexed: true,
internalType: "address",
name: "to",
type: "address",
},
{
indexed: false,
internalType: "uint256",
name: "value",
type: "uint256",
},
],
name: "Transfer",
type: "event",
},
{
inputs: [],
name: "totalSupply",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "account",
type: "address",
},
],
name: "balanceOf",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "recipient",
type: "address",
},
{
internalType: "uint256",
name: "amount",
type: "uint256",
},
],
name: "transfer",
outputs: [
{
internalType: "bool",
name: "",
type: "bool",
},
],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "owner",
type: "address",
},
{
internalType: "address",
name: "spender",
type: "address",
},
],
name: "allowance",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "spender",
type: "address",
},
{
internalType: "uint256",
name: "amount",
type: "uint256",
},
],
name: "approve",
outputs: [
{
internalType: "bool",
name: "",
type: "bool",
},
],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "sender",
type: "address",
},
{
internalType: "address",
name: "recipient",
type: "address",
},
{
internalType: "uint256",
name: "amount",
type: "uint256",
},
],
name: "transferFrom",
outputs: [
{
internalType: "bool",
name: "",
type: "bool",
},
],
stateMutability: "nonpayable",
type: "function",
},
];
export class IERC20__factory {
static readonly abi = _abi;
static createInterface(): IERC20Interface {
return new utils.Interface(_abi) as IERC20Interface;
}
static connect(address: string, signerOrProvider: Signer | Provider): IERC20 {
return new Contract(address, _abi, signerOrProvider) as IERC20;
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,67 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import {Contract, Signer, utils} from "ethers";
import {Provider} from "@ethersproject/providers";
import type {TokenBridge, TokenBridgeInterface} from "../TokenBridge";
const _abi = [
{
inputs: [
{
internalType: "address",
name: "token",
type: "address",
},
{
internalType: "uint256",
name: "amount",
type: "uint256",
},
{
internalType: "uint16",
name: "recipientChain",
type: "uint16",
},
{
internalType: "bytes32",
name: "recipient",
type: "bytes32",
},
{
internalType: "uint256",
name: "arbiterFee",
type: "uint256",
},
{
internalType: "uint32",
name: "nonce",
type: "uint32",
},
],
name: "transferTokens",
outputs: [
{
internalType: "uint64",
name: "",
type: "uint64",
},
],
stateMutability: "payable",
type: "function",
},
];
export class TokenBridge__factory {
static readonly abi = _abi;
static createInterface(): TokenBridgeInterface {
return new utils.Interface(_abi) as TokenBridgeInterface;
}
static connect(
address: string,
signerOrProvider: Signer | Provider
): TokenBridge {
return new Contract(address, _abi, signerOrProvider) as TokenBridge;
}
}

View File

@ -0,0 +1,10 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
export type {IERC20} from "./IERC20";
export type {SimpleDex} from "./SimpleDex";
export type {TokenBridge} from "./TokenBridge";
export {IERC20__factory} from "./factories/IERC20__factory";
export {SimpleDex__factory} from "./factories/SimpleDex__factory";
export {TokenBridge__factory} from "./factories/TokenBridge__factory";

View File

@ -0,0 +1,5 @@
"use strict";
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
Object.defineProperty(exports, "__esModule", { value: true });

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,177 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SOLANA_TEST_WALLET_PUBLIC_KEY = exports.SOLANA_TEST_TOKEN = exports.ETH_TEST_WALLET_PUBLIC_KEY = exports.getSignerForChain = exports.SOLANA_PRIVATE_KEY = exports.ETH_PRIVATE_KEY = exports.BSC_NODE_URL = exports.POLYGON_NODE_URL = exports.ETH_NODE_URL = exports.WORMHOLE_RPC_HOSTS = exports.getTokenBridgeAddressForChain = exports.getNFTBridgeAddressForChain = exports.getBridgeAddressForChain = exports.TERRA_TOKEN_BRIDGE_ADDRESS = exports.TERRA_BRIDGE_ADDRESS = exports.SOL_TOKEN_BRIDGE_ADDRESS = exports.SOL_NFT_BRIDGE_ADDRESS = exports.SOL_BRIDGE_ADDRESS = exports.POLYGON_TOKEN_BRIDGE_ADDRESS = exports.POLYGON_NFT_BRIDGE_ADDRESS = exports.POLYGON_BRIDGE_ADDRESS = exports.BSC_TOKEN_BRIDGE_ADDRESS = exports.BSC_NFT_BRIDGE_ADDRESS = exports.BSC_BRIDGE_ADDRESS = exports.ETH_TOKEN_BRIDGE_ADDRESS = exports.ETH_NFT_BRIDGE_ADDRESS = exports.ETH_BRIDGE_ADDRESS = exports.TERRA_HOST = exports.SOLANA_HOST = exports.CLUSTER = void 0;
var wormhole_sdk_1 = require("@certusone/wormhole-sdk");
var web3_js_1 = require("@solana/web3.js");
var ethers_1 = require("ethers");
var utils_1 = require("ethers/lib/utils");
exports.CLUSTER = "devnet"; //This is the currently selected environment.
exports.SOLANA_HOST = process.env.REACT_APP_SOLANA_API_URL
? process.env.REACT_APP_SOLANA_API_URL
: exports.CLUSTER === "mainnet"
? web3_js_1.clusterApiUrl("mainnet-beta")
: exports.CLUSTER === "testnet"
? web3_js_1.clusterApiUrl("testnet")
: "http://localhost:8899";
exports.TERRA_HOST = exports.CLUSTER === "mainnet"
? {
URL: "https://lcd.terra.dev",
chainID: "columbus-5",
name: "mainnet",
}
: exports.CLUSTER === "testnet"
? {
URL: "https://bombay-lcd.terra.dev",
chainID: "bombay-12",
name: "testnet",
}
: {
URL: "http://localhost:1317",
chainID: "columbus-5",
name: "localterra",
};
exports.ETH_BRIDGE_ADDRESS = utils_1.getAddress(exports.CLUSTER === "mainnet"
? "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B"
: exports.CLUSTER === "testnet"
? "0x44F3e7c20850B3B5f3031114726A9240911D912a"
: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550");
exports.ETH_NFT_BRIDGE_ADDRESS = utils_1.getAddress(exports.CLUSTER === "mainnet"
? "0x6FFd7EdE62328b3Af38FCD61461Bbfc52F5651fE"
: exports.CLUSTER === "testnet"
? "0x26b4afb60d6c903165150c6f0aa14f8016be4aec" // TODO: test address
: "0x26b4afb60d6c903165150c6f0aa14f8016be4aec");
exports.ETH_TOKEN_BRIDGE_ADDRESS = utils_1.getAddress(exports.CLUSTER === "mainnet"
? "0x3ee18B2214AFF97000D974cf647E7C347E8fa585"
: exports.CLUSTER === "testnet"
? "0xa6CDAddA6e4B6704705b065E01E52e2486c0FBf6"
: "0x0290FB167208Af455bB137780163b7B7a9a10C16");
exports.BSC_BRIDGE_ADDRESS = utils_1.getAddress(exports.CLUSTER === "mainnet"
? "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B"
: exports.CLUSTER === "testnet"
? "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550" // TODO: test address
: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550");
exports.BSC_NFT_BRIDGE_ADDRESS = utils_1.getAddress(exports.CLUSTER === "mainnet"
? "0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE"
: exports.CLUSTER === "testnet"
? "0x26b4afb60d6c903165150c6f0aa14f8016be4aec" // TODO: test address
: "0x26b4afb60d6c903165150c6f0aa14f8016be4aec");
exports.BSC_TOKEN_BRIDGE_ADDRESS = utils_1.getAddress(exports.CLUSTER === "mainnet"
? "0xB6F6D86a8f9879A9c87f643768d9efc38c1Da6E7"
: exports.CLUSTER === "testnet"
? "0x0290FB167208Af455bB137780163b7B7a9a10C16" // TODO: test address
: "0x0290FB167208Af455bB137780163b7B7a9a10C16");
exports.POLYGON_BRIDGE_ADDRESS = utils_1.getAddress(exports.CLUSTER === "mainnet"
? "0x7A4B5a56256163F07b2C80A7cA55aBE66c4ec4d7"
: exports.CLUSTER === "testnet"
? "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550" // TODO: test address
: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550");
exports.POLYGON_NFT_BRIDGE_ADDRESS = utils_1.getAddress(exports.CLUSTER === "mainnet"
? "0x90BBd86a6Fe93D3bc3ed6335935447E75fAb7fCf"
: exports.CLUSTER === "testnet"
? "0x26b4afb60d6c903165150c6f0aa14f8016be4aec" // TODO: test address
: "0x26b4afb60d6c903165150c6f0aa14f8016be4aec");
exports.POLYGON_TOKEN_BRIDGE_ADDRESS = utils_1.getAddress(exports.CLUSTER === "mainnet"
? "0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE"
: exports.CLUSTER === "testnet"
? "0x0290FB167208Af455bB137780163b7B7a9a10C16" // TODO: test address
: "0x0290FB167208Af455bB137780163b7B7a9a10C16");
exports.SOL_BRIDGE_ADDRESS = exports.CLUSTER === "mainnet"
? "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth"
: exports.CLUSTER === "testnet"
? "Brdguy7BmNB4qwEbcqqMbyV5CyJd2sxQNUn6NEpMSsUb"
: "Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o";
exports.SOL_NFT_BRIDGE_ADDRESS = exports.CLUSTER === "mainnet"
? "WnFt12ZrnzZrFZkt2xsNsaNWoQribnuQ5B5FrDbwDhD"
: exports.CLUSTER === "testnet"
? "NFTWqJR8YnRVqPDvTJrYuLrQDitTG5AScqbeghi4zSA" // TODO: test address
: "NFTWqJR8YnRVqPDvTJrYuLrQDitTG5AScqbeghi4zSA";
exports.SOL_TOKEN_BRIDGE_ADDRESS = exports.CLUSTER === "mainnet"
? "wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb"
: exports.CLUSTER === "testnet"
? "A4Us8EhCC76XdGAN17L4KpRNEK423nMivVHZzZqFqqBg"
: "B6RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE";
exports.TERRA_BRIDGE_ADDRESS = exports.CLUSTER === "mainnet"
? "terra1dq03ugtd40zu9hcgdzrsq6z2z4hwhc9tqk2uy5"
: exports.CLUSTER === "testnet"
? "terra18vd8fpwxzck93qlwghaj6arh4p7c5n896xzem5"
: "terra18vd8fpwxzck93qlwghaj6arh4p7c5n896xzem5";
exports.TERRA_TOKEN_BRIDGE_ADDRESS = exports.CLUSTER === "mainnet"
? "terra10nmmwe8r3g99a9newtqa7a75xfgs2e8z87r2sf"
: exports.CLUSTER === "testnet"
? "terra10pyejy66429refv3g35g2t7am0was7ya7kz2a4"
: "terra10pyejy66429refv3g35g2t7am0was7ya7kz2a4";
var getBridgeAddressForChain = function (chainId) {
return chainId === wormhole_sdk_1.CHAIN_ID_SOLANA
? exports.SOL_BRIDGE_ADDRESS
: chainId === wormhole_sdk_1.CHAIN_ID_ETH
? exports.ETH_BRIDGE_ADDRESS
: chainId === wormhole_sdk_1.CHAIN_ID_BSC
? exports.BSC_BRIDGE_ADDRESS
: chainId === wormhole_sdk_1.CHAIN_ID_TERRA
? exports.TERRA_BRIDGE_ADDRESS
: chainId === wormhole_sdk_1.CHAIN_ID_POLYGON
? exports.POLYGON_BRIDGE_ADDRESS
: "";
};
exports.getBridgeAddressForChain = getBridgeAddressForChain;
var getNFTBridgeAddressForChain = function (chainId) {
return chainId === wormhole_sdk_1.CHAIN_ID_SOLANA
? exports.SOL_NFT_BRIDGE_ADDRESS
: chainId === wormhole_sdk_1.CHAIN_ID_ETH
? exports.ETH_NFT_BRIDGE_ADDRESS
: chainId === wormhole_sdk_1.CHAIN_ID_BSC
? exports.BSC_NFT_BRIDGE_ADDRESS
: chainId === wormhole_sdk_1.CHAIN_ID_POLYGON
? exports.POLYGON_NFT_BRIDGE_ADDRESS
: "";
};
exports.getNFTBridgeAddressForChain = getNFTBridgeAddressForChain;
var getTokenBridgeAddressForChain = function (chainId) {
return chainId === wormhole_sdk_1.CHAIN_ID_SOLANA
? exports.SOL_TOKEN_BRIDGE_ADDRESS
: chainId === wormhole_sdk_1.CHAIN_ID_ETH
? exports.ETH_TOKEN_BRIDGE_ADDRESS
: chainId === wormhole_sdk_1.CHAIN_ID_BSC
? exports.BSC_TOKEN_BRIDGE_ADDRESS
: chainId === wormhole_sdk_1.CHAIN_ID_TERRA
? exports.TERRA_TOKEN_BRIDGE_ADDRESS
: chainId === wormhole_sdk_1.CHAIN_ID_POLYGON
? exports.POLYGON_TOKEN_BRIDGE_ADDRESS
: "";
};
exports.getTokenBridgeAddressForChain = getTokenBridgeAddressForChain;
exports.WORMHOLE_RPC_HOSTS = exports.CLUSTER === "mainnet"
? [
"https://wormhole-v2-mainnet-api.certus.one",
"https://wormhole.inotel.ro",
"https://wormhole-v2-mainnet-api.mcf.rocks",
"https://wormhole-v2-mainnet-api.chainlayer.network",
"https://wormhole-v2-mainnet-api.staking.fund",
"https://wormhole-v2-mainnet.01node.com",
]
: exports.CLUSTER === "testnet"
? ["https://wormhole-v2-testnet-api.certus.one"]
: ["http://localhost:7071"];
exports.ETH_NODE_URL = "ws://localhost:8545"; //TODO testnet
exports.POLYGON_NODE_URL = "ws:localhost:0000"; //TODO
exports.BSC_NODE_URL = "ws://localhost:8546"; //TODO testnet
exports.ETH_PRIVATE_KEY = "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d";
exports.SOLANA_PRIVATE_KEY = new Uint8Array([
14, 173, 153, 4, 176, 224, 201, 111, 32, 237, 183, 185, 159, 247, 22, 161, 89,
84, 215, 209, 212, 137, 10, 92, 157, 49, 29, 192, 101, 164, 152, 70, 87, 65,
8, 174, 214, 157, 175, 126, 98, 90, 54, 24, 100, 177, 247, 77, 19, 112, 47,
44, 165, 109, 233, 102, 14, 86, 109, 29, 134, 145, 132, 141,
]);
function getSignerForChain(chainId) {
var provider = new ethers_1.ethers.providers.WebSocketProvider(chainId === wormhole_sdk_1.CHAIN_ID_POLYGON
? exports.POLYGON_NODE_URL
: chainId === wormhole_sdk_1.CHAIN_ID_BSC
? exports.BSC_NODE_URL
: exports.ETH_NODE_URL);
var signer = new ethers_1.ethers.Wallet(exports.ETH_PRIVATE_KEY, provider);
return signer;
}
exports.getSignerForChain = getSignerForChain;
exports.ETH_TEST_WALLET_PUBLIC_KEY = "0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1";
exports.SOLANA_TEST_TOKEN = "2WDq7wSs9zYrpx2kbHDA4RUTRch2CCTP6ZWaH4GNfnQQ"; //SOLT on devnet
exports.SOLANA_TEST_WALLET_PUBLIC_KEY = "6sbzC1eH4FTujJXWj51eQe25cYvr4xfXbJ1vAj7j2k5J";

View File

@ -0,0 +1,247 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
var wormhole_sdk_1 = require("@certusone/wormhole-sdk");
var wasm_1 = require("@certusone/wormhole-sdk/lib/cjs/solana/wasm");
var address_1 = require("@ethersproject/address");
var units_1 = require("@ethersproject/units");
var ethers_1 = require("ethers");
var fs = require("fs");
var process_1 = require("process");
var SimpleDex__factory_1 = require("../ethers-contracts/abi/factories/SimpleDex__factory");
var consts_1 = require("./consts");
var commonWorkflows_1 = require("@certusone/wormhole-examples/lib/commonWorkflows");
wasm_1.setDefaultWasm("node");
//This script is reliant on core examples, and the wormhole SDK.
//It is meant to be run against a fresh devnet / tilt environment.
function getPrice(chain) {
if (chain === wormhole_sdk_1.CHAIN_ID_ETH) {
return 4400;
}
if (chain === wormhole_sdk_1.CHAIN_ID_BSC) {
return 630;
}
}
function main() {
return __awaiter(this, void 0, void 0, function () {
var _a, ethAddress, wbnbOnEth, bscAddress, wethOnBsc;
return __generator(this, function (_b) {
switch (_b.label) {
case 0: return [4 /*yield*/, configWormhole()];
case 1:
_b.sent();
return [4 /*yield*/, seedPools()];
case 2:
_a = _b.sent(), ethAddress = _a.ethAddress, wbnbOnEth = _a.wbnbOnEth, bscAddress = _a.bscAddress, wethOnBsc = _a.wethOnBsc;
console.log("Pools seeded");
return [4 /*yield*/, createSwapPoolFile(ethAddress, wbnbOnEth, bscAddress, wethOnBsc)];
case 3:
_b.sent();
console.log("Job done");
return [2 /*return*/, Promise.resolve()];
}
});
});
}
function configWormhole() {
return __awaiter(this, void 0, void 0, function () {
var basisTransferAmount, WETH;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
basisTransferAmount = "1";
WETH = "0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E";
console.log("Doing WETH Attest");
return [4 /*yield*/, commonWorkflows_1.fullAttestation(wormhole_sdk_1.CHAIN_ID_ETH, address_1.getAddress(WETH))];
case 1:
_a.sent();
console.log("Doing WBNB Attest");
return [4 /*yield*/, commonWorkflows_1.fullAttestation(wormhole_sdk_1.CHAIN_ID_BSC, address_1.getAddress(WETH))];
case 2:
_a.sent();
console.log("Bridging over WETH to bsc");
return [4 /*yield*/, commonWorkflows_1.basicTransfer(wormhole_sdk_1.CHAIN_ID_ETH, basisTransferAmount, wormhole_sdk_1.CHAIN_ID_BSC, consts_1.ETH_TEST_WALLET_PUBLIC_KEY, consts_1.ETH_TEST_WALLET_PUBLIC_KEY, true)];
case 3:
_a.sent();
console.log("Bridging over WBNB to eth");
return [4 /*yield*/, commonWorkflows_1.basicTransfer(wormhole_sdk_1.CHAIN_ID_BSC, basisTransferAmount, wormhole_sdk_1.CHAIN_ID_ETH, consts_1.ETH_TEST_WALLET_PUBLIC_KEY, consts_1.ETH_TEST_WALLET_PUBLIC_KEY, true)];
case 4:
_a.sent();
return [2 /*return*/];
}
});
});
}
function createSwapPoolFile(ethAddress, wbnbAddress, bscAddress, wethAddress) {
return __awaiter(this, void 0, void 0, function () {
var literal, content;
var _a, _b, _c;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
literal = (_a = {},
_a[wormhole_sdk_1.CHAIN_ID_ETH] = (_b = {},
_b[wormhole_sdk_1.CHAIN_ID_BSC] = { poolAddress: ethAddress, tokenAddress: wbnbAddress },
_b),
_a[wormhole_sdk_1.CHAIN_ID_BSC] = (_c = {},
_c[wormhole_sdk_1.CHAIN_ID_ETH] = { poolAddress: bscAddress, tokenAddress: wethAddress },
_c),
_a);
content = JSON.stringify(literal);
//TODO not this
return [4 /*yield*/, fs.writeFileSync("../react/src/swapPools.json", content, {
flag: "w+",
})];
case 1:
//TODO not this
_d.sent();
return [2 /*return*/];
}
});
});
}
//TODO, in a for loop for all the EVM chains
var seedPools = function () { return __awaiter(void 0, void 0, void 0, function () {
var ethSigner, bscSigner, currentEthPrice, currentSolPrice, ratio, ethBasis, bnbBasis, WETH, wbnbOnEth, wethOnBsc, contractInterface, bytecode, ethfactory, contract, ethAddress, bscfactory, bscContract, bscAddress, ethDex, bscDex, ethInit, bscInit, ethLiq, bscLiq;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
ethSigner = consts_1.getSignerForChain(wormhole_sdk_1.CHAIN_ID_ETH);
bscSigner = consts_1.getSignerForChain(wormhole_sdk_1.CHAIN_ID_BSC);
currentEthPrice = getPrice(wormhole_sdk_1.CHAIN_ID_ETH);
currentSolPrice = getPrice(wormhole_sdk_1.CHAIN_ID_BSC);
ratio = currentEthPrice / currentSolPrice;
ethBasis = 0.01;
bnbBasis = Math.ceil(ethBasis * ratio);
WETH = "0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E";
return [4 /*yield*/, wormhole_sdk_1.getForeignAssetEth(consts_1.getTokenBridgeAddressForChain(wormhole_sdk_1.CHAIN_ID_ETH), ethSigner.provider, wormhole_sdk_1.CHAIN_ID_BSC, wormhole_sdk_1.hexToUint8Array(wormhole_sdk_1.nativeToHexString(WETH, wormhole_sdk_1.CHAIN_ID_BSC)))];
case 1:
wbnbOnEth = _a.sent();
console.log("WBNB on ETH address", wbnbOnEth);
return [4 /*yield*/, wormhole_sdk_1.getForeignAssetEth(consts_1.getTokenBridgeAddressForChain(wormhole_sdk_1.CHAIN_ID_BSC), bscSigner.provider, wormhole_sdk_1.CHAIN_ID_ETH, wormhole_sdk_1.hexToUint8Array(wormhole_sdk_1.nativeToHexString(WETH, wormhole_sdk_1.CHAIN_ID_ETH)))];
case 2:
wethOnBsc = _a.sent();
console.log("WETH on BSC address", wethOnBsc);
console.log("about to deploy ETH contract");
contractInterface = SimpleDex__factory_1.SimpleDex__factory.createInterface();
bytecode = SimpleDex__factory_1.SimpleDex__factory.bytecode;
ethfactory = new ethers_1.ethers.ContractFactory(contractInterface, bytecode, ethSigner);
return [4 /*yield*/, ethfactory.deploy(address_1.getAddress(wbnbOnEth))];
case 3:
contract = _a.sent();
return [4 /*yield*/, contract.deployed().then(function (result) {
console.log("Successfully deployed contract at " + result.address);
return result.address;
}, function (error) {
console.error(error);
process_1.exit(1);
})];
case 4:
ethAddress = _a.sent();
console.log("about to deploy bsc contract");
bscfactory = new ethers_1.ethers.ContractFactory(contractInterface, bytecode, bscSigner);
return [4 /*yield*/, bscfactory.deploy(address_1.getAddress(wethOnBsc))];
case 5:
bscContract = _a.sent();
return [4 /*yield*/, bscContract.deployed().then(function (result) {
console.log("Successfully deployed contract at " + result.address);
return result.address;
}, function (error) {
console.error(error);
process_1.exit(1);
})];
case 6:
bscAddress = _a.sent();
console.log("Doing WBNB on ETH Approve");
return [4 /*yield*/, wormhole_sdk_1.approveEth(ethAddress, address_1.getAddress(wbnbOnEth), ethSigner, "10000000000000000000000")];
case 7:
_a.sent();
console.log("Doing WETH on BSC Approve");
return [4 /*yield*/, wormhole_sdk_1.approveEth(bscAddress, address_1.getAddress(wethOnBsc), bscSigner, "10000000000000000000000")];
case 8:
_a.sent();
ethDex = SimpleDex__factory_1.SimpleDex__factory.connect(ethAddress, ethSigner);
bscDex = SimpleDex__factory_1.SimpleDex__factory.connect(bscAddress, bscSigner);
console.log("Initializing eth pool");
return [4 /*yield*/, ethDex.init(units_1.parseUnits(bnbBasis.toString(), 18), {
value: units_1.parseUnits(ethBasis.toString(), 18),
gasLimit: 500000,
})];
case 9:
ethInit = _a.sent();
return [4 /*yield*/, ethInit.wait()];
case 10:
_a.sent();
console.log("Initializing bsc pool");
return [4 /*yield*/, bscDex.init(units_1.parseUnits(ethBasis.toString(), 18), {
value: units_1.parseUnits(bnbBasis.toString(), 18),
gasLimit: 500000,
})];
case 11:
bscInit = _a.sent();
return [4 /*yield*/, bscInit.wait()];
case 12:
_a.sent();
console.log("pools initialized");
return [4 /*yield*/, ethDex.totalLiquidity()];
case 13:
ethLiq = _a.sent();
console.log("Eth liquidity", ethLiq);
return [4 /*yield*/, bscDex.totalLiquidity()];
case 14:
bscLiq = _a.sent();
console.log("bsc liquidity", bscLiq);
//Pool should now be seeded with a small amount of ETH and Wormhole-Wrapped SOL.
return [2 /*return*/, { ethAddress: ethAddress, wbnbOnEth: wbnbOnEth, bscAddress: bscAddress, wethOnBsc: wethOnBsc }];
}
});
}); };
var done = false;
main().then(function () { return (done = true); }, function (error) {
console.error(error);
done = true;
});
function wait() {
if (!done) {
setTimeout(wait, 1000);
}
else {
process_1.exit(0);
}
}
wait();

View File

@ -0,0 +1,5 @@
const Migrations = artifacts.require("Migrations");
module.exports = function (deployer) {
deployer.deploy(Migrations);
};

58681
EthSwapNGo/swapPool/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,40 @@
{
"name": "swappool",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"genTypes": "typechain --target ethers-v5 --out-dir ethers-contracts/abi --show-stack-traces ./build/contracts/*.json",
"generate": "rimraf lib && rimraf ethers-contracts && rimraf build && truffle compile && npm run genTypes",
"deployAndSeed": "npm run generate && tsc && node lib/scripts/deployAndSeed.js"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@improbable-eng/grpc-web-node-http-transport": "^0.15.0",
"@openzeppelin/contracts": "3.2.0",
"@typechain/ethers-v5": "^7.0.1",
"@types/jest": "^27.0.2",
"@types/long": "^4.0.1",
"@types/node": "^16.6.1",
"@types/react": "^17.0.19",
"copy-dir": "^1.3.0",
"esm": "^3.2.25",
"ethers": "5.4.4",
"jest": "^27.3.1",
"prettier": "^2.3.2",
"rimraf": "^3.0.2",
"truffle": "^5.4.23",
"ts-jest": "^27.0.7",
"tslint": "^6.1.3",
"tslint-config-prettier": "^1.18.0",
"typescript": "^4.3.5"
},
"dependencies": {
"@certusone/wormhole-examples": "file:../../core",
"@certusone/wormhole-sdk": "^0.1.1",
"@solana/spl-token": "^0.1.8",
"@solana/web3.js": "^1.24.0"
}
}

View File

@ -0,0 +1,218 @@
import {
ChainId,
CHAIN_ID_BSC,
CHAIN_ID_ETH,
CHAIN_ID_POLYGON,
CHAIN_ID_SOLANA,
CHAIN_ID_TERRA,
} from "@certusone/wormhole-sdk";
import { Signer } from "@ethersproject/abstract-signer";
import { clusterApiUrl } from "@solana/web3.js";
import { ethers } from "ethers";
import { getAddress } from "ethers/lib/utils";
//Devnet here means the locahost kubernetes environment used by the certusone/wormhole official git repository.
//Testnet is the official Wormhole testnet
export type Environment = "devnet" | "testnet" | "mainnet";
export const CLUSTER: Environment = "devnet" as Environment; //This is the currently selected environment.
export const SOLANA_HOST = process.env.REACT_APP_SOLANA_API_URL
? process.env.REACT_APP_SOLANA_API_URL
: CLUSTER === "mainnet"
? clusterApiUrl("mainnet-beta")
: CLUSTER === "testnet"
? clusterApiUrl("testnet")
: "http://localhost:8899";
export const TERRA_HOST =
CLUSTER === "mainnet"
? {
URL: "https://lcd.terra.dev",
chainID: "columbus-5",
name: "mainnet",
}
: CLUSTER === "testnet"
? {
URL: "https://bombay-lcd.terra.dev",
chainID: "bombay-12",
name: "testnet",
}
: {
URL: "http://localhost:1317",
chainID: "columbus-5",
name: "localterra",
};
export const ETH_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B"
: CLUSTER === "testnet"
? "0x44F3e7c20850B3B5f3031114726A9240911D912a"
: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
);
export const ETH_NFT_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x6FFd7EdE62328b3Af38FCD61461Bbfc52F5651fE"
: CLUSTER === "testnet"
? "0x26b4afb60d6c903165150c6f0aa14f8016be4aec" // TODO: test address
: "0x26b4afb60d6c903165150c6f0aa14f8016be4aec"
);
export const ETH_TOKEN_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x3ee18B2214AFF97000D974cf647E7C347E8fa585"
: CLUSTER === "testnet"
? "0xa6CDAddA6e4B6704705b065E01E52e2486c0FBf6"
: "0x0290FB167208Af455bB137780163b7B7a9a10C16"
);
export const BSC_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B"
: CLUSTER === "testnet"
? "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550" // TODO: test address
: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
);
export const BSC_NFT_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE"
: CLUSTER === "testnet"
? "0x26b4afb60d6c903165150c6f0aa14f8016be4aec" // TODO: test address
: "0x26b4afb60d6c903165150c6f0aa14f8016be4aec"
);
export const BSC_TOKEN_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0xB6F6D86a8f9879A9c87f643768d9efc38c1Da6E7"
: CLUSTER === "testnet"
? "0x0290FB167208Af455bB137780163b7B7a9a10C16" // TODO: test address
: "0x0290FB167208Af455bB137780163b7B7a9a10C16"
);
export const POLYGON_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x7A4B5a56256163F07b2C80A7cA55aBE66c4ec4d7"
: CLUSTER === "testnet"
? "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550" // TODO: test address
: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
);
export const POLYGON_NFT_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x90BBd86a6Fe93D3bc3ed6335935447E75fAb7fCf"
: CLUSTER === "testnet"
? "0x26b4afb60d6c903165150c6f0aa14f8016be4aec" // TODO: test address
: "0x26b4afb60d6c903165150c6f0aa14f8016be4aec"
);
export const POLYGON_TOKEN_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE"
: CLUSTER === "testnet"
? "0x0290FB167208Af455bB137780163b7B7a9a10C16" // TODO: test address
: "0x0290FB167208Af455bB137780163b7B7a9a10C16"
);
export const SOL_BRIDGE_ADDRESS =
CLUSTER === "mainnet"
? "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth"
: CLUSTER === "testnet"
? "Brdguy7BmNB4qwEbcqqMbyV5CyJd2sxQNUn6NEpMSsUb"
: "Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o";
export const SOL_NFT_BRIDGE_ADDRESS =
CLUSTER === "mainnet"
? "WnFt12ZrnzZrFZkt2xsNsaNWoQribnuQ5B5FrDbwDhD"
: CLUSTER === "testnet"
? "NFTWqJR8YnRVqPDvTJrYuLrQDitTG5AScqbeghi4zSA" // TODO: test address
: "NFTWqJR8YnRVqPDvTJrYuLrQDitTG5AScqbeghi4zSA";
export const SOL_TOKEN_BRIDGE_ADDRESS =
CLUSTER === "mainnet"
? "wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb"
: CLUSTER === "testnet"
? "A4Us8EhCC76XdGAN17L4KpRNEK423nMivVHZzZqFqqBg"
: "B6RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE";
export const TERRA_BRIDGE_ADDRESS =
CLUSTER === "mainnet"
? "terra1dq03ugtd40zu9hcgdzrsq6z2z4hwhc9tqk2uy5"
: CLUSTER === "testnet"
? "terra18vd8fpwxzck93qlwghaj6arh4p7c5n896xzem5"
: "terra18vd8fpwxzck93qlwghaj6arh4p7c5n896xzem5";
export const TERRA_TOKEN_BRIDGE_ADDRESS =
CLUSTER === "mainnet"
? "terra10nmmwe8r3g99a9newtqa7a75xfgs2e8z87r2sf"
: CLUSTER === "testnet"
? "terra10pyejy66429refv3g35g2t7am0was7ya7kz2a4"
: "terra10pyejy66429refv3g35g2t7am0was7ya7kz2a4";
export const getBridgeAddressForChain = (chainId: ChainId) =>
chainId === CHAIN_ID_SOLANA
? SOL_BRIDGE_ADDRESS
: chainId === CHAIN_ID_ETH
? ETH_BRIDGE_ADDRESS
: chainId === CHAIN_ID_BSC
? BSC_BRIDGE_ADDRESS
: chainId === CHAIN_ID_TERRA
? TERRA_BRIDGE_ADDRESS
: chainId === CHAIN_ID_POLYGON
? POLYGON_BRIDGE_ADDRESS
: "";
export const getNFTBridgeAddressForChain = (chainId: ChainId) =>
chainId === CHAIN_ID_SOLANA
? SOL_NFT_BRIDGE_ADDRESS
: chainId === CHAIN_ID_ETH
? ETH_NFT_BRIDGE_ADDRESS
: chainId === CHAIN_ID_BSC
? BSC_NFT_BRIDGE_ADDRESS
: chainId === CHAIN_ID_POLYGON
? POLYGON_NFT_BRIDGE_ADDRESS
: "";
export const getTokenBridgeAddressForChain = (chainId: ChainId) =>
chainId === CHAIN_ID_SOLANA
? SOL_TOKEN_BRIDGE_ADDRESS
: chainId === CHAIN_ID_ETH
? ETH_TOKEN_BRIDGE_ADDRESS
: chainId === CHAIN_ID_BSC
? BSC_TOKEN_BRIDGE_ADDRESS
: chainId === CHAIN_ID_TERRA
? TERRA_TOKEN_BRIDGE_ADDRESS
: chainId === CHAIN_ID_POLYGON
? POLYGON_TOKEN_BRIDGE_ADDRESS
: "";
export const WORMHOLE_RPC_HOSTS =
CLUSTER === "mainnet"
? [
"https://wormhole-v2-mainnet-api.certus.one",
"https://wormhole.inotel.ro",
"https://wormhole-v2-mainnet-api.mcf.rocks",
"https://wormhole-v2-mainnet-api.chainlayer.network",
"https://wormhole-v2-mainnet-api.staking.fund",
"https://wormhole-v2-mainnet.01node.com",
]
: CLUSTER === "testnet"
? ["https://wormhole-v2-testnet-api.certus.one"]
: ["http://localhost:7071"];
export const ETH_NODE_URL = "ws://localhost:8545"; //TODO testnet
export const POLYGON_NODE_URL = "ws:localhost:0000"; //TODO
export const BSC_NODE_URL = "ws://localhost:8546"; //TODO testnet
export const ETH_PRIVATE_KEY =
"0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d";
export const SOLANA_PRIVATE_KEY = new Uint8Array([
14, 173, 153, 4, 176, 224, 201, 111, 32, 237, 183, 185, 159, 247, 22, 161, 89,
84, 215, 209, 212, 137, 10, 92, 157, 49, 29, 192, 101, 164, 152, 70, 87, 65,
8, 174, 214, 157, 175, 126, 98, 90, 54, 24, 100, 177, 247, 77, 19, 112, 47,
44, 165, 109, 233, 102, 14, 86, 109, 29, 134, 145, 132, 141,
]);
export function getSignerForChain(chainId: ChainId): Signer {
const provider = new ethers.providers.WebSocketProvider(
chainId === CHAIN_ID_POLYGON
? POLYGON_NODE_URL
: chainId === CHAIN_ID_BSC
? BSC_NODE_URL
: ETH_NODE_URL
);
const signer = new ethers.Wallet(ETH_PRIVATE_KEY, provider as any);
return signer;
}
export const ETH_TEST_WALLET_PUBLIC_KEY =
"0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1";
export const SOLANA_TEST_TOKEN = "2WDq7wSs9zYrpx2kbHDA4RUTRch2CCTP6ZWaH4GNfnQQ"; //SOLT on devnet
export const SOLANA_TEST_WALLET_PUBLIC_KEY =
"6sbzC1eH4FTujJXWj51eQe25cYvr4xfXbJ1vAj7j2k5J";

View File

@ -0,0 +1,226 @@
import {
ChainId,
CHAIN_ID_BSC,
CHAIN_ID_ETH,
getForeignAssetEth,
nativeToHexString,
hexToUint8Array,
approveEth,
} from "@certusone/wormhole-sdk";
import { setDefaultWasm } from "@certusone/wormhole-sdk/lib/cjs/solana/wasm";
import { getAddress } from "@ethersproject/address";
import { parseUnits } from "@ethersproject/units";
import { ethers } from "ethers";
import * as fs from "fs";
import { exit } from "process";
import { SimpleDex__factory } from "../ethers-contracts/abi/factories/SimpleDex__factory";
import {
ETH_TEST_WALLET_PUBLIC_KEY,
getSignerForChain,
getTokenBridgeAddressForChain,
} from "./consts";
import {
fullAttestation,
basicTransfer,
} from "@certusone/wormhole-examples/lib/commonWorkflows";
setDefaultWasm("node");
//This script is reliant on core examples, and the wormhole SDK.
//It is meant to be run against a fresh devnet / tilt environment.
function getPrice(chain: ChainId) {
if (chain === CHAIN_ID_ETH) {
return 4400;
}
if (chain === CHAIN_ID_BSC) {
return 630;
}
}
async function main() {
await configWormhole();
const { ethAddress, wbnbOnEth, bscAddress, wethOnBsc } = await seedPools();
console.log("Pools seeded");
await createSwapPoolFile(ethAddress, wbnbOnEth, bscAddress, wethOnBsc);
console.log("Job done");
return Promise.resolve();
}
async function configWormhole() {
const basisTransferAmount = "5000";
const WETH = "0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E";
console.log("Doing WETH Attest");
await fullAttestation(CHAIN_ID_ETH, getAddress(WETH));
console.log("Doing WBNB Attest");
await fullAttestation(CHAIN_ID_BSC, getAddress(WETH));
console.log("Bridging over WETH to bsc");
await basicTransfer(
CHAIN_ID_ETH,
basisTransferAmount,
CHAIN_ID_BSC,
ETH_TEST_WALLET_PUBLIC_KEY,
ETH_TEST_WALLET_PUBLIC_KEY,
true
);
console.log("Bridging over WBNB to eth");
await basicTransfer(
CHAIN_ID_BSC,
basisTransferAmount,
CHAIN_ID_ETH,
ETH_TEST_WALLET_PUBLIC_KEY,
ETH_TEST_WALLET_PUBLIC_KEY,
true
);
}
async function createSwapPoolFile(
ethAddress: string,
wbnbAddress: string,
bscAddress: string,
wethAddress: string
) {
const literal: any = {
[CHAIN_ID_ETH]: {
[CHAIN_ID_BSC]: { poolAddress: ethAddress, tokenAddress: wbnbAddress },
},
[CHAIN_ID_BSC]: {
[CHAIN_ID_ETH]: { poolAddress: bscAddress, tokenAddress: wethAddress },
},
};
const content = JSON.stringify(literal);
//TODO not this
await fs.writeFileSync("../react/src/swapPools.json", content, {
flag: "w+",
});
}
//TODO, in a for loop for all the EVM chains
const seedPools = async () => {
const ethSigner = getSignerForChain(CHAIN_ID_ETH);
const bscSigner = getSignerForChain(CHAIN_ID_BSC);
const currentEthPrice = getPrice(CHAIN_ID_ETH);
const currentSolPrice = getPrice(CHAIN_ID_BSC);
const ratio = currentEthPrice / currentSolPrice;
const ethBasis = 500;
const bnbBasis = Math.ceil(ethBasis * ratio);
const WETH = "0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E";
const wbnbOnEth = await getForeignAssetEth(
getTokenBridgeAddressForChain(CHAIN_ID_ETH),
ethSigner.provider as any,
CHAIN_ID_BSC,
hexToUint8Array(nativeToHexString(WETH, CHAIN_ID_BSC))
);
console.log("WBNB on ETH address", wbnbOnEth);
const wethOnBsc = await getForeignAssetEth(
getTokenBridgeAddressForChain(CHAIN_ID_BSC),
bscSigner.provider as any,
CHAIN_ID_ETH,
hexToUint8Array(nativeToHexString(WETH, CHAIN_ID_ETH))
);
console.log("WETH on BSC address", wethOnBsc);
console.log("about to deploy ETH contract");
const contractInterface = SimpleDex__factory.createInterface();
const bytecode = SimpleDex__factory.bytecode;
const ethfactory = new ethers.ContractFactory(
contractInterface,
bytecode,
ethSigner
);
const contract = await ethfactory.deploy(getAddress(wbnbOnEth));
const ethAddress = await contract.deployed().then(
(result) => {
console.log("Successfully deployed contract at " + result.address);
return result.address;
},
(error) => {
console.error(error);
exit(1);
}
);
console.log("about to deploy bsc contract");
const bscfactory = new ethers.ContractFactory(
contractInterface,
bytecode,
bscSigner
);
const bscContract = await bscfactory.deploy(getAddress(wethOnBsc));
const bscAddress = await bscContract.deployed().then(
(result) => {
console.log("Successfully deployed contract at " + result.address);
return result.address;
},
(error) => {
console.error(error);
exit(1);
}
);
console.log("Doing WBNB on ETH Approve");
await approveEth(
ethAddress,
getAddress(wbnbOnEth),
ethSigner,
"10000000000000000000000"
);
console.log("Doing WETH on BSC Approve");
await approveEth(
bscAddress,
getAddress(wethOnBsc),
bscSigner,
"10000000000000000000000"
);
const ethDex = SimpleDex__factory.connect(ethAddress, ethSigner);
const bscDex = SimpleDex__factory.connect(bscAddress, bscSigner);
console.log("Initializing eth pool");
const ethInit = await ethDex.init(parseUnits(bnbBasis.toString(), 18), {
value: parseUnits(ethBasis.toString(), 18),
gasLimit: 500000,
});
await ethInit.wait();
console.log("Initializing bsc pool");
const bscInit = await bscDex.init(parseUnits(ethBasis.toString(), 18), {
value: parseUnits(bnbBasis.toString(), 18),
gasLimit: 500000,
});
await bscInit.wait();
console.log("pools initialized");
const ethLiq = await ethDex.totalLiquidity();
console.log("Eth liquidity", ethLiq);
const bscLiq = await bscDex.totalLiquidity();
console.log("bsc liquidity", bscLiq);
//Pool should now be seeded with a small amount of ETH and Wormhole-Wrapped SOL.
return { ethAddress, wbnbOnEth, bscAddress, wethOnBsc };
};
let done = false;
main().then(
() => (done = true),
(error) => {
console.error(error);
done = true;
}
);
function wait() {
if (!done) {
setTimeout(wait, 1000);
} else {
exit(0);
}
}
wait();

View File

View File

@ -0,0 +1,116 @@
/**
* Use this file to configure your truffle project. It's seeded with some
* common settings for different networks and features like migrations,
* compilation and testing. Uncomment the ones you need or modify
* them to suit your project as necessary.
*
* More information about configuration can be found at:
*
* trufflesuite.com/docs/advanced/configuration
*
* To deploy via Infura you'll need a wallet provider (like @truffle/hdwallet-provider)
* to sign your transactions before they're sent to a remote public node. Infura accounts
* are available for free at: infura.io/register.
*
* You'll also need a mnemonic - the twelve word phrase the wallet uses to generate
* public/private key pairs. If you're publishing your code to GitHub make sure you load this
* phrase from a file you've .gitignored so it doesn't accidentally become public.
*
*/
// const HDWalletProvider = require('@truffle/hdwallet-provider');
//
// const fs = require('fs');
// const mnemonic = fs.readFileSync(".secret").toString().trim();
module.exports = {
/**
* Networks define how you connect to your ethereum client and let you set the
* defaults web3 uses to send transactions. If you don't specify one truffle
* will spin up a development blockchain for you on port 9545 when you
* run `develop` or `test`. You can ask a truffle command to use a specific
* network from the command line, e.g
*
* $ truffle test --network <network-name>
*/
networks: {
// Useful for testing. The `development` name is special - truffle uses it by default
// if it's defined here and no other network is specified at the command line.
// You should run a client (like ganache-cli, geth or parity) in a separate terminal
// tab if you use this network and you must also set the `host`, `port` and `network_id`
// options below to some value.
//
// development: {
// host: "127.0.0.1", // Localhost (default: none)
// port: 8545, // Standard Ethereum port (default: none)
// network_id: "*", // Any network (default: none)
// },
// Another network with more advanced options...
// advanced: {
// port: 8777, // Custom port
// network_id: 1342, // Custom network
// gas: 8500000, // Gas sent with each transaction (default: ~6700000)
// gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei)
// from: <address>, // Account to send txs from (default: accounts[0])
// websocket: true // Enable EventEmitter interface for web3 (default: false)
// },
// Useful for deploying to a public network.
// NB: It's important to wrap the provider as a function.
// ropsten: {
// provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`),
// network_id: 3, // Ropsten's id
// gas: 5500000, // Ropsten has a lower block limit than mainnet
// confirmations: 2, // # of confs to wait between deployments. (default: 0)
// timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50)
// skipDryRun: true // Skip dry run before migrations? (default: false for public nets )
// },
// Useful for private networks
// private: {
// provider: () => new HDWalletProvider(mnemonic, `https://network.io`),
// network_id: 2111, // This network is yours, in the cloud.
// production: true // Treats this network as if it was a public net. (default: false)
// }
},
// Set default mocha options here, use special reporters etc.
mocha: {
// timeout: 100000
},
// Configure your compilers
compilers: {
solc: {
version: "0.6.7", // Fetch exact version from solc-bin (default: truffle's version)
// docker: true, // Use "0.5.1" you've installed locally with docker (default: false)
// settings: { // See the solidity docs for advice about optimization and evmVersion
// optimizer: {
// enabled: false,
// runs: 200
// },
// evmVersion: "byzantium"
// }
},
},
// Truffle DB is currently disabled by default; to enable it, change enabled:
// false to enabled: true. The default storage location can also be
// overridden by specifying the adapter settings, as shown in the commented code below.
//
// NOTE: It is not possible to migrate your contracts to truffle DB and you should
// make a backup of your artifacts to a safe location before enabling this feature.
//
// After you backed up your artifacts you can utilize db by running migrate as follows:
// $ truffle migrate --reset --compile-all
//
// db: {
// enabled: false,
// host: "127.0.0.1",
// adapter: {
// name: "sqlite",
// settings: {
// directory: ".db"
// }
// }
// }
};

View File

@ -0,0 +1,13 @@
{
"compilerOptions": {
"outDir": "lib",
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"lib": ["es2019"],
"skipLibCheck": true,
"allowJs": true
},
"include": ["./scripts"],
"exclude": ["node_modules", "**/__tests__/*", "ethers-contracts"]
}

33
core/.gitignore vendored Normal file
View File

@ -0,0 +1,33 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# ethereum contracts
/contracts
/src/ethers-contracts
# tsproto output
/src/proto
# build
/lib

8
core/jestconfig.json Normal file
View File

@ -0,0 +1,8 @@
{
"transform": {
"^.+\\.(t|j)sx?$": "ts-jest"
},
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
"moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"],
"transformIgnorePatterns": ["/node_modules/"]
}

15093
core/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

59
core/package.json Normal file
View File

@ -0,0 +1,59 @@
{
"name": "@certusone/wormhole-examples",
"version": "0.0.1",
"description": "SDK for interacting with Wormhole",
"homepage": "https://wormholenetwork.com",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"files": [
"lib/**/*"
],
"repository": "https://github.com/certusone/wormhole/tree/dev.v2/examples",
"scripts": {
"build": "rimraf lib && tsc",
"start": "npm run build && node -r esm src/runner.js"
},
"keywords": [
"wormhole",
"bridge",
"token",
"sdk",
"solana",
"ethereum",
"terra",
"bsc"
],
"author": "certusone",
"license": "Apache-2.0",
"devDependencies": {
"@improbable-eng/grpc-web-node-http-transport": "^0.15.0",
"@openzeppelin/contracts": "^4.2.0",
"@typechain/ethers-v5": "^7.0.1",
"@types/jest": "^27.0.2",
"@types/long": "^4.0.1",
"@types/node": "^16.6.1",
"@types/react": "^17.0.19",
"copy-dir": "^1.3.0",
"esm": "^3.2.25",
"ethers": "5.4.4",
"jest": "^27.3.1",
"prettier": "^2.3.2",
"rimraf": "^3.0.2",
"ts-jest": "^27.0.7",
"tslint": "^6.1.3",
"tslint-config-prettier": "^1.18.0",
"typescript": "^4.3.5"
},
"dependencies": {
"@certusone/wormhole-sdk": "^0.1.1",
"@improbable-eng/grpc-web": "^0.14.0",
"@solana/spl-token": "^0.1.8",
"@solana/web3.js": "^1.24.0",
"@terra-money/terra.js": "^2.0.14",
"@terra-money/wallet-provider": "^2.2.0",
"bech32": "^2.0.0",
"js-base64": "^3.6.1",
"protobufjs": "^6.11.2",
"rxjs": "^7.3.0"
}
}

104
core/src/basicRelayer.ts Normal file
View File

@ -0,0 +1,104 @@
import {
ChainId,
parseNFTPayload,
parseTransferPayload,
} from "@certusone/wormhole-sdk";
import { BigNumber } from "@ethersproject/bignumber";
import { formatUnits } from "@ethersproject/units";
import { getSignedVAABySequence } from "./core/guardianQuery";
import { redeem } from "./core/redeem";
/*
The intent behind this module is to represent a backend process which waits for the guardian network to produce a signedVAA
for the bridged tokens, and then submits the VAA to the target chain. This allows the end user to pay fees only in currency from
the source chain, rather than on both chains.
*/
export async function relay(
sourceChain: ChainId,
sequence: string,
isNftTransfer: boolean
) {
//The transaction has already been submitted by the client,
//so the relayer first needs to wait for the guardian network to
//reach consensus and emit the signedVAA.
const vaaBytes = await getSignedVAABySequence(
sourceChain, //Emitter address is always the bridge contract address on the source chain
sequence,
isNftTransfer
);
//The VAA is in the generic format of the Wormhole Core bridge. The VAA payload contains the information needed to redeem the tokens.
const transferInformation = await parsePayload(
await parseVaa(vaaBytes),
isNftTransfer
);
//If the relayer is unwilling to submit VAAs at a potential monetary loss, it should first assess if this will be a profitable action.
const shouldAttempt = await processFee(transferInformation, isNftTransfer);
if (shouldAttempt) {
try {
await redeem(transferInformation.targetChain, vaaBytes, isNftTransfer);
} catch (e) {
//Because VAAs are broadcasted publicly, there is a possibility that the VAA
//will be redeemed by a different relayer. This case should be detected separately from
//other errors, as it is a do-not-retry scenario.
//This error will be deterministic, but dependent upon the implementation
//of the specific wallet provider used. As such, the detection of this error
//will need to be implemented for each provider separately.
if (isAlreadyRedeemedError(e as any)) {
} else {
throw e;
}
}
}
}
//This function converts the raw VAA into a useable javascript object.
async function parseVaa(bytes: Uint8Array) {
//parse_vaa is based on wasm
const { parse_vaa } = await import(
"@certusone/wormhole-sdk/lib/cjs/solana/core/bridge"
);
return parse_vaa(bytes);
}
//This takes the generic parsedVAA format from the Core Bridge, and parses the payload content into the
//protocol specific information of the Token & NFT bridges.
//Note: there are an unlimited variety of VAA formats, and it should not be assumed that a random VAA is one of these
//two types.
async function parsePayload(parsedVaa: any, isNftTransfer: boolean) {
const buffered = Buffer.from(new Uint8Array(parsedVaa.payload));
return isNftTransfer
? parseNFTPayload(buffered)
: parseTransferPayload(buffered);
}
//This is a toy function for the purpose of determining a VAA's profitability.
async function processFee(transferInformation: any, isNftTransfer: boolean) {
if (isNftTransfer) {
//NFTs are always relayed at a loss, because there is no fee field on the VAA.
return true;
}
const targetAssetDecimals = 8; //This will have to be pulled from either the chain or third party provider
const targetAssetUnitPrice = 100; //Accurate price quotes are important for determining profitability.
const feeValue =
parseFloat(
formatUnits(
BigNumber.from((transferInformation.fee || BigInt(0)) as bigint),
targetAssetDecimals
)
) * targetAssetUnitPrice;
const estimatedCurrencyFees = 0.01;
const estimatedCurrencyPrice = -1;
const transactionCost = estimatedCurrencyFees * estimatedCurrencyPrice;
return feeValue > transactionCost;
}
const ALREADY_REDEEMED = "Already Redeemed";
function isAlreadyRedeemedError(e: Error) {
return e?.message === ALREADY_REDEEMED;
}

View File

@ -0,0 +1,55 @@
import { ChainId } from "@certusone/wormhole-sdk";
import getSignedVAAWithRetry from "@certusone/wormhole-sdk/lib/cjs/rpc/getSignedVAAWithRetry";
import { create } from "domain";
import { ALL_CHAINS, WORMHOLE_RPC_HOSTS } from "./consts";
import { attest } from "./core/attestation";
import { createWrapped } from "./core/createWrapped";
import { getSignedVAABySequence } from "./core/guardianQuery";
import { transferTokens } from "./core/transfer";
import { redeem } from "./core/redeem";
export async function fullAttestation(
originChain: ChainId,
originAsset: string
) {
const otherChainIds = ALL_CHAINS.filter((x) => x !== originChain);
const sequence = await attest(originChain, originAsset);
console.log("attest transaction completed " + sequence);
const signedVaa = await getSignedVAABySequence(originChain, sequence, false);
console.log("have attestation VAA");
const promises = [];
otherChainIds.forEach((chain) =>
promises.push(
createWrapped(chain, signedVaa).then(() =>
console.log("attested on " + chain)
)
)
);
return await Promise.all(promises);
}
export async function basicTransfer(
sourceChain: ChainId,
amount: string,
targetChain: ChainId,
sourceAddress: string,
recipientAddress: string,
isNativeAsset: boolean,
assetAddress?: string,
decimals?: number
) {
const sequence = await transferTokens(
sourceChain,
amount,
targetChain,
sourceAddress,
recipientAddress,
isNativeAsset,
assetAddress,
decimals
);
const signedVAA = await getSignedVAABySequence(sourceChain, sequence, false);
return await redeem(targetChain, signedVAA, false);
}

244
core/src/consts.ts Normal file
View File

@ -0,0 +1,244 @@
import {
ChainId,
CHAIN_ID_BSC,
CHAIN_ID_ETH,
CHAIN_ID_ETHEREUM_ROPSTEN,
CHAIN_ID_POLYGON,
CHAIN_ID_SOLANA,
CHAIN_ID_TERRA,
} from "@certusone/wormhole-sdk";
import { Signer } from "@ethersproject/abstract-signer";
import { clusterApiUrl } from "@solana/web3.js";
import { ethers } from "ethers";
import { getAddress } from "ethers/lib/utils";
//Devnet here means the locahost kubernetes environment used by the certusone/wormhole official git repository.
//Testnet is the official Wormhole testnet
export type Environment = "devnet" | "testnet" | "mainnet";
export const CLUSTER: Environment = "devnet" as Environment; //This is the currently selected environment.
export const SOLANA_HOST = process.env.REACT_APP_SOLANA_API_URL
? process.env.REACT_APP_SOLANA_API_URL
: CLUSTER === "mainnet"
? clusterApiUrl("mainnet-beta")
: CLUSTER === "testnet"
? clusterApiUrl("testnet")
: "http://localhost:8899";
export const TERRA_HOST =
CLUSTER === "mainnet"
? {
URL: "https://lcd.terra.dev",
chainID: "columbus-5",
name: "mainnet",
}
: CLUSTER === "testnet"
? {
URL: "https://bombay-lcd.terra.dev",
chainID: "bombay-12",
name: "testnet",
}
: {
URL: "http://localhost:1317",
chainID: "columbus-5",
name: "localterra",
};
export const ETH_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B"
: CLUSTER === "testnet"
? "0x44F3e7c20850B3B5f3031114726A9240911D912a"
: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
);
export const ETH_NFT_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x6FFd7EdE62328b3Af38FCD61461Bbfc52F5651fE"
: CLUSTER === "testnet"
? "0x26b4afb60d6c903165150c6f0aa14f8016be4aec" // TODO: test address
: "0x26b4afb60d6c903165150c6f0aa14f8016be4aec"
);
export const ETH_TOKEN_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x3ee18B2214AFF97000D974cf647E7C347E8fa585"
: CLUSTER === "testnet"
? "0xa6CDAddA6e4B6704705b065E01E52e2486c0FBf6"
: "0x0290FB167208Af455bB137780163b7B7a9a10C16"
);
export const BSC_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B"
: CLUSTER === "testnet"
? "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550" // TODO: test address
: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
);
export const BSC_NFT_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE"
: CLUSTER === "testnet"
? "0x26b4afb60d6c903165150c6f0aa14f8016be4aec" // TODO: test address
: "0x26b4afb60d6c903165150c6f0aa14f8016be4aec"
);
export const BSC_TOKEN_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0xB6F6D86a8f9879A9c87f643768d9efc38c1Da6E7"
: CLUSTER === "testnet"
? "0x0290FB167208Af455bB137780163b7B7a9a10C16" // TODO: test address
: "0x0290FB167208Af455bB137780163b7B7a9a10C16"
);
export const POLYGON_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x7A4B5a56256163F07b2C80A7cA55aBE66c4ec4d7"
: CLUSTER === "testnet"
? "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550" // TODO: test address
: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
);
export const POLYGON_NFT_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x90BBd86a6Fe93D3bc3ed6335935447E75fAb7fCf"
: CLUSTER === "testnet"
? "0x26b4afb60d6c903165150c6f0aa14f8016be4aec" // TODO: test address
: "0x26b4afb60d6c903165150c6f0aa14f8016be4aec"
);
export const POLYGON_TOKEN_BRIDGE_ADDRESS = getAddress(
CLUSTER === "mainnet"
? "0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE"
: CLUSTER === "testnet"
? "0x0290FB167208Af455bB137780163b7B7a9a10C16" // TODO: test address
: "0x0290FB167208Af455bB137780163b7B7a9a10C16"
);
export const SOL_BRIDGE_ADDRESS =
CLUSTER === "mainnet"
? "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth"
: CLUSTER === "testnet"
? "Brdguy7BmNB4qwEbcqqMbyV5CyJd2sxQNUn6NEpMSsUb"
: "Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o";
export const SOL_NFT_BRIDGE_ADDRESS =
CLUSTER === "mainnet"
? "WnFt12ZrnzZrFZkt2xsNsaNWoQribnuQ5B5FrDbwDhD"
: CLUSTER === "testnet"
? "NFTWqJR8YnRVqPDvTJrYuLrQDitTG5AScqbeghi4zSA" // TODO: test address
: "NFTWqJR8YnRVqPDvTJrYuLrQDitTG5AScqbeghi4zSA";
export const SOL_TOKEN_BRIDGE_ADDRESS =
CLUSTER === "mainnet"
? "wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb"
: CLUSTER === "testnet"
? "A4Us8EhCC76XdGAN17L4KpRNEK423nMivVHZzZqFqqBg"
: "B6RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE";
export const TERRA_BRIDGE_ADDRESS =
CLUSTER === "mainnet"
? "terra1dq03ugtd40zu9hcgdzrsq6z2z4hwhc9tqk2uy5"
: CLUSTER === "testnet"
? "terra18vd8fpwxzck93qlwghaj6arh4p7c5n896xzem5"
: "terra18vd8fpwxzck93qlwghaj6arh4p7c5n896xzem5";
export const TERRA_TOKEN_BRIDGE_ADDRESS =
CLUSTER === "mainnet"
? "terra10nmmwe8r3g99a9newtqa7a75xfgs2e8z87r2sf"
: CLUSTER === "testnet"
? "terra10pyejy66429refv3g35g2t7am0was7ya7kz2a4"
: "terra10pyejy66429refv3g35g2t7am0was7ya7kz2a4";
export const getBridgeAddressForChain = (chainId: ChainId) =>
chainId === CHAIN_ID_SOLANA
? SOL_BRIDGE_ADDRESS
: chainId === CHAIN_ID_ETH
? ETH_BRIDGE_ADDRESS
: chainId === CHAIN_ID_BSC
? BSC_BRIDGE_ADDRESS
: chainId === CHAIN_ID_TERRA
? TERRA_BRIDGE_ADDRESS
: chainId === CHAIN_ID_POLYGON
? POLYGON_BRIDGE_ADDRESS
: "";
export const getNFTBridgeAddressForChain = (chainId: ChainId) =>
chainId === CHAIN_ID_SOLANA
? SOL_NFT_BRIDGE_ADDRESS
: chainId === CHAIN_ID_ETH
? ETH_NFT_BRIDGE_ADDRESS
: chainId === CHAIN_ID_BSC
? BSC_NFT_BRIDGE_ADDRESS
: chainId === CHAIN_ID_POLYGON
? POLYGON_NFT_BRIDGE_ADDRESS
: "";
export const getTokenBridgeAddressForChain = (chainId: ChainId) =>
chainId === CHAIN_ID_SOLANA
? SOL_TOKEN_BRIDGE_ADDRESS
: chainId === CHAIN_ID_ETH
? ETH_TOKEN_BRIDGE_ADDRESS
: chainId === CHAIN_ID_BSC
? BSC_TOKEN_BRIDGE_ADDRESS
: chainId === CHAIN_ID_TERRA
? TERRA_TOKEN_BRIDGE_ADDRESS
: chainId === CHAIN_ID_POLYGON
? POLYGON_TOKEN_BRIDGE_ADDRESS
: "";
export const WORMHOLE_RPC_HOSTS =
CLUSTER === "mainnet"
? [
"https://wormhole-v2-mainnet-api.certus.one",
"https://wormhole.inotel.ro",
"https://wormhole-v2-mainnet-api.mcf.rocks",
"https://wormhole-v2-mainnet-api.chainlayer.network",
"https://wormhole-v2-mainnet-api.staking.fund",
"https://wormhole-v2-mainnet.01node.com",
]
: CLUSTER === "testnet"
? ["https://wormhole-v2-testnet-api.certus.one"]
: ["http://localhost:7071"];
export const ETH_NODE_URL = "ws://localhost:8545"; //TODO testnet
export const POLYGON_NODE_URL = "ws:localhost:0000"; //TODO
export const BSC_NODE_URL = "ws://localhost:8546"; //TODO testnet
export const ETH_PRIVATE_KEY =
"0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d";
export const SOLANA_PRIVATE_KEY = new Uint8Array([
14, 173, 153, 4, 176, 224, 201, 111, 32, 237, 183, 185, 159, 247, 22, 161, 89,
84, 215, 209, 212, 137, 10, 92, 157, 49, 29, 192, 101, 164, 152, 70, 87, 65,
8, 174, 214, 157, 175, 126, 98, 90, 54, 24, 100, 177, 247, 77, 19, 112, 47,
44, 165, 109, 233, 102, 14, 86, 109, 29, 134, 145, 132, 141,
]);
export function getSignerForChain(chainId: ChainId): Signer {
const nodeUrl =
chainId === CHAIN_ID_POLYGON
? POLYGON_NODE_URL
: chainId === CHAIN_ID_BSC
? BSC_NODE_URL
: chainId === CHAIN_ID_ETH
? ETH_NODE_URL
: null;
console.log("NODE URL", nodeUrl, "chain", chainId);
const provider = new ethers.providers.WebSocketProvider(nodeUrl) as any;
const signer = new ethers.Wallet(ETH_PRIVATE_KEY, provider);
return signer;
}
export const ETH_TEST_WALLET_PUBLIC_KEY =
"0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1";
export const SOLANA_TEST_TOKEN = "2WDq7wSs9zYrpx2kbHDA4RUTRch2CCTP6ZWaH4GNfnQQ"; //SOLT on devnet
export const SOLANA_TEST_WALLET_PUBLIC_KEY =
"6sbzC1eH4FTujJXWj51eQe25cYvr4xfXbJ1vAj7j2k5J";
export const ALL_CHAINS =
CLUSTER === "devnet"
? [CHAIN_ID_BSC, CHAIN_ID_ETH, CHAIN_ID_SOLANA, CHAIN_ID_TERRA]
: CLUSTER === "testnet"
? [
CHAIN_ID_BSC,
CHAIN_ID_ETH,
CHAIN_ID_SOLANA,
CHAIN_ID_TERRA,
CHAIN_ID_ETHEREUM_ROPSTEN,
CHAIN_ID_POLYGON,
]
: [
CHAIN_ID_BSC,
CHAIN_ID_ETH,
CHAIN_ID_SOLANA,
CHAIN_ID_TERRA,
CHAIN_ID_POLYGON,
];
export const WETH_ADDRESS = "0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E";

View File

@ -0,0 +1,80 @@
import {
attestFromEth,
attestFromSolana,
ChainId,
CHAIN_ID_SOLANA,
CHAIN_ID_TERRA,
parseSequenceFromLogEth,
parseSequenceFromLogSolana,
} from "@certusone/wormhole-sdk";
import { Connection, Keypair } from "@solana/web3.js";
import {
getBridgeAddressForChain,
getSignerForChain,
getTokenBridgeAddressForChain,
SOLANA_HOST,
SOLANA_PRIVATE_KEY,
SOL_BRIDGE_ADDRESS,
SOL_TOKEN_BRIDGE_ADDRESS,
} from "../consts";
/*
This function attests the given token and returns the resultant sequence number, which is then used to retrieve the
VAA from the guardians.
*/
export async function attest(
originChain: ChainId,
originAsset: string
): Promise<string> {
if (originChain === CHAIN_ID_SOLANA) {
return attestSolana(originAsset);
} else if (originChain === CHAIN_ID_TERRA) {
return attestTerra(originAsset);
} else {
return attestEvm(originChain, originAsset);
}
}
export async function attestEvm(
originChain: ChainId,
originAsset: string
): Promise<string> {
const signer = getSignerForChain(originChain);
const receipt = await attestFromEth(
getTokenBridgeAddressForChain(originChain),
signer,
originAsset
);
const sequence = parseSequenceFromLogEth(
receipt,
getBridgeAddressForChain(originChain)
);
return sequence;
}
export async function attestTerra(originAsset: string): Promise<string> {
//TODO modify bridge_ui to use in-memory signer
throw new Error("Unimplemented");
}
export async function attestSolana(originAsset: string): Promise<string> {
const keypair = Keypair.fromSecretKey(SOLANA_PRIVATE_KEY);
const payerAddress = keypair.publicKey.toString();
const connection = new Connection(SOLANA_HOST, "confirmed");
const transaction = await attestFromSolana(
connection,
SOL_BRIDGE_ADDRESS,
SOL_TOKEN_BRIDGE_ADDRESS,
payerAddress,
originAsset
);
transaction.partialSign(keypair);
const txid = await connection.sendRawTransaction(transaction.serialize());
await connection.confirmTransaction(txid);
const info = await connection.getTransaction(txid);
if (!info) {
throw new Error("An error occurred while fetching the transaction info");
}
const sequence = parseSequenceFromLogSolana(info);
return sequence;
}

View File

@ -0,0 +1,70 @@
import {
ChainId,
CHAIN_ID_BSC,
CHAIN_ID_ETH,
CHAIN_ID_POLYGON,
CHAIN_ID_SOLANA,
CHAIN_ID_TERRA,
createWrappedOnEth,
postVaaSolana,
redeemOnEth,
redeemOnSolana,
} from "@certusone/wormhole-sdk";
import { Signer } from "@ethersproject/abstract-signer";
import { Connection, Keypair } from "@solana/web3.js";
import {
getNFTBridgeAddressForChain,
getSignerForChain,
getTokenBridgeAddressForChain,
SOLANA_HOST,
SOLANA_PRIVATE_KEY,
SOL_BRIDGE_ADDRESS,
SOL_TOKEN_BRIDGE_ADDRESS,
} from "../consts";
//This function is quite similar to redeem, but invokes createWrapped, which 'attests' an asset on a foreign
//chain by submitting its VAA. This creates the Wormhole-Wrapped asset on the target chain, hence the name.
export async function createWrapped(
targetChain: ChainId,
signedVaa: Uint8Array
) {
if (
targetChain === CHAIN_ID_ETH ||
targetChain === CHAIN_ID_POLYGON ||
targetChain === CHAIN_ID_BSC
) {
await createWrappedEvm(signedVaa, targetChain);
} else if (targetChain === CHAIN_ID_SOLANA) {
await createWrappedSolana(signedVaa);
} else if (targetChain === CHAIN_ID_TERRA) {
await createWrappedTerra(signedVaa);
} else {
return;
}
}
export async function createWrappedEvm(
signedVAA: Uint8Array,
targetChain: ChainId
) {
const signer: Signer = getSignerForChain(targetChain);
try {
return await createWrappedOnEth(
getTokenBridgeAddressForChain(targetChain),
signer,
signedVAA
);
} catch (e) {
//console.error(e);
//createWrapped throws an exception if the wrapped asset is already created.
}
}
export async function createWrappedSolana(signedVAA: Uint8Array) {
//TODO this
return Promise.resolve();
}
export async function createWrappedTerra(signedVAA: Uint8Array) {
//TODO adapt bridge_ui implementation to use in-memory terra key
}

View File

@ -0,0 +1,57 @@
import {
ChainId,
CHAIN_ID_SOLANA,
CHAIN_ID_TERRA,
getEmitterAddressEth,
getEmitterAddressSolana,
getEmitterAddressTerra,
} from "@certusone/wormhole-sdk";
import getSignedVAAWithRetry from "@certusone/wormhole-sdk/lib/cjs/rpc/getSignedVAAWithRetry";
import { NodeHttpTransport } from "@improbable-eng/grpc-web-node-http-transport";
import {
getNFTBridgeAddressForChain,
getTokenBridgeAddressForChain,
WORMHOLE_RPC_HOSTS,
} from "../consts";
export async function getSignedVAABySequence(
chainId: ChainId,
sequence: string,
isNftTransfer: boolean
): Promise<Uint8Array> {
//Note, if handed a sequence which doesn't exist or was skipped for consensus this will retry until the timeout.
const contractAddress = isNftTransfer
? getNFTBridgeAddressForChain(chainId)
: getTokenBridgeAddressForChain(chainId);
const emitterAddress = await nativeAddressToEmitterAddress(
chainId,
contractAddress
);
console.log("about to do signed vaa with retry");
const { vaaBytes } = await getSignedVAAWithRetry(
WORMHOLE_RPC_HOSTS,
chainId,
emitterAddress,
sequence,
{
transport: NodeHttpTransport(), //This should only be needed when running in node.
},
1000, //retryTimeout
1000 //Maximum retry attempts
);
return vaaBytes;
}
async function nativeAddressToEmitterAddress(
chainId: ChainId,
address: string
): Promise<string> {
if (chainId === CHAIN_ID_SOLANA) {
return await getEmitterAddressSolana(address);
} else if (chainId === CHAIN_ID_TERRA) {
return await getEmitterAddressTerra(address);
} else {
return getEmitterAddressEth(address); //Not a mistake, this one is synchronous.
}
}

96
core/src/core/redeem.ts Normal file
View File

@ -0,0 +1,96 @@
import {
ChainId,
CHAIN_ID_BSC,
CHAIN_ID_ETH,
CHAIN_ID_POLYGON,
CHAIN_ID_SOLANA,
CHAIN_ID_TERRA,
postVaaSolana,
redeemOnEth,
redeemOnSolana,
} from "@certusone/wormhole-sdk";
import { Signer } from "@ethersproject/abstract-signer";
import { Connection, Keypair } from "@solana/web3.js";
import {
getNFTBridgeAddressForChain,
getSignerForChain,
getTokenBridgeAddressForChain,
SOLANA_HOST,
SOLANA_PRIVATE_KEY,
SOL_BRIDGE_ADDRESS,
SOL_TOKEN_BRIDGE_ADDRESS,
} from "../consts";
//This function attempts to redeem the VAA on the target chain.
export async function redeem(
targetChain: ChainId,
signedVaa: Uint8Array,
isNftTransfer: boolean
) {
if (
targetChain === CHAIN_ID_ETH ||
targetChain === CHAIN_ID_POLYGON ||
targetChain === CHAIN_ID_BSC
) {
await redeemEvm(signedVaa, targetChain, isNftTransfer);
} else if (targetChain === CHAIN_ID_SOLANA) {
await redeemSolana(signedVaa, isNftTransfer);
} else if (targetChain === CHAIN_ID_TERRA) {
await redeemTerra(signedVaa);
} else {
return;
}
}
export async function redeemEvm(
signedVAA: Uint8Array,
targetChain: ChainId,
isNftTransfer: boolean
) {
const signer: Signer = getSignerForChain(targetChain);
try {
await redeemOnEth(
isNftTransfer
? getNFTBridgeAddressForChain(targetChain)
: getTokenBridgeAddressForChain(targetChain),
signer,
signedVAA
);
} catch (e) {}
}
export async function redeemSolana(
signedVAA: Uint8Array,
isNftTransfer: boolean
) {
if (isNftTransfer) {
//TODO
//Solana redemptions require sending metadata to the chain inside of transactions,
//and this in not yet available in the sdk.
return;
}
const connection = new Connection(SOLANA_HOST, "confirmed");
const keypair = Keypair.fromSecretKey(SOLANA_PRIVATE_KEY);
const payerAddress = keypair.publicKey.toString();
await postVaaSolana(
connection,
async (transaction) => {
transaction.partialSign(keypair);
return transaction;
},
SOL_BRIDGE_ADDRESS,
payerAddress,
Buffer.from(signedVAA)
);
await redeemOnSolana(
connection,
SOL_BRIDGE_ADDRESS,
SOL_TOKEN_BRIDGE_ADDRESS,
payerAddress,
signedVAA
);
}
export async function redeemTerra(signedVAA: Uint8Array) {
//TODO adapt bridge_ui implementation to use in-memory terra key
}

186
core/src/core/transfer.ts Normal file
View File

@ -0,0 +1,186 @@
import {
ChainId,
CHAIN_ID_SOLANA,
CHAIN_ID_TERRA,
hexToUint8Array,
nativeToHexString,
parseSequenceFromLogEth,
parseSequenceFromLogSolana,
transferFromEth,
transferFromEthNative,
transferFromSolana,
transferNativeSol,
} from "@certusone/wormhole-sdk";
import { parseUnits } from "@ethersproject/units";
import { Connection, Keypair } from "@solana/web3.js";
import {
getBridgeAddressForChain,
getSignerForChain,
getTokenBridgeAddressForChain,
SOLANA_HOST,
SOLANA_PRIVATE_KEY,
SOL_BRIDGE_ADDRESS,
SOL_TOKEN_BRIDGE_ADDRESS,
} from "../consts";
/*
This function transfers the given token and returns the resultant sequence number, which is then used to retrieve the
VAA from the guardians.
*/
export async function transferTokens(
sourceChain: ChainId,
amount: string,
targetChain: ChainId,
sourceAddress: string,
recipientAddress: string,
isNativeAsset: boolean,
assetAddress?: string,
decimals?: number
): Promise<string> {
//TODO support native assets,
//TODO set relayer fee,
if (sourceChain === CHAIN_ID_SOLANA) {
return transferSolana(
amount,
targetChain,
sourceAddress,
recipientAddress,
isNativeAsset,
assetAddress,
decimals
);
} else if (sourceChain === CHAIN_ID_TERRA) {
return transferTerra(
amount,
targetChain,
recipientAddress,
isNativeAsset,
assetAddress,
decimals
);
} else {
return transferEvm(
sourceChain,
amount,
targetChain,
recipientAddress,
isNativeAsset,
assetAddress,
decimals
);
}
}
export async function transferSolana(
amount: string,
targetChain: ChainId,
sourceAddress: string,
recipientAddress: string,
isNativeAsset: boolean,
assetAddress?: string,
decimals?: number
): Promise<string> {
if (isNativeAsset) {
decimals = 9;
} else if (!assetAddress || !decimals) {
throw new Error("No token specified for transfer.");
}
const keypair = Keypair.fromSecretKey(SOLANA_PRIVATE_KEY);
const payerAddress = keypair.publicKey.toString();
const connection = new Connection(SOLANA_HOST, "confirmed");
const amountParsed = parseUnits(amount, decimals).toBigInt();
const hexString = nativeToHexString(recipientAddress, targetChain);
if (!hexString) {
throw new Error("Invalid recipient");
}
const vaaCompatibleAddress = hexToUint8Array(hexString);
const promise = isNativeAsset
? transferNativeSol(
connection,
SOL_BRIDGE_ADDRESS,
SOL_TOKEN_BRIDGE_ADDRESS,
payerAddress,
amountParsed,
vaaCompatibleAddress,
targetChain
)
: transferFromSolana(
connection,
SOL_BRIDGE_ADDRESS,
SOL_TOKEN_BRIDGE_ADDRESS,
payerAddress, //Actual SOL fee paying address
sourceAddress, //SPL token account
assetAddress as string,
amountParsed,
vaaCompatibleAddress,
targetChain
//TODO support non-wormhole assets here.
// originAddress,
// originChain
);
const transaction = await promise;
transaction.partialSign(keypair);
const txid = await connection.sendRawTransaction(transaction.serialize());
await connection.confirmTransaction(txid);
const info = await connection.getTransaction(txid);
if (!info) {
throw new Error("An error occurred while fetching the transaction info");
}
const sequence = parseSequenceFromLogSolana(info);
return sequence;
}
export async function transferTerra(
amount: string,
targetChain: ChainId,
recipientAddress: string,
isNativeAsset: boolean,
assetAddress?: string,
decimals?: number
): Promise<string> {
//TODO modify bridge_ui to use in-memory signer
throw new Error("Unimplemented");
}
export async function transferEvm(
sourceChain: ChainId,
amount: string,
targetChain: ChainId,
recipientAddress: string,
isNativeAsset: boolean,
assetAddress?: string,
decimals?: number
): Promise<string> {
if (isNativeAsset) {
decimals = 18;
} else if (!assetAddress || !decimals) {
throw new Error("No token specified for transfer.");
}
const amountParsed = parseUnits(amount, decimals);
const signer = getSignerForChain(sourceChain);
const hexString = nativeToHexString(recipientAddress, targetChain);
if (!hexString) {
throw new Error("Invalid recipient");
}
const vaaCompatibleAddress = hexToUint8Array(hexString);
const receipt = isNativeAsset
? await transferFromEthNative(
getTokenBridgeAddressForChain(sourceChain),
signer,
amountParsed,
targetChain,
vaaCompatibleAddress
)
: await transferFromEth(
getTokenBridgeAddressForChain(sourceChain),
signer,
assetAddress as string,
amountParsed,
targetChain,
vaaCompatibleAddress
);
return await parseSequenceFromLogEth(
receipt,
getBridgeAddressForChain(sourceChain)
);
}

94
core/src/examples.ts Normal file
View File

@ -0,0 +1,94 @@
import {
CHAIN_ID_BSC,
CHAIN_ID_ETH,
CHAIN_ID_SOLANA,
redeemOnEth,
WSOL_ADDRESS,
} from "@certusone/wormhole-sdk";
import { setDefaultWasm } from "@certusone/wormhole-sdk/lib/cjs/solana/wasm";
import {
ASSOCIATED_TOKEN_PROGRAM_ID,
Token,
TOKEN_PROGRAM_ID,
} from "@solana/spl-token";
import { PublicKey } from "@solana/web3.js";
import { relay } from "./basicRelayer";
import { fullAttestation } from "./commonWorkflows";
import {
ETH_TEST_WALLET_PUBLIC_KEY,
getSignerForChain,
getTokenBridgeAddressForChain,
SOLANA_TEST_TOKEN,
SOLANA_TEST_WALLET_PUBLIC_KEY,
WETH_ADDRESS,
} from "./consts";
/*
The goal of this example program is to demonstrate a common Wormhole token bridge
use-case.
*/
import { attest } from "./core/attestation";
import { createWrapped } from "./core/createWrapped";
import { getSignedVAABySequence } from "./core/guardianQuery";
import { redeem } from "./core/redeem";
import { transferTokens } from "./core/transfer";
setDefaultWasm("node");
/*
This example attests a test token on Solana, retrieves the resulting VAA, and then submits it
to Ethereum, thereby registering the token on Ethereum.
*/
export async function attestWETH() {
return fullAttestation(CHAIN_ID_ETH, WETH_ADDRESS);
}
export async function attestWBNB() {
return fullAttestation(CHAIN_ID_BSC, WETH_ADDRESS);
}
export async function bridgeWsolToEthereum() {
const sequenceNumber = await transferTokens(
CHAIN_ID_SOLANA,
"20.0",
CHAIN_ID_ETH,
SOLANA_TEST_WALLET_PUBLIC_KEY, //When transferring native SOL, use the native / payer address.
ETH_TEST_WALLET_PUBLIC_KEY,
true
);
const signedVaa = await getSignedVAABySequence(
CHAIN_ID_SOLANA,
sequenceNumber,
false
);
await redeemOnEth(
getTokenBridgeAddressForChain(CHAIN_ID_ETH),
getSignerForChain(CHAIN_ID_ETH),
signedVaa
);
}
export async function transferWithRelayHandoff() {
const sourceAddress = (
await Token.getAssociatedTokenAddress(
ASSOCIATED_TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
new PublicKey(SOLANA_TEST_TOKEN),
new PublicKey(SOLANA_TEST_WALLET_PUBLIC_KEY)
)
).toString();
const sequenceNumber = await transferTokens(
CHAIN_ID_SOLANA,
"1.0",
CHAIN_ID_ETH,
sourceAddress,
ETH_TEST_WALLET_PUBLIC_KEY,
false,
SOLANA_TEST_TOKEN,
9
);
await relay(CHAIN_ID_SOLANA, sequenceNumber, false);
}

44
core/src/runner.js Normal file
View File

@ -0,0 +1,44 @@
import { setDefaultWasm } from "@certusone/wormhole-sdk/lib/cjs/solana/wasm";
import { exit } from "process";
import * as examples from "../lib/examples";
function logWrapper(promise) {
return promise.catch((e) => {
console.log(e);
return Promise.resolve();
});
}
export async function runAll() {
console.log("Attesting WBNB");
await logWrapper(examples.attestWBNB());
console.log("Attesting WETH");
await logWrapper(examples.attestWETH());
console.log("Attestation complete.");
console.log();
// console.log("Transfer example");
// await logWrapper(examples.transferWithRelayHandoff());
// console.log("Transfer example complete.");
// console.log();
console.log("Complete");
return Promise.resolve();
}
setDefaultWasm("node");
let done = false;
runAll().then(
() => (done = true),
() => (done = true)
);
function wait() {
if (!done) {
setTimeout(wait, 1000);
} else {
exit(0);
}
}
wait();

13
core/tsconfig.json Normal file
View File

@ -0,0 +1,13 @@
{
"compilerOptions": {
"outDir": "lib",
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"lib": ["es2019"],
"skipLibCheck": true,
"allowJs": true
},
"include": ["src"],
"exclude": ["node_modules", "**/__tests__/*"]
}

View File

@ -0,0 +1,14 @@
[package]
name = "wormhole-messenger-common"
version = "0.1.0"
edition = "2018"
[lib]
name = "messenger_common"
crate-type = ["cdylib", "lib"]
[dependencies]
borsh = "0.8.1"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@ -0,0 +1,13 @@
use borsh::{
BorshDeserialize,
BorshSerialize,
};
#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, PartialEq)]
pub struct Message {
/// Messenger/DM username.
pub nick: String,
/// Message text to be output on the target networks node logs.
pub text: String,
}

21
messenger/rustfmt.toml Normal file
View File

@ -0,0 +1,21 @@
# Uses 2018 for maximum compatibiilty with Solana.
edition = "2018"
# Merge similar crates together to avoid multiple use statements.
imports_granularity = "Module"
# Consistency in formatting makes tool based searching/editing better.
empty_item_single_line = false
# Easier editing when arbitrary mixed use statements do not collapse.
imports_layout = "Vertical"
# Default rustfmt formatting of match arms with branches is awful.
match_arm_leading_pipes = "Preserve"
# Align Fields
enum_discrim_align_threshold = 80
struct_field_align_threshold = 80
# Allow up to two blank lines for grouping.
blank_lines_upper_bound = 2

3602
messenger/solana/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,30 @@
[package]
name = "wormhole-messenger-solana"
version = "0.1.0"
edition = "2018"
[lib]
name = "messenger"
crate-type = ["cdylib", "lib"]
[features]
no-entrypoint = []
test-bpf = []
[dependencies]
borsh = { version="=0.8.1" }
solana-program = { version="=1.7.0" }
nom = { version="7", default-features=false, features=["alloc"] }
wormhole-sdk = { path = "../../../sdk/rust/sdk", features = ["devnet", "solana"] }
wormhole-messenger-common = { path = "../common" }
[dev-dependencies]
borsh = "=0.8.1"
solana-program-test = "=1.7.0"
solana-sdk = "=1.7.0"
rand = "0.7.3"
libsecp256k1 = "0.3.5"
hex = "*"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

View File

@ -0,0 +1,108 @@
use borsh::BorshSerialize;
use solana_program::instruction::{
AccountMeta,
Instruction,
};
use solana_program::pubkey::Pubkey;
use solana_program::system_program;
use solana_program::sysvar::rent;
use solana_program::sysvar::clock;
use wormhole_sdk::{
id,
config,
fee_collector,
sequence,
};
use messenger_common::Message;
use crate::Instruction::{
RecvMessage,
SendMessage,
};
/// Create a RecvMessage instruction.
pub fn recv_message(program_id: Pubkey, payer: Pubkey, vaa: Pubkey) -> Instruction {
Instruction {
program_id,
data: RecvMessage.try_to_vec().unwrap(),
accounts: vec![
AccountMeta::new_readonly(payer, true),
AccountMeta::new_readonly(vaa, false),
],
}
}
/// Create a SendMessage instruction.
pub fn send_message(
program_id: Pubkey,
payer: Pubkey,
emitter: Pubkey,
message: Pubkey,
payload: Message,
nonce: u32,
) -> Instruction {
let wormhole = id();
let config = config(&wormhole);
let fee_collector = fee_collector(&wormhole);
let sequence = sequence(&wormhole, &emitter);
// Note that accounts are passed in in order of useful-ness. The payer and message accounts are
// used to invoke Wormhole. Many of the example send_message* instruction handlers will only
// pop off as many accounts as required.
Instruction {
program_id,
accounts: vec![
AccountMeta::new(payer, true),
AccountMeta::new(message, true),
AccountMeta::new(fee_collector, false),
AccountMeta::new(config, false),
AccountMeta::new_readonly(emitter, false),
AccountMeta::new(sequence, false),
AccountMeta::new_readonly(clock::id(), false),
AccountMeta::new_readonly(rent::id(), false),
AccountMeta::new_readonly(system_program::id(), false),
AccountMeta::new_readonly(wormhole, false),
],
data: SendMessage(payload, nonce).try_to_vec().unwrap(),
}
}
/// Create a SendMessageRaw instruction. This does the same as SendMessage however the instruction
/// handler does not use the Wormhole SDK helper API.
pub fn send_message_raw(
program_id: Pubkey,
payer: Pubkey,
emitter: Pubkey,
message: Pubkey,
payload: Message,
nonce: u32,
) -> Instruction {
let wormhole = id();
let config = config(&wormhole);
let fee_collector = fee_collector(&wormhole);
let sequence = sequence(&wormhole, &emitter);
// Note that accounts are passed in in order of useful-ness. The payer and message accounts are
// used to invoke Wormhole. Many of the example send_message* instruction handlers will only
// pop off as many accounts as required.
Instruction {
program_id,
accounts: vec![
AccountMeta::new(payer, true),
AccountMeta::new(message, true),
AccountMeta::new(fee_collector, false),
AccountMeta::new(config, false),
AccountMeta::new_readonly(emitter, false),
AccountMeta::new(sequence, false),
AccountMeta::new_readonly(clock::id(), false),
AccountMeta::new_readonly(rent::id(), false),
AccountMeta::new_readonly(system_program::id(), false),
AccountMeta::new_readonly(wormhole, false),
],
data: SendMessage(payload, nonce).try_to_vec().unwrap(),
}
}

Some files were not shown because too many files have changed in this diff Show More