Add terra ust for quoting

This commit is contained in:
Karl Kempe 2022-01-26 17:06:26 +00:00
parent 29704b701d
commit 0c50e93dc0
11 changed files with 750 additions and 372 deletions

View File

@ -1,41 +1,92 @@
import { ethers } from "ethers";
import { UniEvmToken } from "./uniswap-core";
import { QuickswapRouter } from "./quickswap";
import { SingleAmmSwapRouter as UniswapV3Router } from "./uniswap-v3";
import { QuickswapRouter as MaticRouter } from "./quickswap";
import { UniswapV3Router as EthRouter } from "./uniswap-v3";
import { TerraUstTransfer as UstRouter } from "./terra-ust-transfer";
import {
ETH_NETWORK_CHAIN_ID,
POLYGON_NETWORK_CHAIN_ID,
WETH_TOKEN_INFO,
WMATIC_TOKEN_INFO,
UST_TOKEN_INFO,
WORMHOLE_CHAIN_ID_ETHEREUM,
WORMHOLE_CHAIN_ID_POLYGON,
WORMHOLE_CHAIN_ID_TERRA,
} from "../utils/consts";
import { addFixedAmounts, subtractFixedAmounts } from "../utils/math";
import { UstLocation } from "./generic";
import {
ExactInParameters,
ExactOutParameters,
makeExactInParameters,
makeExactOutParameters,
} from "./uniswap-core";
import { ChainId } from "@certusone/wormhole-sdk";
export { PROTOCOL as PROTOCOL_UNISWAP_V2 } from "./uniswap-v2";
export { PROTOCOL as PROTOCOL_UNISWAP_V3 } from "./uniswap-v3";
export { PROTOCOL as PROTOCOL_TERRA_UST_TRANSFER } from "./terra-ust-transfer";
export const TERRA_UST = UST_TOKEN_INFO.address;
export enum QuoteType {
ExactIn = 1,
ExactOut,
}
function makeRouter(provider: ethers.providers.Provider, id: number) {
switch (id) {
case ETH_NETWORK_CHAIN_ID: {
return new UniswapV3Router(provider);
export function makeEvmProviderFromAddress(tokenAddress: string) {
switch (tokenAddress) {
case WETH_TOKEN_INFO.address: {
const url = process.env.REACT_APP_GOERLI_PROVIDER;
if (!url) {
throw new Error("Could not find REACT_APP_GOERLI_PROVIDER");
}
return new ethers.providers.StaticJsonRpcProvider(url);
}
case POLYGON_NETWORK_CHAIN_ID: {
return new QuickswapRouter(provider);
case WMATIC_TOKEN_INFO.address: {
const url = process.env.REACT_APP_MUMBAI_PROVIDER;
if (!url) {
throw new Error("Could not find REACT_APP_MUMBAI_PROVIDER");
}
return new ethers.providers.StaticJsonRpcProvider(url);
}
default: {
throw Error("unrecognized chain id");
throw Error("unrecognized evm token address");
}
}
}
export function getUstAddress(id: number): string {
switch (id) {
case ETH_NETWORK_CHAIN_ID: {
return "0x36Ed51Afc79619b299b238898E72ce482600568a";
export function getChainIdFromAddress(tokenAddress: string) {
switch (tokenAddress) {
case WETH_TOKEN_INFO.address: {
return WORMHOLE_CHAIN_ID_ETHEREUM;
}
case POLYGON_NETWORK_CHAIN_ID: {
return "0xe3a1c77e952b57b5883f6c906fc706fcc7d4392c";
case WMATIC_TOKEN_INFO.address: {
return WORMHOLE_CHAIN_ID_POLYGON;
}
case UST_TOKEN_INFO.address: {
return WORMHOLE_CHAIN_ID_TERRA;
}
default: {
throw Error("unrecognized evm token address");
}
}
}
async function makeRouter(tokenAddress: string, loc: UstLocation) {
switch (tokenAddress) {
case WETH_TOKEN_INFO.address: {
const provider = makeEvmProviderFromAddress(tokenAddress);
const router = new EthRouter(provider);
await router.initialize(loc);
return router;
}
case WMATIC_TOKEN_INFO.address: {
const provider = makeEvmProviderFromAddress(tokenAddress);
const router = new MaticRouter(provider);
await router.initialize(loc);
return router;
}
case UST_TOKEN_INFO.address: {
return new UstRouter();
}
default: {
throw Error("unrecognized chain id");
@ -51,123 +102,105 @@ function splitSlippageInHalf(totalSlippage: string): string {
.toString();
}
interface RelayerFee {
amount: ethers.BigNumber;
export interface RelayerFee {
amount: string;
tokenAddress: string;
}
export interface ExactInParameters {
protocol: string;
amountIn: ethers.BigNumber;
minAmountOut: ethers.BigNumber;
deadline: ethers.BigNumber;
poolFee: string;
path: [string, string];
}
export interface ExactInCrossParameters {
amountIn: string;
minAmountOut: string;
src: ExactInParameters;
dst: ExactInParameters;
relayerFee: RelayerFee;
}
export interface ExactOutParameters {
protocol: string;
amountOut: ethers.BigNumber;
maxAmountIn: ethers.BigNumber;
deadline: ethers.BigNumber;
poolFee: string;
path: [string, string];
}
export interface ExactOutCrossParameters {
amountOut: string;
maxAmountIn: string;
src: ExactOutParameters;
dst: ExactOutParameters;
relayerFee: RelayerFee;
}
export class UniswapToUniswapQuoter {
// providers
srcProvider: ethers.providers.Provider;
dstProvider: ethers.providers.Provider;
// networks
srcNetwork: ethers.providers.Network;
dstNetwork: ethers.providers.Network;
// tokens
tokenInAddress: string;
tokenOutAddress: string;
// routers
srcRouter: UniswapV3Router | QuickswapRouter;
dstRouter: UniswapV3Router | QuickswapRouter;
srcRouter: UstRouter | EthRouter | MaticRouter;
dstRouter: UstRouter | EthRouter | MaticRouter;
// tokens
srcTokenIn: UniEvmToken;
srcTokenOut: UniEvmToken;
dstTokenIn: UniEvmToken;
dstTokenOut: UniEvmToken;
constructor() {}
constructor(
srcProvider: ethers.providers.Provider,
dstProvider: ethers.providers.Provider
) {
this.srcProvider = srcProvider;
this.dstProvider = dstProvider;
}
async initialize(
tokenInAddress: string,
tokenOutAddress: string
): Promise<void> {
if (tokenInAddress !== this.tokenInAddress) {
this.tokenInAddress = tokenInAddress;
this.srcRouter = await makeRouter(tokenInAddress, UstLocation.Out);
}
async initialize(): Promise<void> {
[this.srcNetwork, this.dstNetwork] = await Promise.all([
this.srcProvider.getNetwork(),
this.dstProvider.getNetwork(),
]);
this.srcRouter = makeRouter(this.srcProvider, this.srcNetwork.chainId);
this.dstRouter = makeRouter(this.dstProvider, this.dstNetwork.chainId);
if (tokenOutAddress != this.tokenOutAddress) {
this.tokenOutAddress = tokenOutAddress;
this.dstRouter = await makeRouter(tokenOutAddress, UstLocation.In);
}
return;
}
sameChain(): boolean {
return this.srcNetwork.chainId === this.dstNetwork.chainId;
}
async makeSrcTokens(
tokenInAddress: string
): Promise<[UniEvmToken, UniEvmToken]> {
const ustOutAddress = getUstAddress(this.srcNetwork.chainId);
const router = this.srcRouter;
[this.srcTokenIn, this.srcTokenOut] = await Promise.all([
router.makeToken(tokenInAddress),
router.makeToken(ustOutAddress),
]);
return [this.srcTokenIn, this.srcTokenOut];
}
async makeDstTokens(
tokenOutAddress: string
): Promise<[UniEvmToken, UniEvmToken]> {
const ustInAddress = getUstAddress(this.dstNetwork.chainId);
const router = this.dstRouter;
[this.dstTokenIn, this.dstTokenOut] = await Promise.all([
router.makeToken(ustInAddress),
router.makeToken(tokenOutAddress),
]);
return [this.dstTokenIn, this.dstTokenOut];
}
async computeAndVerifySrcPoolAddress(): Promise<string> {
return this.srcRouter.computeAndVerifyPoolAddress(
this.srcTokenIn,
this.srcTokenOut
);
return this.srcRouter.computeAndVerifyPoolAddress();
}
async computeAndVerifyDstPoolAddress(): Promise<string> {
return this.dstRouter.computeAndVerifyPoolAddress(
this.dstTokenIn,
this.dstTokenOut
);
return this.dstRouter.computeAndVerifyPoolAddress();
}
computeSwapSlippage(slippage): string {
if (this.isSrcUst() || this.isDstUst()) {
return slippage;
}
return splitSlippageInHalf(slippage);
}
getRelayerFee(amount: string): RelayerFee {
if (this.isSrcUst()) {
return {
amount: this.srcRouter.computeUnitAmountOut(amount),
tokenAddress: TERRA_UST, // TODO: make sure this is the right address for bridge transfer?
};
}
const relayerFee: RelayerFee = {
amount: this.srcRouter.computeUnitAmountOut(amount),
tokenAddress: this.srcRouter.getTokenOutAddress(),
};
return relayerFee;
}
makeSrcExactInParameters(
amountIn: string,
minAmountOut: string
): ExactInParameters {
if (this.isSrcUst()) {
return undefined;
}
// @ts-ignore
return makeExactInParameters(this.srcRouter, amountIn, minAmountOut);
}
makeDstExactInParameters(
amountIn: string,
minAmountOut: string
): ExactInParameters {
if (this.isDstUst()) {
return undefined;
}
// @ts-ignore
return makeExactInParameters(this.dstRouter, amountIn, minAmountOut);
}
async computeExactInParameters(
@ -175,71 +208,68 @@ export class UniswapToUniswapQuoter {
slippage: string,
relayerFeeUst: string
): Promise<ExactInCrossParameters> {
const singleSlippage = splitSlippageInHalf(slippage);
const singleSlippage = this.computeSwapSlippage(slippage);
// src quote
const srcRouter = this.srcRouter;
const srcTokenIn = this.srcTokenIn;
const srcTokenOut = this.srcTokenOut;
const srcMinAmountOut = await srcRouter.fetchQuoteAmountOut(
srcTokenIn,
srcTokenOut,
const srcMinAmountOut = await srcRouter.fetchExactInQuote(
amountIn,
singleSlippage
);
// dst quote
const dstRouter = this.dstRouter;
const dstAmountIn = this.srcTokenOut.formatAmount(srcMinAmountOut);
const dstAmountIn = srcMinAmountOut; //srcRouter.formatAmountOut(srcMinAmountOut);
if (Number(dstAmountIn) < Number(relayerFeeUst)) {
throw Error(
`srcAmountOut <= relayerFeeUst. ${dstAmountIn} vs ${relayerFeeUst}`
);
}
const dstTokenIn = this.dstTokenIn;
const dstTokenOut = this.dstTokenOut;
const dstAmountInAfterFee = dstTokenIn.subtractAmounts(
const dstAmountInAfterFee = subtractFixedAmounts(
dstAmountIn,
relayerFeeUst
relayerFeeUst,
dstRouter.getTokenInDecimals()
);
const dstMinAmountOut = await dstRouter.fetchQuoteAmountOut(
dstTokenIn,
dstTokenOut,
const dstMinAmountOut = await dstRouter.fetchExactInQuote(
dstAmountInAfterFee,
singleSlippage
);
const srcParameters: ExactInParameters = {
protocol: srcRouter.getProtocol(),
amountIn: srcTokenIn.computeUnitAmount(amountIn),
minAmountOut: srcMinAmountOut,
poolFee: srcRouter.getPoolFee(),
deadline: srcRouter.getTradeDeadline(),
path: [srcTokenIn.getAddress(), srcTokenOut.getAddress()],
};
const dstParameters: ExactInParameters = {
protocol: dstRouter.getProtocol(),
amountIn: dstTokenIn.computeUnitAmount(dstAmountInAfterFee),
minAmountOut: dstMinAmountOut,
poolFee: dstRouter.getPoolFee(),
deadline: dstRouter.getTradeDeadline(),
path: [dstTokenIn.getAddress(), dstTokenOut.getAddress()],
};
// organize parameters
const params: ExactInCrossParameters = {
src: srcParameters,
dst: dstParameters,
relayerFee: {
amount: dstTokenIn.computeUnitAmount(relayerFeeUst),
tokenAddress: this.dstTokenIn.getAddress(),
},
amountIn: amountIn,
minAmountOut: dstMinAmountOut,
src: this.makeSrcExactInParameters(amountIn, srcMinAmountOut),
dst: this.makeDstExactInParameters(dstAmountInAfterFee, dstMinAmountOut),
relayerFee: this.getRelayerFee(relayerFeeUst),
};
return params;
}
makeSrcExactOutParameters(
amountOut: string,
maxAmountIn: string
): ExactOutParameters {
if (this.isSrcUst()) {
return null;
}
// @ts-ignore
return makeExactOutParameters(this.srcRouter, amountOut, maxAmountIn);
}
makeDstExactOutParameters(
amountOut: string,
maxAmountIn: string
): ExactOutParameters {
if (this.isDstUst()) {
return null;
}
// @ts-ignore
return makeExactOutParameters(this.dstRouter, amountOut, maxAmountIn);
}
async computeExactOutParameters(
amountOut: string,
slippage: string,
@ -249,69 +279,85 @@ export class UniswapToUniswapQuoter {
// dst quote first
const dstRouter = this.dstRouter;
const dstTokenIn = this.dstTokenIn;
const dstTokenOut = this.dstTokenOut;
const dstMaxAmountIn = await dstRouter.fetchQuoteAmountIn(
dstTokenIn,
dstTokenOut,
const dstMaxAmountIn = await dstRouter.fetchExactOutQuote(
amountOut,
singleSlippage
);
// src quote
const srcRouter = this.srcRouter;
const srcAmountOut = this.dstTokenIn.formatAmount(dstMaxAmountIn);
const srcAmountOut = dstMaxAmountIn;
if (Number(srcAmountOut) < Number(relayerFeeUst)) {
throw Error(
`dstAmountIn <= relayerFeeUst. ${srcAmountOut} vs ${relayerFeeUst}`
);
}
const srcTokenIn = this.srcTokenIn;
const srcTokenOut = this.srcTokenOut;
const srcAmountOutBeforeFee = srcTokenOut.addAmounts(
const srcAmountOutBeforeFee = addFixedAmounts(
srcAmountOut,
relayerFeeUst
relayerFeeUst,
srcRouter.getTokenOutDecimals()
);
const srcMaxAmountIn = await srcRouter.fetchQuoteAmountIn(
srcTokenIn,
srcTokenOut,
const srcMaxAmountIn = await srcRouter.fetchExactOutQuote(
srcAmountOutBeforeFee,
singleSlippage
);
const srcParameters: ExactOutParameters = {
protocol: srcRouter.getProtocol(),
amountOut: srcTokenOut.computeUnitAmount(srcAmountOutBeforeFee),
maxAmountIn: srcMaxAmountIn,
poolFee: srcRouter.getPoolFee(),
deadline: srcRouter.getTradeDeadline(),
path: [srcTokenIn.getAddress(), srcTokenOut.getAddress()],
};
const dstParameters: ExactOutParameters = {
protocol: dstRouter.getProtocol(),
amountOut: dstTokenOut.computeUnitAmount(amountOut),
maxAmountIn: dstMaxAmountIn,
poolFee: dstRouter.getPoolFee(),
deadline: dstRouter.getTradeDeadline(),
path: [dstTokenIn.getAddress(), dstTokenOut.getAddress()],
};
// organize parameters
const params: ExactOutCrossParameters = {
src: srcParameters,
dst: dstParameters,
relayerFee: {
amount: dstTokenIn.computeUnitAmount(relayerFeeUst),
tokenAddress: this.dstTokenIn.getAddress(),
},
amountOut: amountOut,
maxAmountIn: srcMaxAmountIn,
src: this.makeSrcExactOutParameters(
srcAmountOutBeforeFee,
srcMaxAmountIn
),
dst: this.makeDstExactOutParameters(amountOut, dstMaxAmountIn),
relayerFee: this.getRelayerFee(relayerFeeUst),
};
return params;
}
setDeadlines(deadline: string): void {
this.srcRouter.setDeadline(deadline);
this.dstRouter.setDeadline(deadline);
if (!this.isSrcUst()) {
// @ts-ignore
this.srcRouter.setDeadline(deadline);
}
if (!this.isDstUst()) {
// @ts-ignore
this.dstRouter.setDeadline(deadline);
}
}
isSrcUst(): boolean {
return this.tokenInAddress === TERRA_UST;
}
isDstUst(): boolean {
return this.tokenOutAddress === TERRA_UST;
}
getSrcEvmProvider(): ethers.providers.Provider {
if (this.isSrcUst()) {
return undefined;
}
// @ts-ignore
return this.srcRouter.getProvider();
}
getDstEvmProvider(): ethers.providers.Provider {
if (this.isDstUst()) {
return undefined;
}
// @ts-ignore
return this.dstRouter.getProvider();
}
getSrcChainId(): ChainId {
return getChainIdFromAddress(this.tokenInAddress);
}
getDstChainId(): ChainId {
return getChainIdFromAddress(this.tokenOutAddress);
}
}

View File

@ -3,7 +3,7 @@ import { ethers } from "ethers";
import { GenericToken } from "./generic";
// erc20 spec
import { abi as Erc20Abi } from "../abi/erc20.json";
import { abi as Erc20Abi } from "../../abi/erc20.json";
import {
TransactionReceipt,
TransactionRequest,

View File

@ -1,7 +1,40 @@
export abstract class DexRouter {
abstract makeToken(tokenAddress: string): any;
abstract quoteLot(tokenA: any, tokenB: any, amount: string): Promise<any>;
abstract setSlippage(slippage: string): void;
import { FixedNumber } from "ethers";
export enum UstLocation {
In = 1,
Out,
}
export abstract class RouterCore {
abstract computeAndVerifyPoolAddress(): Promise<string>;
abstract computePoolAddress(): string;
//abstract computeUnitAmountIn(amount: string): string;
abstract computeUnitAmountOut(amount: string): string;
abstract fetchExactInQuote(
amountOut: string,
slippage: string
): Promise<string>;
abstract fetchExactOutQuote(
amountOut: string,
slippage: string
): Promise<string>;
abstract formatAmountIn(amount: string): string;
abstract formatAmountOut(amount: string): string;
abstract getProtocol(): string;
abstract getTokenInDecimals(): number;
abstract getTokenOutDecimals(): number;
abstract getTokenOutAddress(): string;
}
export abstract class GenericToken {
@ -9,16 +42,3 @@ export abstract class GenericToken {
abstract getDecimals(): number;
}
// TODO: wrap SwapRoute and other routes
export class GenericRoute {
route: any;
constructor(route: any) {
this.route = route;
}
getRoute(): any {
return this.route;
}
}

View File

@ -1,12 +1,18 @@
import { ethers } from "ethers";
import { QUICKSWAP_FACTORY_ADDRESS } from "../utils/consts";
import { SingleAmmSwapRouter } from "./uniswap-v2";
import { QUICKSWAP_FACTORY_ADDRESS, WMATIC_TOKEN_INFO } from "../utils/consts";
import { UstLocation } from "./generic";
import { UniswapV2Router } from "./uniswap-v2";
export { PROTOCOL } from "./uniswap-v2";
export class QuickswapRouter extends SingleAmmSwapRouter {
export class QuickswapRouter extends UniswapV2Router {
constructor(provider: ethers.providers.Provider) {
super(provider);
super.setFactoryAddress(QUICKSWAP_FACTORY_ADDRESS);
}
async initialize(ustLocation: UstLocation): Promise<void> {
await super.initializeTokens(WMATIC_TOKEN_INFO, ustLocation);
return;
}
}

View File

@ -0,0 +1,67 @@
import { Dec, Int } from "@terra-money/terra.js";
import { UST_TOKEN_INFO } from "../utils/consts";
import { RouterCore } from "./generic";
export const PROTOCOL = "TerraUstTransfer";
const UST_DECIMALS = 6;
const UST_AMOUNT_MULTIPLIER = "1000000";
export class TerraUstTransfer extends RouterCore {
computePoolAddress(): string {
return UST_TOKEN_INFO.address;
}
computeAndVerifyPoolAddress(): Promise<string> {
return new Promise<string>((resolve) => {
return resolve(this.computePoolAddress());
});
}
formatAmountIn(amount: string): string {
const formatted = new Dec(amount).div(UST_AMOUNT_MULTIPLIER);
return formatted.toString();
}
formatAmountOut(amount: string): string {
return this.formatAmountIn(amount);
}
computeUnitAmountIn(amount: string): string {
const unitified = new Dec(amount).mul(UST_AMOUNT_MULTIPLIER);
return new Int(unitified.toString()).toString();
}
computeUnitAmountOut(amount: string): string {
return this.computeUnitAmountIn(amount);
}
getProtocol(): string {
return PROTOCOL;
}
async fetchExactInQuote(amountIn: string, slippage: string): Promise<string> {
return amountIn;
}
async fetchExactOutQuote(
amountOut: string,
slippage: string
): Promise<string> {
return amountOut;
}
getTokenInDecimals(): number {
return UST_DECIMALS;
}
getTokenOutDecimals(): number {
return UST_DECIMALS;
}
getTokenOutAddress(): string {
return this.computePoolAddress();
}
}

View File

@ -2,6 +2,8 @@ import { ethers } from "ethers";
import { CurrencyAmount, Token } from "@uniswap/sdk-core";
import { EvmToken } from "./evm";
import { RouterCore, UstLocation } from "./generic";
import { TokenInfo } from "../utils/consts";
export function computeTradeDeadline(deadline: string): ethers.BigNumber {
return ethers.BigNumber.from(Math.floor(Date.now() / 1000)).add(deadline);
@ -78,46 +80,112 @@ export async function makeUniEvmToken(
return new UniEvmToken(chainId, erc20);
}
export abstract class UniswapRouterCore {
function stringToBigNumber(value: string): ethers.BigNumber {
return ethers.BigNumber.from(value);
}
export interface ExactInParameters {
protocol: string;
amountIn: ethers.BigNumber;
minAmountOut: ethers.BigNumber;
deadline: ethers.BigNumber;
poolFee: string;
path: [string, string];
}
export interface ExactOutParameters {
protocol: string;
amountOut: ethers.BigNumber;
maxAmountIn: ethers.BigNumber;
deadline: ethers.BigNumber;
poolFee: string;
path: [string, string];
}
export function makeExactInParameters(
router: UniswapRouterCore,
amountIn: string,
minAmountOut: string
): ExactInParameters {
const params: ExactInParameters = {
protocol: router.getProtocol(),
amountIn: router.tokenIn.computeUnitAmount(amountIn),
minAmountOut: router.tokenOut.computeUnitAmount(minAmountOut),
poolFee: router.getPoolFee(),
deadline: router.getTradeDeadline(),
path: [router.tokenIn.getAddress(), router.tokenOut.getAddress()],
};
return params;
}
export function makeExactOutParameters(
router: UniswapRouterCore,
amountOut: string,
maxAmountIn: string
): ExactOutParameters {
const params: ExactOutParameters = {
protocol: router.getProtocol(),
amountOut: router.tokenOut.computeUnitAmount(amountOut),
maxAmountIn: router.tokenIn.computeUnitAmount(maxAmountIn),
poolFee: router.getPoolFee(),
deadline: router.getTradeDeadline(),
path: [router.tokenIn.getAddress(), router.tokenOut.getAddress()],
};
return params;
}
export abstract class UniswapRouterCore extends RouterCore {
provider: ethers.providers.Provider;
network: ethers.providers.Network;
// wormhole
chainId: number;
// tokens
tokenIn: UniEvmToken;
tokenOut: UniEvmToken;
// params
deadline: string = "";
constructor(provider: ethers.providers.Provider) {
super();
this.provider = provider;
}
public async makeToken(tokenAddress: string): Promise<UniEvmToken> {
const network = await this.provider.getNetwork();
return makeUniEvmToken(this.provider, network.chainId, tokenAddress);
public getProvider(): ethers.providers.Provider {
return this.provider;
}
abstract computePoolAddress(
tokenIn: UniEvmToken,
tokenOut: UniEvmToken
): string;
public async initializeTokens(
tokenInfo: TokenInfo,
ustLocation: UstLocation
): Promise<void> {
this.network = await this.provider.getNetwork();
abstract computeAndVerifyPoolAddress(
tokenIn: UniEvmToken,
tokenOut: UniEvmToken
): Promise<string>;
const network = this.network;
abstract fetchQuoteAmountOut(
tokenIn: UniEvmToken,
tokenOut: UniEvmToken,
amountOut: string,
slippage: string
): Promise<ethers.BigNumber>;
abstract fetchQuoteAmountIn(
tokenIn: UniEvmToken,
tokenOut: UniEvmToken,
amountOut: string,
slippage: string
): Promise<ethers.BigNumber>;
abstract getProtocol(): string;
if (ustLocation == UstLocation.Out) {
[this.tokenIn, this.tokenOut] = await Promise.all([
makeUniEvmToken(this.provider, network.chainId, tokenInfo.address),
makeUniEvmToken(
this.provider,
network.chainId,
tokenInfo.ustPairedAddress
),
]);
} else {
[this.tokenIn, this.tokenOut] = await Promise.all([
makeUniEvmToken(
this.provider,
network.chainId,
tokenInfo.ustPairedAddress
),
makeUniEvmToken(this.provider, network.chainId, tokenInfo.address),
]);
}
return;
}
public getPoolFee(): string {
return "";
@ -130,4 +198,36 @@ export abstract class UniswapRouterCore {
public getTradeDeadline(): ethers.BigNumber {
return computeTradeDeadline(this.deadline);
}
/*
public computeUnitAmountIn(amount: string): string {
return this.tokenIn.computeUnitAmount(amount).toString();
}
*/
public computeUnitAmountOut(amount: string): string {
return this.tokenOut.computeUnitAmount(amount).toString();
}
public formatAmountIn(amount: string): string {
return this.tokenIn.formatAmount(stringToBigNumber(amount));
}
public formatAmountOut(amount: string): string {
return this.tokenOut.formatAmount(stringToBigNumber(amount));
}
public getTokenInDecimals(): number {
return this.tokenIn.getDecimals();
}
public getTokenOutDecimals(): number {
return this.tokenOut.getDecimals();
}
public getTokenOutAddress(): string {
return this.tokenOut.getAddress();
}
abstract getProtocol(): string;
}

View File

@ -3,11 +3,19 @@ import { CurrencyAmount, TradeType } from "@uniswap/sdk-core";
import { abi as IUniswapV2PairABI } from "@uniswap/v2-core/build/UniswapV2Pair.json";
import { computePairAddress, Pair, Route, Trade } from "@uniswap/v2-sdk";
import { UniEvmToken, UniswapRouterCore } from "./uniswap-core";
import { UniswapRouterCore } from "./uniswap-core";
export const PROTOCOL = "UniswapV2";
export class SingleAmmSwapRouter extends UniswapRouterCore {
// uniswap v3 (ethereum)
//export const UNISWAP_V3_FACTORY_ADDRESS = '0x1F98431c8aD98523631AE4a59f267346ea31F984';
//export const UNISWAP_V3_ROUTER_ADDRESS = '0xE592427A0AEce92De3Edee1F18E0157C05861564';
// quickswap (polygon)
export const QUICKSWAP_V2_ROUTER_ADDRESS =
"0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff";
export class UniswapV2Router extends UniswapRouterCore {
factoryAddress: string;
pairContract: ethers.Contract;
pair: Pair;
@ -17,23 +25,20 @@ export class SingleAmmSwapRouter extends UniswapRouterCore {
return;
}
computePoolAddress(tokenIn: UniEvmToken, tokenOut: UniEvmToken): string {
computePoolAddress(): string {
if (this.factoryAddress === undefined) {
throw Error("factoryAddress is undefined. use setFactoryAddress");
}
return computePairAddress({
factoryAddress: this.factoryAddress,
tokenA: tokenIn.getUniToken(),
tokenB: tokenOut.getUniToken(),
tokenA: this.tokenIn.getUniToken(),
tokenB: this.tokenOut.getUniToken(),
});
}
async computeAndVerifyPoolAddress(
tokenIn: UniEvmToken,
tokenOut: UniEvmToken
): Promise<string> {
const pairAddress = this.computePoolAddress(tokenIn, tokenOut);
async computeAndVerifyPoolAddress(): Promise<string> {
const pairAddress = this.computePoolAddress();
// verify by attempting to call factory()
const poolContract = new ethers.Contract(
@ -46,8 +51,8 @@ export class SingleAmmSwapRouter extends UniswapRouterCore {
return pairAddress;
}
async createPool(tokenIn: UniEvmToken, tokenOut: UniEvmToken): Promise<Pair> {
const pairAddress = this.computePoolAddress(tokenIn, tokenOut);
async createPool(): Promise<Pair> {
const pairAddress = this.computePoolAddress();
const pairContract = new ethers.Contract(
pairAddress,
@ -63,6 +68,9 @@ export class SingleAmmSwapRouter extends UniswapRouterCore {
const reserve0 = reserves._reserve0.toString();
const reserve1 = reserves._reserve1.toString();
const tokenIn = this.tokenIn;
const tokenOut = this.tokenOut;
if (token0.toLowerCase() === tokenIn.getAddress().toLowerCase()) {
return new Pair(
CurrencyAmount.fromRawAmount(tokenIn.getUniToken(), reserve0),
@ -76,15 +84,13 @@ export class SingleAmmSwapRouter extends UniswapRouterCore {
);
}
async fetchQuoteAmountOut(
tokenIn: UniEvmToken,
tokenOut: UniEvmToken,
amountIn: string,
slippage: string
): Promise<ethers.BigNumber> {
async fetchExactInQuote(amountIn: string, slippage: string): Promise<string> {
// create pool
const pair = await this.createPool(tokenIn, tokenOut);
const pair = await this.createPool();
// let's get that quote
const tokenIn = this.tokenIn;
const tokenOut = this.tokenOut;
const route = new Route(
[pair],
@ -108,18 +114,24 @@ export class SingleAmmSwapRouter extends UniswapRouterCore {
.mulUnsafe(slippageMultiplier)
.round(decimals);
return tokenOut.computeUnitAmount(minAmountOutWithSlippage.toString());
/*
return tokenOut
.computeUnitAmount(minAmountOutWithSlippage.toString())
.toString();
*/
return minAmountOutWithSlippage.toString();
}
async fetchQuoteAmountIn(
tokenIn: UniEvmToken,
tokenOut: UniEvmToken,
async fetchExactOutQuote(
amountOut: string,
slippage: string
): Promise<ethers.BigNumber> {
): Promise<string> {
// create pool
const pair = await this.createPool(tokenIn, tokenOut);
const pair = await this.createPool();
// let's get that quote
const tokenIn = this.tokenIn;
const tokenOut = this.tokenOut;
const route = new Route(
[pair],
@ -142,7 +154,12 @@ export class SingleAmmSwapRouter extends UniswapRouterCore {
.divUnsafe(slippageDivisor)
.round(decimals);
return tokenIn.computeUnitAmount(maxAmountInWithSlippage.toString());
/*
return tokenIn
.computeUnitAmount(maxAmountInWithSlippage.toString())
.toString();
*/
return maxAmountInWithSlippage.toString();
}
getProtocol(): string {

View File

@ -14,11 +14,12 @@ import {
} from "@uniswap/v3-sdk";
import { UniEvmToken, UniswapRouterCore } from "./uniswap-core";
import { UNISWAP_V3_FACTORY_ADDRESS } from "../utils/consts";
import { WETH_TOKEN_INFO, UNISWAP_V3_FACTORY_ADDRESS } from "../utils/consts";
import { UstLocation } from "./generic";
export const PROTOCOL = "UniswapV3";
export class SingleAmmSwapRouter extends UniswapRouterCore {
export class UniswapV3Router extends UniswapRouterCore {
poolContract: ethers.Contract;
pool: Pool;
poolFee: FeeAmount;
@ -30,24 +31,26 @@ export class SingleAmmSwapRouter extends UniswapRouterCore {
this.poolFee = FeeAmount.MEDIUM;
}
async initialize(ustLocation: UstLocation): Promise<void> {
await this.initializeTokens(WETH_TOKEN_INFO, ustLocation);
return;
}
getPoolFee(): string {
return this.poolFee.toString();
}
computePoolAddress(tokenIn: UniEvmToken, tokenOut: UniEvmToken): string {
computePoolAddress(): string {
return computePoolAddress({
factoryAddress: UNISWAP_V3_FACTORY_ADDRESS,
fee: this.poolFee,
tokenA: tokenIn.getUniToken(),
tokenB: tokenOut.getUniToken(),
tokenA: this.tokenIn.getUniToken(),
tokenB: this.tokenOut.getUniToken(),
});
}
async computeAndVerifyPoolAddress(
tokenIn: UniEvmToken,
tokenOut: UniEvmToken
): Promise<string> {
const pairAddress = this.computePoolAddress(tokenIn, tokenOut);
async computeAndVerifyPoolAddress(): Promise<string> {
const pairAddress = this.computePoolAddress();
// verify by attempting to call factory()
const poolContract = new ethers.Contract(
@ -60,8 +63,8 @@ export class SingleAmmSwapRouter extends UniswapRouterCore {
return pairAddress;
}
async createPool(tokenIn: UniEvmToken, tokenOut: UniEvmToken): Promise<Pool> {
const poolAddress = this.computePoolAddress(tokenIn, tokenOut);
async createPool(): Promise<Pool> {
const poolAddress = this.computePoolAddress();
const poolContract = new ethers.Contract(
poolAddress,
@ -103,8 +106,8 @@ export class SingleAmmSwapRouter extends UniswapRouterCore {
];
return new Pool(
tokenIn.getUniToken(),
tokenOut.getUniToken(),
this.tokenIn.getUniToken(),
this.tokenOut.getUniToken(),
this.poolFee,
sqrtPriceX96.toString(), //note the description discrepancy - sqrtPriceX96 and sqrtRatioX96 are interchangable values
liquidity,
@ -114,13 +117,15 @@ export class SingleAmmSwapRouter extends UniswapRouterCore {
}
async computeTradeExactIn(
tokenIn: UniEvmToken,
tokenOut: UniEvmToken,
amount: string
): Promise<Trade<Token, Token, TradeType.EXACT_INPUT>> {
// create pool
const pool = await this.createPool(tokenIn, tokenOut);
const pool = await this.createPool();
// let's get that quote
const tokenIn = this.tokenIn;
const tokenOut = this.tokenOut;
const amountIn = tokenIn.computeUnitAmount(amount);
const route = new Route(
@ -136,13 +141,15 @@ export class SingleAmmSwapRouter extends UniswapRouterCore {
}
async computeTradeExactOut(
tokenIn: UniEvmToken,
tokenOut: UniEvmToken,
amount: string
): Promise<Trade<Token, Token, TradeType.EXACT_OUTPUT>> {
// create pool
const pool = await this.createPool(tokenIn, tokenOut);
const pool = await this.createPool();
// let's get that quote
const tokenIn = this.tokenIn;
const tokenOut = this.tokenOut;
const amountOut = tokenOut.computeUnitAmount(amount);
const route = new Route(
@ -160,15 +167,11 @@ export class SingleAmmSwapRouter extends UniswapRouterCore {
);
}
async fetchQuoteAmountOut(
tokenIn: UniEvmToken,
tokenOut: UniEvmToken,
amountIn: string,
slippage: string
): Promise<ethers.BigNumber> {
async fetchExactInQuote(amountIn: string, slippage: string): Promise<string> {
// get the quote
const trade = await this.computeTradeExactIn(tokenIn, tokenOut, amountIn);
const trade = await this.computeTradeExactIn(amountIn);
const tokenOut = this.tokenOut;
const decimals = tokenOut.getDecimals();
// calculate output amount with slippage
@ -183,18 +186,22 @@ export class SingleAmmSwapRouter extends UniswapRouterCore {
.mulUnsafe(slippageMultiplier)
.round(decimals);
return tokenOut.computeUnitAmount(minAmountOutWithSlippage.toString());
/*
return tokenOut
.computeUnitAmount(minAmountOutWithSlippage.toString())
.toString();
*/
return minAmountOutWithSlippage.toString();
}
async fetchQuoteAmountIn(
tokenIn: UniEvmToken,
tokenOut: UniEvmToken,
async fetchExactOutQuote(
amountOut: string,
slippage: string
): Promise<ethers.BigNumber> {
): Promise<string> {
// get the quote
const trade = await this.computeTradeExactOut(tokenIn, tokenOut, amountOut);
const trade = await this.computeTradeExactOut(amountOut);
const tokenIn = this.tokenIn;
const decimals = tokenIn.getDecimals();
// calculate output amount with slippage
@ -209,7 +216,12 @@ export class SingleAmmSwapRouter extends UniswapRouterCore {
.divUnsafe(slippageDivisor)
.round(decimals);
return tokenIn.computeUnitAmount(maxAmountInWithSlippage.toString());
/*
return tokenIn
.computeUnitAmount(maxAmountInWithSlippage.toString())
.toString();
*/
return maxAmountInWithSlippage.toString();
}
getProtocol(): string {

View File

@ -1,15 +1,14 @@
import { ethers } from "ethers";
import { TransactionReceipt } from "@ethersproject/abstract-provider";
import {
CHAIN_ID_POLYGON as WORMHOLE_CHAIN_ID_POLYGON,
CHAIN_ID_ETH as WORMHOLE_CHAIN_ID_ETHEREUM,
ChainId,
getEmitterAddressEth,
hexToUint8Array,
nativeToHexString,
parseSequenceFromLogEth,
getSignedVAAWithRetry,
//getSignedVAAWithRetry,
} from "@certusone/wormhole-sdk";
import getSignedVAAWithRetry from "@certusone/wormhole-sdk/lib/cjs/rpc/getSignedVAAWithRetry";
import { grpc } from "@improbable-eng/grpc-web";
import { UniEvmToken } from "../route/uniswap-core";
import {
@ -21,15 +20,22 @@ import {
UniswapToUniswapQuoter,
} from "../route/cross-quote";
import {
TOKEN_BRIDGE_ADDRESS_ETHEREUM,
TOKEN_BRIDGE_ADDRESS_POLYGON,
TOKEN_BRIDGE_ADDRESS_TERRA,
CORE_BRIDGE_ADDRESS_ETHEREUM,
CORE_BRIDGE_ADDRESS_POLYGON,
TOKEN_BRIDGE_ADDRESS_ETHEREUM,
CORE_BRIDGE_ADDRESS_TERRA,
WORMHOLE_CHAIN_ID_ETHEREUM,
WORMHOLE_CHAIN_ID_POLYGON,
WORMHOLE_CHAIN_ID_TERRA,
WORMHOLE_RPC_HOSTS,
POLYGON_NETWORK_CHAIN_ID,
ETH_NETWORK_CHAIN_ID,
//ETH_NETWORK_CHAIN_ID,
//POLYGON_NETWORK_CHAIN_ID,
//TERRA_NETWORK_CHAIN_ID,
WETH_TOKEN_INFO,
WMATIC_TOKEN_INFO,
UST_TOKEN_INFO,
} from "../utils/consts";
import {
CROSSCHAINSWAP_GAS_PARAMETERS,
@ -38,10 +44,13 @@ import {
swapExactOutFromVaaNative,
swapExactOutFromVaaToken,
} from "./util";
import { abi as SWAP_CONTRACT_V2_ABI } from "../abi/contracts/CrossChainSwapV2.json";
import { abi as SWAP_CONTRACT_V3_ABI } from "../abi/contracts/CrossChainSwapV3.json";
import { SWAP_CONTRACT_ADDRESS as CROSSCHAINSWAP_CONTRACT_ADDRESS_ETHEREUM } from "../addresses/goerli";
import { SWAP_CONTRACT_ADDRESS as CROSSCHAINSWAP_CONTRACT_ADDRESS_POLYGON } from "../addresses/mumbai";
import { abi as SWAP_CONTRACT_V2_ABI } from "../../abi/contracts/CrossChainSwapV2.json";
import { abi as SWAP_CONTRACT_V3_ABI } from "../../abi/contracts/CrossChainSwapV3.json";
import { SWAP_CONTRACT_ADDRESS as CROSSCHAINSWAP_CONTRACT_ADDRESS_ETHEREUM } from "../../scripts/contract-addresses/goerli";
import { SWAP_CONTRACT_ADDRESS as CROSSCHAINSWAP_CONTRACT_ADDRESS_POLYGON } from "../../scripts/contract-addresses/mumbai";
// placeholders
const CROSSCHAINSWAP_CONTRACT_ADDRESS_TERRA = "";
interface SwapContractParameters {
address: string;
@ -80,21 +89,35 @@ const EXECUTION_PARAMETERS_POLYGON: ExecutionParameters = {
},
};
function makeExecutionParameters(id: number): ExecutionParameters {
switch (id) {
case ETH_NETWORK_CHAIN_ID: {
const EXECUTION_PARAMETERS_TERRA: ExecutionParameters = {
crossChainSwap: {
address: CROSSCHAINSWAP_CONTRACT_ADDRESS_TERRA,
},
wormhole: {
chainId: WORMHOLE_CHAIN_ID_TERRA,
coreBridgeAddress: CORE_BRIDGE_ADDRESS_TERRA,
tokenBridgeAddress: TOKEN_BRIDGE_ADDRESS_TERRA,
},
};
function makeExecutionParameters(chainId: ChainId): ExecutionParameters {
switch (chainId) {
case WORMHOLE_CHAIN_ID_ETHEREUM: {
return EXECUTION_PARAMETERS_ETHEREUM;
}
case POLYGON_NETWORK_CHAIN_ID: {
case WORMHOLE_CHAIN_ID_POLYGON: {
return EXECUTION_PARAMETERS_POLYGON;
}
case WORMHOLE_CHAIN_ID_TERRA: {
return EXECUTION_PARAMETERS_TERRA;
}
default: {
throw Error("unrecognized chain id");
}
}
}
async function approveContractTokenSpend(
async function evmApproveContractTokenSpend(
provider: ethers.providers.Provider,
signer: ethers.Signer,
tokenContract: ethers.Contract,
@ -140,7 +163,7 @@ function makeCrossChainSwapV2Contract(
return new ethers.Contract(contractAddress, SWAP_CONTRACT_V2_ABI, provider);
}
function makeCrossChainSwapContract(
function makeCrossChainSwapEvmContract(
provider: ethers.providers.Provider,
protocol: string,
contractAddress: string
@ -163,7 +186,7 @@ function addressToBytes32(
return hexToUint8Array(hexString);
}
async function approveAndSwapExactIn(
async function evmApproveAndSwapExactIn(
srcProvider: ethers.providers.Provider,
srcWallet: ethers.Signer,
srcTokenIn: UniEvmToken,
@ -175,7 +198,7 @@ async function approveAndSwapExactIn(
const swapContractParams = srcExecutionParams.crossChainSwap;
const protocol = quoteParams.src.protocol;
const swapContract = makeCrossChainSwapContract(
const swapContract = makeCrossChainSwapEvmContract(
srcProvider,
protocol,
swapContractParams.address
@ -187,18 +210,19 @@ async function approveAndSwapExactIn(
const address = await srcWallet.getAddress();
const dstWormholeChainId = dstExecutionParams.wormhole.chainId;
const swapParams = [
amountIn,
quoteParams.src.minAmountOut,
quoteParams.dst.minAmountOut,
address,
addressToBytes32(address, dstWormholeChainId),
quoteParams.src.deadline,
quoteParams.dst.poolFee || quoteParams.src.poolFee,
];
const pathArray = quoteParams.src.path.concat(quoteParams.dst.path);
const dstWormholeChainId = dstExecutionParams.wormhole.chainId;
const dstContractAddress = addressToBytes32(
dstExecutionParams.crossChainSwap.address,
dstWormholeChainId
@ -227,7 +251,7 @@ async function approveAndSwapExactIn(
return tx.wait();
} else {
console.info("approving contract to spend token in");
await approveContractTokenSpend(
await evmApproveContractTokenSpend(
srcProvider,
srcWallet,
srcTokenIn.getContract(),
@ -249,7 +273,7 @@ async function approveAndSwapExactIn(
}
}
async function approveAndSwapExactOut(
async function evmApproveAndSwapExactOut(
srcProvider: ethers.providers.Provider,
srcWallet: ethers.Signer,
srcTokenIn: UniEvmToken,
@ -261,7 +285,7 @@ async function approveAndSwapExactOut(
const swapContractParams = srcExecutionParams.crossChainSwap;
const protocol = quoteParams.src.protocol;
const swapContract = makeCrossChainSwapContract(
const swapContract = makeCrossChainSwapEvmContract(
srcProvider,
protocol,
swapContractParams.address
@ -274,17 +298,18 @@ async function approveAndSwapExactOut(
const address = await srcWallet.getAddress();
const dstWormholeChainId = dstExecutionParams.wormhole.chainId;
const swapParams = [
amountOut,
maxAmountIn,
quoteParams.dst.amountOut,
address,
addressToBytes32(address, dstWormholeChainId),
quoteParams.src.deadline,
quoteParams.dst.poolFee || quoteParams.src.poolFee,
];
const pathArray = quoteParams.src.path.concat(quoteParams.dst.path);
const dstWormholeChainId = dstExecutionParams.wormhole.chainId;
const dstContractAddress = addressToBytes32(
dstExecutionParams.crossChainSwap.address,
dstWormholeChainId
@ -313,7 +338,7 @@ async function approveAndSwapExactOut(
return tx.wait();
} else {
console.info("approving contract to spend token in");
await approveContractTokenSpend(
await evmApproveContractTokenSpend(
srcProvider,
srcWallet,
srcTokenIn.getContract(),
@ -345,7 +370,7 @@ async function swapExactInFromVaa(
): Promise<TransactionReceipt> {
const swapContractParams = dstExecutionParams.crossChainSwap;
const swapContract = makeCrossChainSwapContract(
const swapContract = makeCrossChainSwapEvmContract(
dstProvider,
dstProtocol,
swapContractParams.address
@ -371,7 +396,7 @@ async function swapExactOutFromVaa(
): Promise<TransactionReceipt> {
const swapContractParams = dstExecutionParams.crossChainSwap;
const swapContract = makeCrossChainSwapContract(
const swapContract = makeCrossChainSwapEvmContract(
dstProvider,
dstProtocol,
swapContractParams.address
@ -399,7 +424,7 @@ interface VaaSearchParams {
emitterAddress: string;
}
export function makeProvider(tokenAddress: string) {
export function makeEvmProvider(tokenAddress: string) {
switch (tokenAddress) {
case WETH_TOKEN_INFO.address: {
const url = process.env.REACT_APP_GOERLI_PROVIDER;
@ -416,6 +441,7 @@ export function makeProvider(tokenAddress: string) {
return new ethers.providers.StaticJsonRpcProvider(url);
}
default: {
console.log("huh?", tokenAddress);
throw Error("unrecognized token address");
}
}
@ -440,8 +466,16 @@ export class UniswapToUniswapExecutor {
transportFactory: grpc.TransportFactory;
vaaSearchParams: VaaSearchParams;
vaaBytes: Uint8Array;
srcReceipt: TransactionReceipt;
dstReceipt: TransactionReceipt;
// receipts
srcEvmReceipt: TransactionReceipt;
dstEvmReceipt: TransactionReceipt;
srcTerraReceipt: any;
dstTerraReceipt: any;
constructor() {
this.quoter = new UniswapToUniswapQuoter();
}
async initialize(
tokenInAddress: string,
@ -450,20 +484,14 @@ export class UniswapToUniswapExecutor {
): Promise<void> {
this.isNative = isNative;
const srcProvider = makeProvider(tokenInAddress);
const dstProvider = makeProvider(tokenOutAddress);
this.quoter = new UniswapToUniswapQuoter(srcProvider, dstProvider);
await this.quoter.initialize();
await this.makeTokens(tokenInAddress, tokenOutAddress);
await this.quoter.initialize(tokenInAddress, tokenOutAddress);
// now that we have a chain id for each network, get contract info for each chain
this.srcExecutionParams = makeExecutionParameters(
this.quoter.srcNetwork.chainId
this.quoter.getSrcChainId()
);
this.dstExecutionParams = makeExecutionParameters(
this.quoter.dstNetwork.chainId
this.quoter.getDstChainId()
);
}
@ -483,6 +511,7 @@ export class UniswapToUniswapExecutor {
this.quoter.setDeadlines(deadline);
}
/*
async makeTokens(
tokenInAddress: string,
tokenOutAddress: string
@ -507,7 +536,7 @@ export class UniswapToUniswapExecutor {
getTokens(): CrossChainSwapTokens {
return this.tokens;
}
*/
async computeAndVerifySrcPoolAddress(): Promise<string> {
return this.quoter.computeAndVerifySrcPoolAddress();
}
@ -546,19 +575,19 @@ export class UniswapToUniswapExecutor {
return this.cachedExactOutParams;
}
getSrcProvider(): ethers.providers.Provider {
return this.quoter.srcProvider;
getSrcEvmProvider(): ethers.providers.Provider {
return this.quoter.getSrcEvmProvider();
}
getDstProvider(): ethers.providers.Provider {
return this.quoter.dstProvider;
getDstEvmProvider(): ethers.providers.Provider {
return this.quoter.getDstEvmProvider();
}
async approveAndSwapExactIn(
async evmApproveAndSwapExactIn(
wallet: ethers.Signer
): Promise<TransactionReceipt> {
return approveAndSwapExactIn(
this.getSrcProvider(),
return evmApproveAndSwapExactIn(
this.getSrcEvmProvider(),
wallet,
this.tokens.srcIn,
this.cachedExactInParams,
@ -568,11 +597,11 @@ export class UniswapToUniswapExecutor {
);
}
async approveAndSwapExactOut(
async evmApproveAndSwapExactOut(
wallet: ethers.Signer
): Promise<TransactionReceipt> {
return approveAndSwapExactOut(
this.getSrcProvider(),
return evmApproveAndSwapExactOut(
this.getSrcEvmProvider(),
wallet,
this.tokens.srcIn,
this.cachedExactOutParams,
@ -582,23 +611,40 @@ export class UniswapToUniswapExecutor {
);
}
async approveAndSwap(wallet: ethers.Signer): Promise<TransactionReceipt> {
srcIsUst(): boolean {
return (
this.quoter.tokenInAddress === UST_TOKEN_INFO.address &&
this.cachedExactInParams.src === undefined
);
}
async evmApproveAndSwap(wallet: ethers.Signer): Promise<TransactionReceipt> {
const quoteType = this.quoteType;
if (quoteType === QuoteType.ExactIn) {
this.srcReceipt = await this.approveAndSwapExactIn(wallet);
this.srcEvmReceipt = await this.evmApproveAndSwapExactIn(wallet);
} else if (quoteType === QuoteType.ExactOut) {
this.srcReceipt = await this.approveAndSwapExactOut(wallet);
this.srcEvmReceipt = await this.evmApproveAndSwapExactOut(wallet);
} else {
throw Error("no quote found");
}
this.fetchAndSetEmitterAndSequence();
return this.srcReceipt;
return this.srcEvmReceipt;
}
fetchAndSetEmitterAndSequence(): void {
const receipt = this.srcReceipt;
// TODO
return;
}
fetchAndSetTerraEmitterAndSequence(): void {
// TODO
return;
}
fetchAndSetEvmEmitterAndSequence(): void {
const receipt = this.srcEvmReceipt;
if (receipt === undefined) {
throw Error("no swap receipt found");
}
@ -623,11 +669,15 @@ export class UniswapToUniswapExecutor {
const emitterAddress = vaaSearchParams.emitterAddress;
console.info(`sequence: ${sequence}, emitterAddress: ${emitterAddress}`);
// wait for VAA to be signed
const vaaResponse = await getSignedVAAWithRetry(
WORMHOLE_RPC_HOSTS,
this.srcExecutionParams.wormhole.chainId,
vaaSearchParams.emitterAddress,
vaaSearchParams.sequence
vaaSearchParams.sequence,
{
transport: this.transportFactory,
}
);
// grab vaaBytes
this.vaaBytes = vaaResponse.vaaBytes;
@ -636,22 +686,27 @@ export class UniswapToUniswapExecutor {
async fetchVaaAndSwap(wallet: ethers.Signer): Promise<TransactionReceipt> {
await this.fetchSignedVaaFromSwap();
// check if Terra transaction
// TODO: change return as something else (not evm TransactionReceipt)
const quoteType = this.quoteType;
if (quoteType === QuoteType.ExactIn) {
this.dstReceipt = await this.swapExactInFromVaa(wallet);
this.dstEvmReceipt = await this.evmSwapExactInFromVaa(wallet);
} else if (quoteType === QuoteType.ExactOut) {
this.dstReceipt = await this.swapExactOutFromVaa(wallet);
this.dstEvmReceipt = await this.evmSwapExactOutFromVaa(wallet);
} else {
throw Error("no quote found");
}
return this.dstReceipt;
return this.dstEvmReceipt;
}
async swapExactInFromVaa(wallet: ethers.Signer): Promise<TransactionReceipt> {
async evmSwapExactInFromVaa(
wallet: ethers.Signer
): Promise<TransactionReceipt> {
return swapExactInFromVaa(
this.getDstProvider(),
this.getDstEvmProvider(),
wallet,
this.dstExecutionParams,
this.cachedExactInParams.dst.protocol,
@ -660,11 +715,11 @@ export class UniswapToUniswapExecutor {
);
}
async swapExactOutFromVaa(
async evmSwapExactOutFromVaa(
wallet: ethers.Signer
): Promise<TransactionReceipt> {
return swapExactOutFromVaa(
this.getDstProvider(),
this.getDstEvmProvider(),
wallet,
this.dstExecutionParams,
this.cachedExactOutParams.dst.protocol,
@ -673,6 +728,10 @@ export class UniswapToUniswapExecutor {
);
}
setTransport(transportFactory: grpc.TransportFactory) {
this.transportFactory = transportFactory;
}
//getSwapResult(
// walletAddress: string,
// onSwapResult: (result: boolean) => void
@ -680,7 +739,7 @@ export class UniswapToUniswapExecutor {
// console.log(this.cachedExactInParams.dst.protocol);
// console.log(this.dstExecutionParams.crossChainSwap.address);
// const contract = makeCrossChainSwapContract(
// this.getDstProvider(),
// this.getDstEvmProvider(),
// this.quoteType === QuoteType.ExactIn
// ? this.cachedExactInParams.dst.protocol
// : this.cachedExactOutParams.dst.protocol,

View File

@ -1,10 +1,21 @@
import {
ChainId,
CHAIN_ID_ETH,
CHAIN_ID_POLYGON,
CHAIN_ID_ETH as WORMHOLE_CHAIN_ID_ETHEREUM,
CHAIN_ID_POLYGON as WORMHOLE_CHAIN_ID_POLYGON,
CHAIN_ID_TERRA as WORMHOLE_CHAIN_ID_TERRA,
} from "@certusone/wormhole-sdk";
import ethIcon from "../icons/eth.svg";
import polygonIcon from "../icons/polygon.svg";
//import ethIcon from "../icons/eth.svg";
//import polygonIcon from "../icons/polygon.svg";
const ethIcon = undefined;
const polygonIcon = undefined;
const ustIcon = undefined;
export {
WORMHOLE_CHAIN_ID_ETHEREUM,
WORMHOLE_CHAIN_ID_POLYGON,
WORMHOLE_CHAIN_ID_TERRA,
};
export interface TokenInfo {
name: string;
@ -13,42 +24,57 @@ export interface TokenInfo {
logo: string;
isNative: boolean;
maxAmount: number;
ustPairedAddress: string;
}
export const MATIC_TOKEN_INFO: TokenInfo = {
name: "MATIC",
address: "0x9c3c9283d3e44854697cd22d3faa240cfb032889", // used to compute quote
chainId: CHAIN_ID_POLYGON,
chainId: WORMHOLE_CHAIN_ID_POLYGON,
logo: polygonIcon,
isNative: true,
maxAmount: 0.1,
ustPairedAddress: "0xe3a1c77e952b57b5883f6c906fc706fcc7d4392c",
};
export const WMATIC_TOKEN_INFO: TokenInfo = {
name: "WMATIC",
address: "0x9c3c9283d3e44854697cd22d3faa240cfb032889",
chainId: CHAIN_ID_POLYGON,
chainId: WORMHOLE_CHAIN_ID_POLYGON,
logo: polygonIcon,
isNative: false,
maxAmount: 0.1,
ustPairedAddress: "0xe3a1c77e952b57b5883f6c906fc706fcc7d4392c",
};
export const ETH_TOKEN_INFO: TokenInfo = {
name: "ETH",
address: "0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6", // used to compute quote
chainId: CHAIN_ID_ETH,
chainId: WORMHOLE_CHAIN_ID_ETHEREUM,
logo: ethIcon,
isNative: true,
maxAmount: 0.01,
ustPairedAddress: "0x36Ed51Afc79619b299b238898E72ce482600568a",
};
export const WETH_TOKEN_INFO: TokenInfo = {
name: "WETH",
address: "0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6",
chainId: CHAIN_ID_ETH,
chainId: WORMHOLE_CHAIN_ID_ETHEREUM,
logo: ethIcon,
isNative: false,
maxAmount: 0.01,
ustPairedAddress: "0x36Ed51Afc79619b299b238898E72ce482600568a",
};
export const UST_TOKEN_INFO: TokenInfo = {
name: "UST",
address: "uusd",
chainId: WORMHOLE_CHAIN_ID_TERRA,
logo: ustIcon,
isNative: false,
maxAmount: 10.0,
ustPairedAddress: undefined,
};
export const TOKEN_INFOS = [
@ -56,19 +82,21 @@ export const TOKEN_INFOS = [
WMATIC_TOKEN_INFO,
ETH_TOKEN_INFO,
WETH_TOKEN_INFO,
UST_TOKEN_INFO,
];
export const ETH_NETWORK_CHAIN_ID = 5;
export const POLYGON_NETWORK_CHAIN_ID = 80001;
// evm handling
export const EVM_ETH_NETWORK_CHAIN_ID = 5;
export const EVM_POLYGON_NETWORK_CHAIN_ID = 80001;
export const getEvmChainId = (chainId: ChainId) =>
chainId === CHAIN_ID_ETH
? ETH_NETWORK_CHAIN_ID
: chainId === CHAIN_ID_POLYGON
? POLYGON_NETWORK_CHAIN_ID
chainId === WORMHOLE_CHAIN_ID_ETHEREUM
? EVM_ETH_NETWORK_CHAIN_ID
: chainId === WORMHOLE_CHAIN_ID_POLYGON
? EVM_POLYGON_NETWORK_CHAIN_ID
: undefined;
// misc
export const RELAYER_FEE_UST = "0.25";
export const WORMHOLE_RPC_HOSTS = [
@ -81,12 +109,16 @@ export const CORE_BRIDGE_ADDRESS_ETHEREUM =
export const CORE_BRIDGE_ADDRESS_POLYGON =
"0x0CBE91CF822c73C2315FB05100C2F714765d5c20";
export const CORE_BRIDGE_ADDRESS_TERRA = undefined;
export const TOKEN_BRIDGE_ADDRESS_ETHEREUM =
"0xF890982f9310df57d00f659cf4fd87e65adEd8d7";
export const TOKEN_BRIDGE_ADDRESS_POLYGON =
"0x377D55a7928c046E18eEbb61977e714d2a76472a";
export const TOKEN_BRIDGE_ADDRESS_TERRA = undefined;
export const QUICKSWAP_FACTORY_ADDRESS =
"0x5757371414417b8C6CAad45bAeF941aBc7d3Ab32";

19
react/src/utils/math.ts Normal file
View File

@ -0,0 +1,19 @@
import { FixedNumber } from "ethers";
export function addFixedAmounts(
left: string,
right: string,
decimals: number
): string {
const sum = FixedNumber.from(left).addUnsafe(FixedNumber.from(right));
return sum.round(this.getDecimals()).toString();
}
export function subtractFixedAmounts(
left: string,
right: string,
decimals: number
): string {
const diff = FixedNumber.from(left).subUnsafe(FixedNumber.from(right));
return diff.round(decimals).toString();
}