Create associated token accounts and wrap SOL (#59)
This commit is contained in:
parent
c64caec9d5
commit
671225aec8
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@project-serum/swap-ui",
|
"name": "@project-serum/swap-ui",
|
||||||
"version": "0.1.8",
|
"version": "0.1.9",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
"homepage": "https://github.com/project-serum/swap-ui",
|
"homepage": "https://github.com/project-serum/swap-ui",
|
||||||
|
@ -8,7 +8,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/roboto": "^4.3.0",
|
"@fontsource/roboto": "^4.3.0",
|
||||||
"@project-serum/serum": "^0.13.34",
|
"@project-serum/serum": "^0.13.34",
|
||||||
"@project-serum/swap": "^0.1.0-alpha.28",
|
"@project-serum/swap": "^0.1.0-alpha.31",
|
||||||
"@solana/spl-token": "^0.1.4"
|
"@solana/spl-token": "^0.1.4"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
"@project-serum/anchor": "^0.7.0",
|
"@project-serum/anchor": "^0.7.0",
|
||||||
"@project-serum/serum": "^0.13.34",
|
"@project-serum/serum": "^0.13.34",
|
||||||
"@project-serum/sol-wallet-adapter": "^0.2.0",
|
"@project-serum/sol-wallet-adapter": "^0.2.0",
|
||||||
"@project-serum/swap": "^0.1.0-alpha.28",
|
"@project-serum/swap": "^0.1.0-alpha.31",
|
||||||
"@solana/spl-token": "^0.1.4",
|
"@solana/spl-token": "^0.1.4",
|
||||||
"@solana/spl-token-registry": "^0.2.86",
|
"@solana/spl-token-registry": "^0.2.86",
|
||||||
"@solana/web3.js": "^1.17.0",
|
"@solana/web3.js": "^1.17.0",
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { PublicKey } from "@solana/web3.js";
|
import {
|
||||||
import { BN } from "@project-serum/anchor";
|
PublicKey,
|
||||||
|
Keypair,
|
||||||
|
Transaction,
|
||||||
|
SystemProgram,
|
||||||
|
Signer,
|
||||||
|
} from "@solana/web3.js";
|
||||||
|
import { Token, TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
||||||
|
import { BN, Provider } from "@project-serum/anchor";
|
||||||
import {
|
import {
|
||||||
makeStyles,
|
makeStyles,
|
||||||
Card,
|
Card,
|
||||||
|
@ -24,6 +31,7 @@ import { useCanSwap, useReferral } from "../context/Swap";
|
||||||
import TokenDialog from "./TokenDialog";
|
import TokenDialog from "./TokenDialog";
|
||||||
import { SettingsButton } from "./Settings";
|
import { SettingsButton } from "./Settings";
|
||||||
import { InfoLabel } from "./Info";
|
import { InfoLabel } from "./Info";
|
||||||
|
import { WRAPPED_SOL_MINT } from "../utils/pubkeys";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
card: {
|
card: {
|
||||||
|
@ -334,9 +342,11 @@ export function SwapButton() {
|
||||||
const canSwap = useCanSwap();
|
const canSwap = useCanSwap();
|
||||||
const referral = useReferral(fromMarket);
|
const referral = useReferral(fromMarket);
|
||||||
const fair = useSwapFair();
|
const fair = useSwapFair();
|
||||||
const fromWallet = useOwnedTokenAccount(fromMint);
|
let fromWallet = useOwnedTokenAccount(fromMint);
|
||||||
const toWallet = useOwnedTokenAccount(toMint);
|
let toWallet = useOwnedTokenAccount(toMint);
|
||||||
const quoteMint = useMint(fromMarket && fromMarket.quoteMintAddress);
|
const quoteMint = fromMarket && fromMarket.quoteMintAddress;
|
||||||
|
const quoteMintInfo = useMint(quoteMint);
|
||||||
|
const quoteWallet = useOwnedTokenAccount(quoteMint);
|
||||||
|
|
||||||
// Click handler.
|
// Click handler.
|
||||||
const sendSwapTransaction = async () => {
|
const sendSwapTransaction = async () => {
|
||||||
|
@ -346,16 +356,43 @@ export function SwapButton() {
|
||||||
if (!fair) {
|
if (!fair) {
|
||||||
throw new Error("Invalid fair");
|
throw new Error("Invalid fair");
|
||||||
}
|
}
|
||||||
if (!quoteMint) {
|
if (!quoteMint || !quoteMintInfo) {
|
||||||
throw new Error("Quote mint not found");
|
throw new Error("Quote mint not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// All transactions to send for the swap.
|
||||||
|
let txs: { tx: Transaction; signers: Array<Signer | undefined> }[] = [];
|
||||||
const amount = new BN(fromAmount * 10 ** fromMintInfo.decimals);
|
const amount = new BN(fromAmount * 10 ** fromMintInfo.decimals);
|
||||||
|
|
||||||
|
const isSol =
|
||||||
|
fromMint.equals(WRAPPED_SOL_MINT) || toMint.equals(WRAPPED_SOL_MINT);
|
||||||
|
const wrappedSolAccount = isSol ? Keypair.generate() : undefined;
|
||||||
|
|
||||||
|
// Wrap the SOL into an SPL token.
|
||||||
|
if (isSol) {
|
||||||
|
txs.push(
|
||||||
|
await wrapSol(
|
||||||
|
swapClient.program.provider,
|
||||||
|
wrappedSolAccount as Keypair,
|
||||||
|
fromMint,
|
||||||
|
amount
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the swap.
|
||||||
|
txs.push(
|
||||||
|
...(await (async () => {
|
||||||
|
if (!fromMarket) {
|
||||||
|
throw new Error("Market undefined");
|
||||||
|
}
|
||||||
|
|
||||||
const minExchangeRate = {
|
const minExchangeRate = {
|
||||||
rate: new BN((10 ** toMintInfo.decimals * FEE_MULTIPLIER) / fair)
|
rate: new BN((10 ** toMintInfo.decimals * FEE_MULTIPLIER) / fair)
|
||||||
.muln(100 - slippage)
|
.muln(100 - slippage)
|
||||||
.divn(100),
|
.divn(100),
|
||||||
fromDecimals: fromMintInfo.decimals,
|
fromDecimals: fromMintInfo.decimals,
|
||||||
quoteDecimals: quoteMint.decimals,
|
quoteDecimals: quoteMintInfo.decimals,
|
||||||
strict: isStrict,
|
strict: isStrict,
|
||||||
};
|
};
|
||||||
const fromOpenOrders = fromMarket
|
const fromOpenOrders = fromMarket
|
||||||
|
@ -364,23 +401,48 @@ export function SwapButton() {
|
||||||
const toOpenOrders = toMarket
|
const toOpenOrders = toMarket
|
||||||
? openOrders.get(toMarket?.address.toString())
|
? openOrders.get(toMarket?.address.toString())
|
||||||
: undefined;
|
: undefined;
|
||||||
await swapClient.swap({
|
const fromWalletAddr = fromMint.equals(WRAPPED_SOL_MINT)
|
||||||
|
? wrappedSolAccount!.publicKey
|
||||||
|
: fromWallet
|
||||||
|
? fromWallet.publicKey
|
||||||
|
: undefined;
|
||||||
|
const toWalletAddr = toMint.equals(WRAPPED_SOL_MINT)
|
||||||
|
? wrappedSolAccount!.publicKey
|
||||||
|
: toWallet
|
||||||
|
? toWallet.publicKey
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
return await swapClient.swapTxs({
|
||||||
fromMint,
|
fromMint,
|
||||||
toMint,
|
toMint,
|
||||||
fromWallet: fromWallet ? fromWallet.publicKey : undefined,
|
quoteMint,
|
||||||
toWallet: toWallet ? toWallet.publicKey : undefined,
|
|
||||||
amount,
|
amount,
|
||||||
minExchangeRate,
|
minExchangeRate,
|
||||||
referral,
|
referral,
|
||||||
// Pass in the below parameters so that the client doesn't perform
|
|
||||||
// wasteful network requests when we already have the data.
|
|
||||||
fromMarket,
|
fromMarket,
|
||||||
toMarket,
|
toMarket,
|
||||||
fromOpenOrders: fromOpenOrders ? fromOpenOrders[0].address : undefined,
|
// Automatically created if undefined.
|
||||||
|
fromOpenOrders: fromOpenOrders
|
||||||
|
? fromOpenOrders[0].address
|
||||||
|
: undefined,
|
||||||
toOpenOrders: toOpenOrders ? toOpenOrders[0].address : undefined,
|
toOpenOrders: toOpenOrders ? toOpenOrders[0].address : undefined,
|
||||||
|
fromWallet: fromWalletAddr,
|
||||||
|
toWallet: toWalletAddr,
|
||||||
|
quoteWallet: quoteWallet ? quoteWallet.publicKey : undefined,
|
||||||
// Auto close newly created open orders accounts.
|
// Auto close newly created open orders accounts.
|
||||||
close: isClosingNewAccounts,
|
close: isClosingNewAccounts,
|
||||||
});
|
});
|
||||||
|
})())
|
||||||
|
);
|
||||||
|
|
||||||
|
// Unwrap the SOL.
|
||||||
|
if (isSol) {
|
||||||
|
txs.push(
|
||||||
|
unwrapSol(swapClient.program.provider, wrappedSolAccount as Keypair)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await swapClient.program.provider.sendAll(txs);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
|
@ -393,3 +455,63 @@ export function SwapButton() {
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function wrapSol(
|
||||||
|
provider: Provider,
|
||||||
|
wrappedSolAccount: Keypair,
|
||||||
|
fromMint: PublicKey,
|
||||||
|
amount: BN
|
||||||
|
): Promise<{ tx: Transaction; signers: Array<Signer | undefined> }> {
|
||||||
|
const tx = new Transaction();
|
||||||
|
const signers = [wrappedSolAccount];
|
||||||
|
// Create new, rent exempt account.
|
||||||
|
tx.add(
|
||||||
|
SystemProgram.createAccount({
|
||||||
|
fromPubkey: provider.wallet.publicKey,
|
||||||
|
newAccountPubkey: wrappedSolAccount.publicKey,
|
||||||
|
lamports: await Token.getMinBalanceRentForExemptAccount(
|
||||||
|
provider.connection
|
||||||
|
),
|
||||||
|
space: 165,
|
||||||
|
programId: TOKEN_PROGRAM_ID,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// Transfer lamports. These will be converted to an SPL balance by the
|
||||||
|
// token program.
|
||||||
|
if (fromMint.equals(WRAPPED_SOL_MINT)) {
|
||||||
|
tx.add(
|
||||||
|
SystemProgram.transfer({
|
||||||
|
fromPubkey: provider.wallet.publicKey,
|
||||||
|
toPubkey: wrappedSolAccount.publicKey,
|
||||||
|
lamports: amount.toNumber(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Initialize the account.
|
||||||
|
tx.add(
|
||||||
|
Token.createInitAccountInstruction(
|
||||||
|
TOKEN_PROGRAM_ID,
|
||||||
|
WRAPPED_SOL_MINT,
|
||||||
|
wrappedSolAccount.publicKey,
|
||||||
|
provider.wallet.publicKey
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return { tx, signers };
|
||||||
|
}
|
||||||
|
|
||||||
|
function unwrapSol(
|
||||||
|
provider: Provider,
|
||||||
|
wrappedSolAccount: Keypair
|
||||||
|
): { tx: Transaction; signers: Array<Signer | undefined> } {
|
||||||
|
const tx = new Transaction();
|
||||||
|
tx.add(
|
||||||
|
Token.createCloseAccountInstruction(
|
||||||
|
TOKEN_PROGRAM_ID,
|
||||||
|
wrappedSolAccount.publicKey,
|
||||||
|
provider.wallet.publicKey,
|
||||||
|
provider.wallet.publicKey,
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return { tx, signers: [] };
|
||||||
|
}
|
||||||
|
|
|
@ -139,7 +139,7 @@ export function DexContextProvider(props: any) {
|
||||||
mint.publicKey.equals(m.account.baseMintAddress)
|
mint.publicKey.equals(m.account.baseMintAddress)
|
||||||
)[0];
|
)[0];
|
||||||
const quoteMintInfo = mintInfos.filter((mint) =>
|
const quoteMintInfo = mintInfos.filter((mint) =>
|
||||||
mint.publicKey.equals(m.account.baseMintAddress)
|
mint.publicKey.equals(m.account.quoteMintAddress)
|
||||||
)[0];
|
)[0];
|
||||||
assert.ok(baseMintInfo && quoteMintInfo);
|
assert.ok(baseMintInfo && quoteMintInfo);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|
|
@ -20,6 +20,7 @@ import {
|
||||||
SPL_REGISTRY_SOLLET_TAG,
|
SPL_REGISTRY_SOLLET_TAG,
|
||||||
SPL_REGISTRY_WORM_TAG,
|
SPL_REGISTRY_WORM_TAG,
|
||||||
} from "./TokenList";
|
} from "./TokenList";
|
||||||
|
import { useOwnedTokenAccount } from "../context/Token";
|
||||||
|
|
||||||
const DEFAULT_SLIPPAGE_PERCENT = 0.5;
|
const DEFAULT_SLIPPAGE_PERCENT = 0.5;
|
||||||
|
|
||||||
|
@ -180,6 +181,7 @@ export function useCanSwap(): boolean {
|
||||||
const { fromMint, toMint, fromAmount, toAmount } = useSwapContext();
|
const { fromMint, toMint, fromAmount, toAmount } = useSwapContext();
|
||||||
const { swapClient } = useDexContext();
|
const { swapClient } = useDexContext();
|
||||||
const { wormholeMap, solletMap } = useTokenListContext();
|
const { wormholeMap, solletMap } = useTokenListContext();
|
||||||
|
const fromWallet = useOwnedTokenAccount(fromMint);
|
||||||
const fair = useSwapFair();
|
const fair = useSwapFair();
|
||||||
const route = useRouteVerbose(fromMint, toMint);
|
const route = useRouteVerbose(fromMint, toMint);
|
||||||
if (route === null) {
|
if (route === null) {
|
||||||
|
@ -187,6 +189,9 @@ export function useCanSwap(): boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
// From wallet exists.
|
||||||
|
fromWallet !== undefined &&
|
||||||
|
fromWallet !== null &&
|
||||||
// Fair price is defined.
|
// Fair price is defined.
|
||||||
fair !== undefined &&
|
fair !== undefined &&
|
||||||
fair > 0 &&
|
fair > 0 &&
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { useContext, useState, useEffect } from "react";
|
import React, { useContext, useState, useEffect } from "react";
|
||||||
import * as assert from "assert";
|
import * as assert from "assert";
|
||||||
import { useAsync } from "react-async-hook";
|
import { useAsync } from "react-async-hook";
|
||||||
import { Provider } from "@project-serum/anchor";
|
import { Provider, BN } from "@project-serum/anchor";
|
||||||
import { PublicKey, Account } from "@solana/web3.js";
|
import { PublicKey, Account } from "@solana/web3.js";
|
||||||
import {
|
import {
|
||||||
MintInfo,
|
MintInfo,
|
||||||
|
@ -10,6 +10,7 @@ import {
|
||||||
TOKEN_PROGRAM_ID,
|
TOKEN_PROGRAM_ID,
|
||||||
} from "@solana/spl-token";
|
} from "@solana/spl-token";
|
||||||
import { getOwnedTokenAccounts, parseTokenAccountData } from "../utils/tokens";
|
import { getOwnedTokenAccounts, parseTokenAccountData } from "../utils/tokens";
|
||||||
|
import { WRAPPED_SOL_MINT } from "../utils/pubkeys";
|
||||||
|
|
||||||
export type TokenContext = {
|
export type TokenContext = {
|
||||||
provider: Provider;
|
provider: Provider;
|
||||||
|
@ -27,6 +28,7 @@ export function TokenContextProvider(props: any) {
|
||||||
setRefresh((r) => r + 1);
|
setRefresh((r) => r + 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Fetch SPL tokens.
|
||||||
getOwnedTokenAccounts(provider.connection, provider.wallet.publicKey).then(
|
getOwnedTokenAccounts(provider.connection, provider.wallet.publicKey).then(
|
||||||
(accs) => {
|
(accs) => {
|
||||||
if (accs) {
|
if (accs) {
|
||||||
|
@ -35,6 +37,22 @@ export function TokenContextProvider(props: any) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
// Fetch SOL balance.
|
||||||
|
provider.connection
|
||||||
|
.getAccountInfo(provider.wallet.publicKey)
|
||||||
|
.then((acc: { lamports: number }) => {
|
||||||
|
if (acc) {
|
||||||
|
_OWNED_TOKEN_ACCOUNTS_CACHE.push({
|
||||||
|
publicKey: provider.wallet.publicKey,
|
||||||
|
// @ts-ignore
|
||||||
|
account: {
|
||||||
|
amount: new BN(acc.lamports),
|
||||||
|
mint: WRAPPED_SOL_MINT,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
setRefresh((r) => r + 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
}, [provider.wallet.publicKey, provider.connection]);
|
}, [provider.wallet.publicKey, provider.connection]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -76,12 +94,32 @@ export function useOwnedTokenAccount(
|
||||||
: 0
|
: 0
|
||||||
);
|
);
|
||||||
|
|
||||||
const tokenAccount = tokenAccounts[0];
|
let tokenAccount = tokenAccounts[0];
|
||||||
|
const isSol = mint?.equals(WRAPPED_SOL_MINT);
|
||||||
|
|
||||||
// Stream updates when the balance changes.
|
// Stream updates when the balance changes.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let listener: number;
|
let listener: number;
|
||||||
if (tokenAccount) {
|
// SOL is special cased since it's not an SPL token.
|
||||||
|
if (tokenAccount && isSol) {
|
||||||
|
listener = provider.connection.onAccountChange(
|
||||||
|
provider.wallet.publicKey,
|
||||||
|
(info: { lamports: number }) => {
|
||||||
|
const token = {
|
||||||
|
amount: new BN(info.lamports),
|
||||||
|
mint: WRAPPED_SOL_MINT,
|
||||||
|
} as TokenAccount;
|
||||||
|
if (token.amount !== tokenAccount.account.amount) {
|
||||||
|
const index = _OWNED_TOKEN_ACCOUNTS_CACHE.indexOf(tokenAccount);
|
||||||
|
assert.ok(index >= 0);
|
||||||
|
_OWNED_TOKEN_ACCOUNTS_CACHE[index].account = token;
|
||||||
|
setRefresh((r) => r + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// SPL tokens.
|
||||||
|
else if (tokenAccount) {
|
||||||
listener = provider.connection.onAccountChange(
|
listener = provider.connection.onAccountChange(
|
||||||
tokenAccount.publicKey,
|
tokenAccount.publicKey,
|
||||||
(info) => {
|
(info) => {
|
||||||
|
@ -106,7 +144,7 @@ export function useOwnedTokenAccount(
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tokenAccounts.length === 0) {
|
if (!isSol && tokenAccounts.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,8 +38,7 @@ export function TokenListContextProvider(props: any) {
|
||||||
const tokens = tokenList.filter((t: TokenInfo) => {
|
const tokens = tokenList.filter((t: TokenInfo) => {
|
||||||
const isUsdxQuoted =
|
const isUsdxQuoted =
|
||||||
t.extensions?.serumV3Usdt || t.extensions?.serumV3Usdc;
|
t.extensions?.serumV3Usdt || t.extensions?.serumV3Usdc;
|
||||||
const isSol = t.address === "So11111111111111111111111111111111111111112";
|
return isUsdxQuoted;
|
||||||
return isUsdxQuoted && !isSol;
|
|
||||||
});
|
});
|
||||||
tokens.sort((a: TokenInfo, b: TokenInfo) =>
|
tokens.sort((a: TokenInfo, b: TokenInfo) =>
|
||||||
a.symbol < b.symbol ? -1 : a.symbol > b.symbol ? 1 : 0
|
a.symbol < b.symbol ? -1 : a.symbol > b.symbol ? 1 : 0
|
||||||
|
|
|
@ -16,6 +16,10 @@ export const USDT_MINT = new PublicKey(
|
||||||
"Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"
|
"Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const WRAPPED_SOL_MINT = new PublicKey(
|
||||||
|
"So11111111111111111111111111111111111111112"
|
||||||
|
);
|
||||||
|
|
||||||
export const WORM_MARKET_BASE = new PublicKey(
|
export const WORM_MARKET_BASE = new PublicKey(
|
||||||
"6a9wpsZpZGxGhFVSQBpcTNjNjytdbSA1iUw1A5KNDxPw"
|
"6a9wpsZpZGxGhFVSQBpcTNjNjytdbSA1iUw1A5KNDxPw"
|
||||||
);
|
);
|
||||||
|
|
|
@ -1654,10 +1654,10 @@
|
||||||
bs58 "^4.0.1"
|
bs58 "^4.0.1"
|
||||||
eventemitter3 "^4.0.4"
|
eventemitter3 "^4.0.4"
|
||||||
|
|
||||||
"@project-serum/swap@^0.1.0-alpha.28":
|
"@project-serum/swap@^0.1.0-alpha.31":
|
||||||
version "0.1.0-alpha.28"
|
version "0.1.0-alpha.31"
|
||||||
resolved "https://registry.yarnpkg.com/@project-serum/swap/-/swap-0.1.0-alpha.28.tgz#523bb161c2d0011f4df0317ed31b77107f72ed06"
|
resolved "https://registry.yarnpkg.com/@project-serum/swap/-/swap-0.1.0-alpha.31.tgz#0656c9959f7be18248731c6ec8025d7619d58b74"
|
||||||
integrity sha512-LJ6roKiK43JyAjjDNejXxyFurngXI+YVNdliTnedKp8MgJm6QFj2S8606xiD0Col3aXI6jq7Y6xw6CczHc1j4g==
|
integrity sha512-LiepwTqC9+1PYF+Oce2VQJmiia8Li9ysRwbvbaW5+0B4ZmcXS1rc3fqrn8Xyi36XL4NGRbsT7xZa/XoyP5S7fg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@project-serum/serum" "^0.13.34"
|
"@project-serum/serum" "^0.13.34"
|
||||||
"@solana/spl-token" "^0.1.3"
|
"@solana/spl-token" "^0.1.3"
|
||||||
|
|
Loading…
Reference in New Issue