Skip to content

Commit

Permalink
2690, add Lifi on-chain/cross-chain provider for Solana (#689)
Browse files Browse the repository at this point in the history
  • Loading branch information
IDIDOS authored Oct 25, 2024
2 parents 2fd7372 + b92443b commit a5a3724
Show file tree
Hide file tree
Showing 29 changed files with 785 additions and 134 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rubic-sdk",
"version": "5.41.3",
"version": "5.41.4",
"description": "Simplify dApp creation",
"main": "lib/index.js",
"types": "lib/index.d.ts",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { VersionedTransaction } from '@solana/web3.js';
import { base64 } from 'ethers/lib/utils';
import { BLOCKCHAIN_NAME, BlockchainName } from 'src/core/blockchain/models/blockchain-name';
import { EvmWeb3Private } from 'src/core/blockchain/web3-private-service/web3-private/evm-web3-private/evm-web3-private';
import { Web3Error } from 'src/core/blockchain/web3-private-service/web3-private/models/web3.error';
Expand All @@ -18,8 +19,11 @@ export class SolanaWeb3Private extends Web3Private {
public async sendTransaction(options: SolanaTransactionOptions = {}): Promise<string> {
try {
const web3Public = Injector.web3PublicService.getWeb3Public(BLOCKCHAIN_NAME.SOLANA);
const decodedData = options.data!.startsWith('0x')
? Buffer.from(options.data!.slice(2), 'hex')
: base64.decode(options.data!);

const tx = VersionedTransaction.deserialize(Buffer.from(options.data!.slice(2), 'hex'));
const tx = VersionedTransaction.deserialize(decodedData);
const { blockhash } = await web3Public.getRecentBlockhash();
tx.message.recentBlockhash = blockhash;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { compareAddresses } from 'src/common/utils/blockchain';
import { staticImplements } from 'src/common/utils/decorators';
import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name';
import { isChangenowReceiverAddressCorrect } from 'src/core/blockchain/utils/changenow-receiver-address-validator';
import { TypedWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/typed-web3-pure';
import { changenowApiBlockchain } from 'src/features/cross-chain/calculation-manager/providers/changenow-provider/constants/changenow-api-blockchain';

@staticImplements<TypedWeb3Pure>()
export class SolanaWeb3Pure {
Expand All @@ -23,7 +23,7 @@ export class SolanaWeb3Pure {
public static async isAddressCorrect(address: string): Promise<boolean> {
return isChangenowReceiverAddressCorrect(
address,
changenowApiBlockchain.SOLANA,
BLOCKCHAIN_NAME.SOLANA,
/^[1-9A-HJ-NP-Za-km-z]{32,44}$/
);
}
Expand Down
1 change: 1 addition & 0 deletions src/features/common/constants/fake-wallet-address.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/* used for passing fromAddress in swap/quote requests with disabled wallet */
export const FAKE_WALLET_ADDRESS = '0xe388Ed184958062a2ea29B7fD049ca21244AE02e';
export const FAKE_SOLANA_WALLET_ADDRESS = '7cwWhuCJUHc27Dq4nQRhggwgeuVHEeS3NWv7BY6yY9Bk';
53 changes: 53 additions & 0 deletions src/features/common/providers/lifi/lifi-utils-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { BLOCKCHAIN_NAME, BlockchainName } from 'src/core/blockchain/models/blockchain-name';
import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id';
import {
FAKE_SOLANA_WALLET_ADDRESS,
FAKE_WALLET_ADDRESS
} from 'src/features/common/constants/fake-wallet-address';

import { LifiCrossChainSupportedBlockchain } from '../../../cross-chain/calculation-manager/providers/lifi-provider/constants/lifi-cross-chain-supported-blockchain';

export class LifiUtilsService {
private static readonly SOLANA_CHAIN_ID = 'SOL';

private static readonly SOLANA_NATIVE_TOKEN_ADDRESS = '11111111111111111111111111111111';

public static getLifiReceiverAddress(
fromBlockchain: LifiCrossChainSupportedBlockchain,
toBlockchain: LifiCrossChainSupportedBlockchain,
fromAddress: string,
receiverAddress: string | undefined
): string {
if (receiverAddress) {
return receiverAddress;
}

if (fromBlockchain === BLOCKCHAIN_NAME.SOLANA) {
return FAKE_WALLET_ADDRESS;
}
if (toBlockchain === BLOCKCHAIN_NAME.SOLANA) {
return FAKE_SOLANA_WALLET_ADDRESS;
}

return fromAddress;
}

public static getLifiChainId(blockchain: BlockchainName): number | string {
if (blockchain === BLOCKCHAIN_NAME.SOLANA) {
return this.SOLANA_CHAIN_ID;
}
return blockchainId[blockchain];
}

public static getLifiTokenAddress(
blockchain: BlockchainName,
isNative: boolean,
tokenAddress: string
): string {
if (blockchain === BLOCKCHAIN_NAME.SOLANA && isNative) {
return this.SOLANA_NATIVE_TOKEN_ADDRESS;
}

return tokenAddress;
}
}
17 changes: 17 additions & 0 deletions src/features/common/utils/get-solana-fee.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { PriceTokenAmount } from 'src/common/tokens';

const DEFAULT_FEE_PERCENT = 0.02;

export function getSolanaFee(from: PriceTokenAmount): number {
if (!from.price) {
return DEFAULT_FEE_PERCENT;
}

const usdTokenAmount = from.tokenAmount.multipliedBy(from.price);

if (usdTokenAmount.gt(100)) {
return DEFAULT_FEE_PERCENT;
}

return 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export const changenowApiBlockchain = {
[BLOCKCHAIN_NAME.RAVENCOIN]: 'rvn',
[BLOCKCHAIN_NAME.SIA]: 'sc',
[BLOCKCHAIN_NAME.SECRET]: 'scrt',
[BLOCKCHAIN_NAME.SOLANA]: 'sol',
// [BLOCKCHAIN_NAME.SOLANA]: 'sol',
[BLOCKCHAIN_NAME.STEEM]: 'steem',
[BLOCKCHAIN_NAME.STRATIS]: 'strax',
[BLOCKCHAIN_NAME.STACKS]: 'stx',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure';
import { DlnApiService } from 'src/features/common/providers/dln/dln-api-service';
import { DlnUtils } from 'src/features/common/providers/dln/dln-utils';
import { getFromWithoutFee } from 'src/features/common/utils/get-from-without-fee';
import { getSolanaFee } from 'src/features/common/utils/get-solana-fee';
import { RequiredCrossChainOptions } from 'src/features/cross-chain/calculation-manager/models/cross-chain-options';
import { CROSS_CHAIN_TRADE_TYPE } from 'src/features/cross-chain/calculation-manager/models/cross-chain-trade-type';
import { CrossChainProvider } from 'src/features/cross-chain/calculation-manager/providers/common/cross-chain-provider';
Expand All @@ -37,7 +38,6 @@ import {
TransactionErrorResponse
} from 'src/features/cross-chain/calculation-manager/providers/debridge-provider/models/transaction-response';
import { DeflationTokenManager } from 'src/features/deflation-token-manager/deflation-token-manager';
import { DlnOnChainSupportedBlockchain } from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/constants/dln-on-chain-supported-blockchains';
import { DlnOnChainSwapRequest } from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/models/dln-on-chain-swap-request';
import { ON_CHAIN_TRADE_TYPE } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type';

Expand Down Expand Up @@ -86,7 +86,7 @@ export class DebridgeCrossChainProvider extends CrossChainProvider {
);

const requestParams: TransactionRequest = {
...this.getAffiliateFee(fromBlockchain),
...this.getAffiliateFee(from),
srcChainId: blockchainId[fromBlockchain],
srcChainTokenIn: DlnUtils.getSupportedAddress(from),
srcChainTokenInAmount: fromWithoutFee.stringWeiAmount,
Expand Down Expand Up @@ -312,13 +312,16 @@ export class DebridgeCrossChainProvider extends CrossChainProvider {
}

private getAffiliateFee(
fromBlockchain: DlnOnChainSupportedBlockchain
from: PriceTokenAmount
): Partial<Pick<DlnOnChainSwapRequest, 'affiliateFeePercent' | 'affiliateFeeRecipient'>> {
if (fromBlockchain === BLOCKCHAIN_NAME.SOLANA) {
return {
affiliateFeeRecipient: '4juPxgyQapaKdgxuCS7N8pRxjttXGRZsS5WTVZ42rNjn',
affiliateFeePercent: 0.1
};
if (from.blockchain === BLOCKCHAIN_NAME.SOLANA) {
const feePercent = getSolanaFee(from);
if (feePercent) {
return {
affiliateFeeRecipient: '4juPxgyQapaKdgxuCS7N8pRxjttXGRZsS5WTVZ42rNjn',
affiliateFeePercent: feePercent * 100
};
}
}
return {};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import BigNumber from 'bignumber.js';
import { RubicSdkError, SwapRequestError } from 'src/common/errors';
import { PriceTokenAmount } from 'src/common/tokens';
import { Cache } from 'src/common/utils/decorators';
import { EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name';
import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name';
import { BlockchainsInfo } from 'src/core/blockchain/utils/blockchains-info/blockchains-info';
import { EvmWeb3Pure } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure';
import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config';
import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure';
Expand All @@ -29,14 +30,15 @@ import { LifiCrossChainSupportedBlockchain } from 'src/features/cross-chain/calc
import { LifiTransactionRequest } from 'src/features/cross-chain/calculation-manager/providers/lifi-provider/models/lifi-transaction-request';
import { getCrossChainGasData } from 'src/features/cross-chain/calculation-manager/utils/get-cross-chain-gas-data';

import { Estimate } from './models/lifi-fee-cost';
import { Route } from './models/lifi-route';
import { LifiApiService } from './services/lifi-api-service';
import { LifiEvmCrossChainTradeConstructor } from '../models/lifi-cross-chain-trade-constructor';
import { Estimate } from '../models/lifi-fee-cost';
import { Route } from '../models/lifi-route';
import { LifiApiService } from '../services/lifi-api-service';

/**
* Calculated Celer cross-chain trade.
*/
export class LifiCrossChainTrade extends EvmCrossChainTrade {
export class LifiEvmCrossChainTrade extends EvmCrossChainTrade {
/** @internal */
public static async getGasData(
from: PriceTokenAmount<EvmBlockchainName>,
Expand All @@ -47,7 +49,7 @@ export class LifiCrossChainTrade extends EvmCrossChainTrade {
providerAddress: string,
receiverAddress?: string
): Promise<GasData | null> {
const trade = new LifiCrossChainTrade(
const trade = new LifiEvmCrossChainTrade(
{
from,
to: toToken,
Expand Down Expand Up @@ -77,7 +79,7 @@ export class LifiCrossChainTrade extends EvmCrossChainTrade {

public readonly from: PriceTokenAmount<EvmBlockchainName>;

public readonly to: PriceTokenAmount<EvmBlockchainName>;
public readonly to: PriceTokenAmount<BlockchainName>;

public readonly toTokenAmountMin: BigNumber;

Expand Down Expand Up @@ -116,18 +118,7 @@ export class LifiCrossChainTrade extends EvmCrossChainTrade {
}

constructor(
crossChainTrade: {
from: PriceTokenAmount<EvmBlockchainName>;
to: PriceTokenAmount<EvmBlockchainName>;
route: Route;
gasData: GasData | null;
toTokenAmountMin: BigNumber;
feeInfo: FeeInfo;
priceImpact: number | null;
onChainSubtype: OnChainSubtype;
bridgeType: BridgeType;
slippage: number;
},
crossChainTrade: LifiEvmCrossChainTradeConstructor,
providerAddress: string,
routePath: RubicStep[],
useProxy: boolean
Expand Down Expand Up @@ -204,14 +195,18 @@ export class LifiCrossChainTrade extends EvmCrossChainTrade {
options?.receiverAddress
);

const isEvmDestination = BlockchainsInfo.isEvmBlockchainName(this.to.blockchain);
const receivingAsset = isEvmDestination ? this.to.address : this.from.address;

const bridgeData = ProxyCrossChainEvmTrade.getBridgeData(options, {
walletAddress: this.walletAddress,
fromTokenAmount: this.from,
toTokenAmount: this.to,
srcChainTrade: null,
providerAddress: this.providerAddress,
type: `lifi:${this.bridgeType}`,
fromAddress: this.walletAddress
fromAddress: this.walletAddress,
toAddress: receivingAsset
});

const fromWithoutFee = getFromWithoutFee(
Expand All @@ -225,7 +220,7 @@ export class LifiCrossChainTrade extends EvmCrossChainTrade {
const providerData = await ProxyCrossChainEvmTrade.getGenericProviderData(
providerRouter,
data!,
this.fromBlockchain,
this.fromBlockchain as EvmBlockchainName,
providerRouter,
extraNativeFee
);
Expand Down Expand Up @@ -286,6 +281,7 @@ export class LifiCrossChainTrade extends EvmCrossChainTrade {
step.action.toToken.symbol,
step.action.fromAmount,
step.action.fromAddress,
step.action.toAddress,
step.action.slippage
);
return {
Expand Down
Loading

0 comments on commit a5a3724

Please sign in to comment.