Skip to content

Commit

Permalink
wip: adding jupiter swap
Browse files Browse the repository at this point in the history
  • Loading branch information
NickKelly1 committed Aug 28, 2024
1 parent f764970 commit da9a4c3
Show file tree
Hide file tree
Showing 32 changed files with 1,076 additions and 55 deletions.
1 change: 1 addition & 0 deletions packages/extension/src/providers/bitcoin/libs/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { toBN } from "web3-utils";
import { getAddress as getBitcoinAddress } from "../types/bitcoin-network";
import { filterOutOrdinals } from "./filter-ordinals";

/** Bitcoin API wrapper */
class API implements ProviderAPIInterface {
node: string;
networkInfo: BitcoinNetworkInfo;
Expand Down
1 change: 1 addition & 0 deletions packages/extension/src/providers/ethereum/libs/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { numberToHex, toBN } from "web3-utils";
import { ERC20TokenInfo } from "../types";
import erc20 from "./abi/erc20";

/** Ethereum API wrapper */
class API implements ProviderAPIInterface {
node: string;
web3: Web3Eth;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
import { Hardfork, Common } from "@ethereumjs/common";
import { FeeMarketEIP1559Transaction, LegacyTransaction } from "@ethereumjs/tx";

/** Represents an EVM transaction */
class Transaction {
tx: EthereumTransaction;
web3: Web3Eth;
Expand Down Expand Up @@ -55,6 +56,10 @@ class Transaction {
maxFeePerGas,
};
};

/**
* Gathers the last bits of data required to serialize a transaction eg gas price, nonce, etc
*/
async finalizeTransaction(options: TransactionOptions): Promise<{
transaction:
| FinalizedFeeMarketEthereumTransaction
Expand All @@ -77,8 +82,10 @@ class Transaction {
isFeeMarketNetwork: false,
feeHistory: {} as FeeHistoryResult,
}));
// Gets the number of transactions that they will have sent by the next pending block
const nonce = await this.web3.getTransactionCount(this.tx.from, "pending");
if (!isFeeMarketNetwork) {
// Legacy transaction
const gasPrice = await this.web3.getGasPrice();
const gasLimit =
this.tx.gasLimit ||
Expand All @@ -105,6 +112,7 @@ class Transaction {
gasLimit: legacyTx.gasLimit,
};
} else {
// Fee market transaction (post EIP1559)
const baseFeePerGas =
feeHistory.baseFeePerGas[feeHistory.baseFeePerGas.length - 2]; // -2 since -1 is the pending block
const formattedFeeHistory = formatFeeHistory(feeHistory);
Expand Down Expand Up @@ -147,6 +155,13 @@ class Transaction {
};
}
}

/**
* Create a sendable transaction
*
* Gathers last live bits of data required to send the transaction then
* creates a sendable transaction from it
*/
async getFinalizedTransaction(
options: TransactionOptions
): Promise<LegacyTransaction | FeeMarketEIP1559Transaction> {
Expand Down Expand Up @@ -175,10 +190,12 @@ class Transaction {
);
}
}

async getMessageToSign(options: TransactionOptions): Promise<Uint8Array> {
const tx = await this.getFinalizedTransaction(options);
return tx.getHashedMessageToSign();
}

async getGasCosts(): Promise<GasCosts> {
const { gasLimit, gasPrice, baseFeePerGas, formattedFeeHistory } =
await this.finalizeTransaction({
Expand Down
15 changes: 11 additions & 4 deletions packages/extension/src/providers/ethereum/ui/libs/signer.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { InternalMethods, InternalOnMessageResponse } from "@/types/messenger";
import { FeeMarketEIP1559Transaction } from "@ethereumjs/tx";
import { FeeMarketEIP1559Transaction, LegacyTransaction } from "@ethereumjs/tx";
import { SignerTransactionOptions, SignerMessageOptions } from "../types";
import HWwallet from "@enkryptcom/hw-wallets";
import { HWwalletType } from "@enkryptcom/types";
import { fromRpcSig, hashPersonalMessage } from "@ethereumjs/util";
import { getCustomError } from "@/libs/error";
import { bufferToHex } from "@enkryptcom/utils";
import sendUsingInternalMessengers from "@/libs/messenger/internal-messenger";

/**
* Sign a transaction
*/
const TransactionSigner = (
options: SignerTransactionOptions
): Promise<FeeMarketEIP1559Transaction> => {
): Promise<LegacyTransaction | FeeMarketEIP1559Transaction> => {
const { account, network, payload } = options;
if (account.isHardware) {
const hwwallets = new HWwallet();
Expand All @@ -26,7 +30,7 @@ const TransactionSigner = (
})
.then((rpcsig: string) => {
const rpcSig = fromRpcSig(rpcsig);
const signedTx = (payload as FeeMarketEIP1559Transaction).addSignature(
const signedTx = payload.addSignature(
BigInt(rpcSig.v),
rpcSig.r,
rpcSig.s,
Expand All @@ -49,7 +53,7 @@ const TransactionSigner = (
return Promise.reject(res);
} else {
const rpcSig = fromRpcSig(JSON.parse(res.result as string) || "0x");
const signedTx = (payload as FeeMarketEIP1559Transaction).addSignature(
const signedTx = payload.addSignature(
rpcSig.v,
rpcSig.r,
rpcSig.s,
Expand All @@ -61,6 +65,9 @@ const TransactionSigner = (
}
};

/**
* Sign a message
*/
const MessageSigner = (
options: SignerMessageOptions
): Promise<InternalOnMessageResponse> => {
Expand Down
1 change: 1 addition & 0 deletions packages/extension/src/providers/kadena/libs/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import { toBase } from "@enkryptcom/utils";
import DomainState from "@/libs/domain-state";

/** Kadena API wrapper */
class API implements ProviderAPIInterface {
decimals: number;
node: string;
Expand Down
2 changes: 2 additions & 0 deletions packages/extension/src/providers/polkadot/libs/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { PolkadotAPIOptions } from "../types";
import { AccountInfoWithRefCount } from "@polkadot/types/interfaces";
import { NetworkEndpoints } from "./activity-handlers/providers/subscan/configs";
import { SubscanExtrinsicInfo } from "@/types/activity";

/** Polkadot Substrate API wrapper */
class API implements ProviderAPIInterface {
node: string;
decimals: number;
Expand Down
1 change: 1 addition & 0 deletions packages/extension/src/providers/solana/libs/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { hexToBuffer, numberToHex } from "@enkryptcom/utils";
import cacheFetch from "@/libs/cache-fetch";
import { SPLTokenInfo } from "../types/sol-token";

/** Solana API wrapper */
class API implements ProviderAPIInterface {
node: string;
web3: Connection;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ export class EnkryptWallet implements Wallet {
#signMessage: SolanaSignMessageMethod = async (...inputs) => {
if (!this.#accounts?.length) throw new Error("not connected");
const outputs: SolanaSignMessageOutput[] = [];

if (inputs.length === 1) {
const { message, account } = inputs[0]!;
let isValidAccount = false;
Expand Down
13 changes: 13 additions & 0 deletions packages/extension/src/types/base-network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@ export interface BaseNetworkOptions {
customTokens?: boolean;
}

/**
* The main representation of a network in the Enkrypt extension.
*
* Instantiated from a mostly static network config.
*
* Provides some common properties of networks used throughout UI elements and functionality in the app.
*
* Intended to be subclassed by different network types; for example EVMNetwork, SolanaNetwork, PolkadotNetwork etc.
*
* Has an `api` function (property) which returns a `ProviderAPIInterface` with some basic methods to interract with the network
* for balances, transaction statuses, etc. The implementing class of `ProviderAPIInterface` is typically one-to-one with the
* the network type. For example EVM networks have their own API (which wraps node JSON RPC). Solana networks have a different API.
*/
export abstract class BaseNetwork {
public name: NetworkNames;
public name_long: string;
Expand Down
6 changes: 6 additions & 0 deletions packages/extension/src/types/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ export abstract class BackgroundProviderInterface extends EventEmitter {
abstract sendNotification(notif: string): Promise<void>;
}

/**
* Wraps basic network functionality to provide common features like balances and transaction statuses.
*
* Each network type will typically have its own implementing class. For example, for EVM networks the implementing
* class need just wrap JSON RPC calls.
*/
export abstract class ProviderAPIInterface {
abstract node: string;
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
Expand Down
42 changes: 41 additions & 1 deletion packages/extension/src/ui/action/views/swap/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ import { ProviderResponseWithStatus } from "./types";
import { GenericNameResolver, CoinType } from "@/libs/name-resolver";
import { trackSwapEvents } from "@/libs/metrics";
import { SwapEventType } from "@/libs/metrics/types";
import { Connection } from "@solana/web3.js";
type BN = ReturnType<typeof toBN>;
Expand Down Expand Up @@ -237,8 +238,19 @@ const isLooking = ref(false);
const swapMax = ref(false);
let api: Web3Eth | Connection;
switch (props.network.name) {
case NetworkNames.Solana:
api = new Connection(props.network.node);
break;
default:
// Assume EVM
api = new Web3Eth(props.network.node);
break;
}
const swap = new EnkryptSwap({
api: new Web3Eth(props.network.node),
api,
network: props.network.name as unknown as SupportedNetworkName,
walletIdentifier: WalletIdentifier.enkrypt,
evmOptions: {
Expand Down Expand Up @@ -433,22 +445,36 @@ const nativeSwapToken = computed(() => {
return undefined;
});
/**
* Choose the best deal from a list of swap providers' quotes
*
* Update the quote list and dest asset amount based
*/
const pickBestQuote = (fromAmountBN: BN, quotes: ProviderQuoteResponse[]) => {
errors.value.inputAmount = "";
// No quotes at all
if (!quotes.length) return;
const fromT = new SwapToken(fromToken.value!);
/** Users would-be balance of the source asset after the swap */
const remainingBalance =
fromT.token.address === NATIVE_TOKEN_ADDRESS
? nativeSwapToken.value!.getBalanceRaw().sub(fromAmountBN)
: nativeSwapToken.value!.getBalanceRaw();
// Drop quotes that don't fit the users desired "amount"
const filteredQuotes = quotes.filter((q) => {
return (
q.minMax.minimumFrom.lte(fromAmountBN) &&
q.minMax.maximumFrom.gte(fromAmountBN) &&
q.additionalNativeFees.lte(remainingBalance)
);
});
if (!filteredQuotes.length) {
// No quotes fit the users sap "amount"
let lowestMinimum: BN = quotes[0].minMax.minimumFrom;
let highestMaximum: BN = quotes[0].minMax.maximumFrom;
let smallestNativeFees: BN = nativeSwapToken.value!.getBalanceRaw();
Expand Down Expand Up @@ -478,17 +504,28 @@ const pickBestQuote = (fromAmountBN: BN, quotes: ProviderQuoteResponse[]) => {
}
return;
}
// There exist quotes that fit the users swap amount
if (fromT.getBalanceRaw().lt(fromAmountBN)) {
errors.value.inputAmount = "Insufficient funds";
}
// Sort remaining quotes descending by the amount of the dest asset to be received
// i.e. best deal first
filteredQuotes.sort((a, b) => (b.toTokenAmount.gt(a.toTokenAmount) ? 1 : -1));
// Apply the results
toAmount.value = new SwapToken(toToken.value!).toReadable(
filteredQuotes[0].toTokenAmount
);
bestProviderQuotes.value = filteredQuotes;
isFindingRate.value = false;
};
/**
* Request quotes from all swap providers & pick the best one
*/
const updateQuote = () => {
isFindingRate.value = true;
toAmount.value = "";
Expand Down Expand Up @@ -724,6 +761,7 @@ const sendAction = async () => {

<style lang="less" scoped>
@import "~@action/styles/theme.less";
.container {
width: 100%;
height: 600px;
Expand All @@ -733,6 +771,7 @@ const sendAction = async () => {
box-sizing: border-box;
position: relative;
}
.swap {
width: 100%;
height: 100%;
Expand Down Expand Up @@ -827,6 +866,7 @@ const sendAction = async () => {
max-height: 114px;
padding: 8px;
box-sizing: border-box;
h3 {
display: none;
}
Expand Down
Loading

0 comments on commit da9a4c3

Please sign in to comment.