Skip to content

Commit

Permalink
Merge pull request #349 from oraichain/feat/support-swap-oraidex-osmosis
Browse files Browse the repository at this point in the history
Feat/support swap oraidex osmosis
  • Loading branch information
haunv3 authored Oct 23, 2024
2 parents 09c7db1 + 1e2b60e commit 9df748d
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 86 deletions.
2 changes: 1 addition & 1 deletion packages/universal-swap/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@oraichain/oraidex-universal-swap",
"version": "1.1.14",
"version": "1.1.15",
"main": "build/index.js",
"files": [
"build/"
Expand Down
104 changes: 67 additions & 37 deletions packages/universal-swap/src/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ import {
import { GasPrice } from "@cosmjs/stargate";
import { OraiswapRouterQueryClient } from "@oraichain/oraidex-contracts-sdk";
import { Affiliate } from "@oraichain/oraidex-contracts-sdk/build/OraiswapMixedRouter.types";
import { COSMOS_CHAIN_IDS } from "@oraichain/common";
import { COSMOS_CHAIN_IDS, EVM_CHAIN_IDS } from "@oraichain/common";
import { generateMsgSwap } from "./msg/msgs";

const AFFILIATE_DECIMAL = 1e4; // 10_000
Expand Down Expand Up @@ -277,7 +277,6 @@ export class UniversalSwapHandler {
let ibcInfos = ibcInfo;
let getEncodedExecuteMsgs = [];
if (["kawaii-islands", "milky-token"].includes(originalToToken.coinGeckoId)) {
const IBC_DECIMALS = 18;
const toTokenInOrai = this.getTokenOnOraichain(originalToToken.coinGeckoId, true);
const evmToken = tokenMap[toTokenInOrai.denom];
const evmAmount = coin(toAmount(this.swapData.fromAmount, evmToken.decimals).toString(), evmToken.denom);
Expand Down Expand Up @@ -1085,16 +1084,13 @@ export class UniversalSwapHandler {
);

// get swapRoute
const [oraiAddress, obridgeAddress] = await Promise.all([
this.config.cosmosWallet.getKeplrAddr("Oraichain"),
this.config.cosmosWallet.getKeplrAddr("oraibridge-subnet-2")
]);
const oraiAddress = await this.config.cosmosWallet.getKeplrAddr("Oraichain");

let minimumReceive = simulateAmount;
if (this.config.swapOptions?.isIbcWasm) minimumReceive = await this.caculateMinimumReceive();
if (this.config.swapOptions?.isIbcWasm) minimumReceive = await this.calculateMinimumReceive();

const { swapRoute: completeSwapRoute } = await UniversalSwapHelper.addOraiBridgeRoute(
{ obridgeAddress, sourceReceiver: oraiAddress, destReceiver: destinationReceiver },
{ sourceReceiver: oraiAddress, destReceiver: destinationReceiver },
originalFromToken,
originalToToken,
minimumReceive,
Expand Down Expand Up @@ -1174,55 +1170,89 @@ export class UniversalSwapHandler {
return this.transferAndSwap(swapRoute);
}

async processUniversalSwap() {
const { evm, tron } = this.swapData.sender;
const { originalFromToken, originalToToken, simulateAmount, recipientAddress, userSlippage, alphaSmartRoutes } =
this.swapData;
const { swapOptions } = this.config;
let toAddress = "";
const currentToNetwork = originalToToken.chainId;

async getToAddressUniversalSwap(evm, tron, recipientAddress, originalToToken) {
if (this.swapData.recipientAddress) {
const isValidRecipient = checkValidateAddressWithNetwork(recipientAddress, currentToNetwork);
const isValidRecipient = checkValidateAddressWithNetwork(recipientAddress, originalToToken.chainId);

if (!isValidRecipient.isValid) throw generateError("Recipient address invalid!");

toAddress =
originalToToken.chainId === ChainIdEnum.TRON
? tronToEthAddress(recipientAddress)
: this.swapData.recipientAddress;
return originalToToken.chainId === ChainIdEnum.TRON
? tronToEthAddress(recipientAddress)
: this.swapData.recipientAddress;
} else {
toAddress = await this.getUniversalSwapToAddress(originalToToken.chainId, {
return await this.getUniversalSwapToAddress(originalToToken.chainId, {
metamaskAddress: evm,
tronAddress: tron
});
}
}

async processUniversalSwap() {
const { evm, tron } = this.swapData.sender;
const { originalFromToken, originalToToken, simulateAmount, recipientAddress, userSlippage, alphaSmartRoutes } =
this.swapData;
const { swapOptions } = this.config;

const toAddress = await this.getToAddressUniversalSwap(evm, tron, this.swapData.recipientAddress, originalToToken);
const oraiAddress = await this.config.cosmosWallet.getKeplrAddr(COSMOS_CHAIN_IDS.ORAICHAIN);
if (!oraiAddress) throw generateError("orai address and obridge address invalid!");
const isValidRecipientOraichain = checkValidateAddressWithNetwork(oraiAddress, COSMOS_CHAIN_IDS.ORAICHAIN);
if (!isValidRecipientOraichain.isValid) throw generateError("orai get address invalid!");

let injAddress = undefined;
const [oraiAddress, obridgeAddress] = await Promise.all([
this.config.cosmosWallet.getKeplrAddr(COSMOS_CHAIN_IDS.ORAICHAIN),
this.config.cosmosWallet.getKeplrAddr(COSMOS_CHAIN_IDS.ORAIBRIDGE)
]);
let evmAddress = undefined;
let tronAddress = undefined;
let addressParams = {
oraiAddress,
injAddress,
evmInfo: {}
};

let minimumReceive = simulateAmount;
if (swapOptions?.isIbcWasm) minimumReceive = await this.caculateMinimumReceive();

if (swapOptions?.isIbcWasm) minimumReceive = await this.calculateMinimumReceive();
if (swapOptions?.isAlphaIbcWasm) {
const routesFlatten = UniversalSwapHelper.flattenSmartRouters(alphaSmartRoutes.routes);
const hasInjectiveAddress = routesFlatten.some((route) =>
[route.chainId, route.tokenOutChainId].includes(COSMOS_CHAIN_IDS.INJECTVE)
);
const [hasEVMChains, hasTron, hasInjectiveAddress] = [
routesFlatten.some((route) =>
[route.chainId, route.tokenOutChainId].some((id) =>
[EVM_CHAIN_IDS.ETH, EVM_CHAIN_IDS.BSC].includes(id as any)
)
),
routesFlatten.some((route) => [route.chainId, route.tokenOutChainId].includes(EVM_CHAIN_IDS.TRON)),
routesFlatten.some((route) => [route.chainId, route.tokenOutChainId].includes(COSMOS_CHAIN_IDS.INJECTVE))
];

if (hasInjectiveAddress) injAddress = await this.config.cosmosWallet.getKeplrAddr(COSMOS_CHAIN_IDS.INJECTVE);
if (hasInjectiveAddress) {
injAddress = await this.config.cosmosWallet.getKeplrAddr(COSMOS_CHAIN_IDS.INJECTVE);
addressParams.injAddress = injAddress;
}

if (hasEVMChains) {
evmAddress = this.swapData.sender.evm;
addressParams.evmInfo = {
...addressParams.evmInfo,
[EVM_CHAIN_IDS.ETH]: this.swapData.sender.evm,
[EVM_CHAIN_IDS.BSC]: this.swapData.sender.evm
};
}

if (hasTron) {
tronAddress = tronToEthAddress(this.swapData.sender.tron);
addressParams.evmInfo = {
...addressParams.evmInfo,
[EVM_CHAIN_IDS.TRON]: tronToEthAddress(this.swapData.sender.tron)
};
}
}

const { swapRoute, universalSwapType } = await UniversalSwapHelper.addOraiBridgeRoute(
{
obridgeAddress,
injAddress,
sourceReceiver: oraiAddress,
destReceiver: toAddress,
recipientAddress
recipientAddress,
evmAddress,
tronAddress
},
originalFromToken,
originalToToken,
Expand All @@ -1233,8 +1263,8 @@ export class UniversalSwapHandler {
);

if (alphaSmartRoutes?.routes?.length && swapOptions.isAlphaIbcWasm) {
let receiverAddresses = UniversalSwapHelper.generateAddress({ oraiAddress, injAddress });
if (recipientAddress) receiverAddresses[currentToNetwork] = toAddress;
let receiverAddresses = UniversalSwapHelper.generateAddress(addressParams);
if (recipientAddress) receiverAddresses[originalToToken.chainId] = toAddress;
return this.alphaSmartRouterSwapNewMsg(swapRoute, universalSwapType, receiverAddresses);
}

Expand All @@ -1253,7 +1283,7 @@ export class UniversalSwapHandler {
return this.transferAndSwap(swapRoute);
}

async caculateMinimumReceive() {
async calculateMinimumReceive() {
const { relayerFee, simulateAmount, originalToToken, bridgeFee = 1, userSlippage = 0 } = this.swapData;
const { cosmosWallet } = this.config;

Expand Down
96 changes: 74 additions & 22 deletions packages/universal-swap/src/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ import {
parseAssetInfoFromContractAddrOrDenom,
parseAssetInfo,
calculateTimeoutTimestamp,
COSMOS_CHAIN_ID_COMMON
COSMOS_CHAIN_ID_COMMON,
checkValidateAddressWithNetwork,
cosmosChains
} from "@oraichain/oraidex-common";
import {
ConvertReverse,
Expand Down Expand Up @@ -79,7 +81,7 @@ import { Amount, CwIcs20LatestQueryClient, Uint128 } from "@oraichain/common-con
import { CosmWasmClient, ExecuteInstruction, toBinary } from "@cosmjs/cosmwasm-stargate";
import { swapFromTokens, swapToTokens } from "./swap-filter";
import { Coin } from "@cosmjs/proto-signing";
import { AXIOS_TIMEOUT, IBC_TRANSFER_TIMEOUT } from "@oraichain/common";
import { AXIOS_TIMEOUT, COSMOS_CHAIN_IDS, EVM_CHAIN_IDS, IBC_TRANSFER_TIMEOUT } from "@oraichain/common";
import { TransferBackMsg } from "@oraichain/common-contracts-sdk/build/CwIcs20Latest.types";
import { buildUniversalSwapMemo } from "./proto/universal-swap-memo-proto-handler";
import { Affiliate } from "@oraichain/oraidex-contracts-sdk/build/OraiswapMixedRouter.types";
Expand Down Expand Up @@ -222,6 +224,9 @@ export class UniversalSwapHelper {
contractAddress?: string,
isSourceReceiverTest?: boolean
): string => {
const isValidRecipient = checkValidateAddressWithNetwork(oraiAddress, COSMOS_CHAIN_IDS.ORAICHAIN);
if (!isValidRecipient.isValid) throw generateError("orai Address get source receiver invalid!");

let sourceReceiver = `${oraib2oraichain}/${oraiAddress}`;
// TODO: test retire v2 (change structure memo evm -> oraichain)
if (isSourceReceiverTest) {
Expand Down Expand Up @@ -328,29 +333,58 @@ export class UniversalSwapHelper {
return toBech32(prefix, data);
};

static generateAddress = ({ oraiAddress, injAddress }) => {
/**
* need update when support new chain
*/
const addressFollowCoinType = { address60: injAddress, address118: oraiAddress };
return {
[COSMOS_CHAIN_ID_COMMON.INJECTVE_CHAIN_ID]: injAddress,
[COSMOS_CHAIN_ID_COMMON.ORAICHAIN_CHAIN_ID]: oraiAddress,
[COSMOS_CHAIN_ID_COMMON.ORAIBRIDGE_CHAIN_ID]: UniversalSwapHelper.getAddress("oraib", addressFollowCoinType),
[COSMOS_CHAIN_ID_COMMON.COSMOSHUB_CHAIN_ID]: UniversalSwapHelper.getAddress("cosmos", addressFollowCoinType),
[COSMOS_CHAIN_ID_COMMON.OSMOSIS_CHAIN_ID]: UniversalSwapHelper.getAddress("osmo", addressFollowCoinType),
[COSMOS_CHAIN_ID_COMMON.NOBLE_CHAIN_ID]: UniversalSwapHelper.getAddress("noble", addressFollowCoinType),
[COSMOS_CHAIN_ID_COMMON.CELESTIA_CHAIN_ID]: UniversalSwapHelper.getAddress("celestia", addressFollowCoinType)
static generateAddress = ({
oraiAddress,
injAddress,
evmInfo
}: {
oraiAddress: string;
injAddress?: string;
evmInfo?: {
[key: string]: string;
};
}) => {
const addressFollowCoinType = { address60: injAddress, address118: oraiAddress };
let cosmosAddress = cosmosChains.reduce(
(acc, cur) => {
if (!injAddress && cur?.bip44?.coinType === 60) return acc;
// coinType 118 cosmos
if (!acc?.[cur.chainId]) {
return {
...acc,
[cur.chainId]: UniversalSwapHelper.getAddress(
cur.bech32Config.bech32PrefixAccAddr,
addressFollowCoinType,
cur.bip44.coinType
)
};
}
return acc;
},
{
Oraichain: oraiAddress,
[COSMOS_CHAIN_IDS.INJECTVE]: injAddress
}
);

if (evmInfo) {
cosmosAddress = {
...cosmosAddress,
...evmInfo
};
}

return cosmosAddress;
};

static addOraiBridgeRoute = async (
addresses: {
obridgeAddress?: string;
sourceReceiver: string;
injAddress?: string;
destReceiver?: string;
recipientAddress?: string;
evmAddress?: string;
tronAddress?: string;
},
fromToken: TokenItemType,
toToken: TokenItemType,
Expand Down Expand Up @@ -380,13 +414,17 @@ export class UniversalSwapHelper {

if (isSmartRouter && swapOption.isIbcWasm && !swapOption?.isAlphaIbcWasm) {
if (!alphaSmartRoute && fromToken.coinGeckoId !== toToken.coinGeckoId) throw generateError(`Missing router !`);
const obridgeAddress = UniversalSwapHelper.getAddress("oraib", {
address118: addresses.sourceReceiver,
address60: addresses.injAddress
});

swapRoute = await UniversalSwapHelper.getRouteV2(
{
minimumReceive,
recoveryAddr: addresses.sourceReceiver,
destReceiver: addresses.destReceiver,
remoteAddressObridge: addresses.obridgeAddress
remoteAddressObridge: obridgeAddress
},
{
...alphaSmartRoute,
Expand All @@ -399,20 +437,34 @@ export class UniversalSwapHelper {
}

/**
* useAlphaIbcWasm case: (evm -> oraichain -> osmosis -> inj/tia not using wasm)
* useAlphaIbcWasm case:
* evm -> oraichain -> osmosis -> inj/tia
* tia/inj -> osmosis -> oraichain -> evm
*/
if (swapOption.isAlphaIbcWasm && !fromToken.cosmosBased) {
if (swapOption.isAlphaIbcWasm) {
if (!alphaSmartRoute) throw generateError(`Missing router with alpha ibc wasm!`);
const routes = alphaSmartRoute.routes;
const alphaRoutes = routes[0];
let paths = alphaRoutes.paths;

if (alphaSmartRoute.routes.length > 1) throw generateError(`Missing router with alpha ibc wasm max length!`);
const fromTokenIsEvm = !fromToken.cosmosBased;
// if from is EVM only support one routes
if (fromTokenIsEvm && alphaSmartRoute.routes.length > 1) {
throw generateError(`Missing router with alpha ibc wasm max length!`);
}

const paths = alphaRoutes.paths.filter((_, index) => index > 0);
if (fromTokenIsEvm) {
paths = alphaRoutes.paths.filter((_, index) => index > 0);
}

let receiverAddresses = UniversalSwapHelper.generateAddress({
injAddress: addresses.injAddress,
oraiAddress: addresses.sourceReceiver
oraiAddress: addresses.sourceReceiver,
evmInfo: !toToken.cosmosBased
? {
[toToken.chainId]: toToken.chainId === EVM_CHAIN_IDS.TRON ? addresses.tronAddress : addresses.evmAddress
}
: {}
});

if (addresses?.recipientAddress) receiverAddresses[toToken.chainId] = addresses?.recipientAddress;
Expand Down
8 changes: 3 additions & 5 deletions packages/universal-swap/src/msg/chains/oraichain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -499,11 +499,9 @@ export class OraichainMsg extends ChainMsg {
msg: toUtf8(
JSON.stringify({
send: {
send: {
contract: this.ENTRY_POINT_CONTRACT,
amount: this.path.tokenInAmount,
msg: toBinary(msg)
}
contract: this.ENTRY_POINT_CONTRACT,
amount: this.path.tokenInAmount,
msg: toBinary(msg)
}
})
),
Expand Down
Loading

0 comments on commit 9df748d

Please sign in to comment.