diff --git a/libs/coin-modules/coin-solana/package.json b/libs/coin-modules/coin-solana/package.json index 398ad49de6bd..ccbac599475d 100644 --- a/libs/coin-modules/coin-solana/package.json +++ b/libs/coin-modules/coin-solana/package.json @@ -53,8 +53,8 @@ "@ledgerhq/logs": "workspace:^", "@ledgerhq/types-cryptoassets": "workspace:^", "@ledgerhq/types-live": "workspace:^", - "@solana/spl-token": "^0.3.7", - "@solana/web3.js": "1.77.3", + "@solana/spl-token": "0.3.9", + "@solana/web3.js": "1.91.6", "bignumber.js": "^9.1.2", "bs58": "^4.0.1", "expect": "^27.4.6", @@ -65,12 +65,12 @@ "superstruct": "0.14.2" }, "devDependencies": { + "@faker-js/faker": "^8.4.1", "@types/bs58": "^4.0.1", "@types/invariant": "^2.2.2", "@types/jest": "^29.5.10", "@types/lodash": "^4.14.191", "@types/object-hash": "^2.1.0", - "@faker-js/faker": "^8.4.1", "jest": "^29.7.0", "ts-jest": "^29.1.1" }, diff --git a/libs/coin-modules/coin-solana/src/api/cached.ts b/libs/coin-modules/coin-solana/src/api/cached.ts index bc82f12ecdc3..828471f14d2d 100644 --- a/libs/coin-modules/coin-solana/src/api/cached.ts +++ b/libs/coin-modules/coin-solana/src/api/cached.ts @@ -1,4 +1,5 @@ import { makeLRUCache, minutes, seconds } from "@ledgerhq/live-network/cache"; +import { PublicKey, TransactionInstruction, TransactionMessage } from "@solana/web3.js"; import hash from "object-hash"; import { ChainAPI } from "./chain"; @@ -8,6 +9,17 @@ const cacheKeyAssocTokenAccAddress = (owner: string, mint: string) => `${owner}: const cacheKeyMinimumBalanceForRentExemption = (dataLengt: number) => dataLengt.toString(); const cacheKeyTransactions = (signatures: string[]) => hash([...signatures].sort()); +const cacheKeyInstructions = (ixs: TransactionInstruction[], payer: PublicKey) => { + return hash( + new TransactionMessage({ + instructions: ixs, + payerKey: payer, + recentBlockhash: payer.toString(), + }) + .compileToLegacyMessage() + .serialize(), + ); +}; const cacheKeyByArgs = (...args: any[]) => hash(args); @@ -74,6 +86,18 @@ export function cached(api: ChainAPI): ChainAPI { getEpochInfo: makeLRUCache(api.getEpochInfo, cacheKeyEmpty, minutes(1)), + getRecentPrioritizationFees: makeLRUCache( + api.getRecentPrioritizationFees, + cacheKeyByArgs, + seconds(30), + ), + + getSimulationComputeUnits: makeLRUCache( + api.getSimulationComputeUnits, + cacheKeyInstructions, + seconds(30), + ), + config: api.config, }; } diff --git a/libs/coin-modules/coin-solana/src/api/chain/index.ts b/libs/coin-modules/coin-solana/src/api/chain/index.ts index fe95cde5b0d1..7ad46f725e2b 100644 --- a/libs/coin-modules/coin-solana/src/api/chain/index.ts +++ b/libs/coin-modules/coin-solana/src/api/chain/index.ts @@ -11,6 +11,11 @@ import { sendAndConfirmRawTransaction, SignaturesForAddressOptions, StakeProgram, + GetRecentPrioritizationFeesConfig, + TransactionInstruction, + ComputeBudgetProgram, + VersionedTransaction, + TransactionMessage, } from "@solana/web3.js"; import { makeLRUCache, minutes } from "@ledgerhq/live-network/cache"; import { getEnv } from "@ledgerhq/live-env"; @@ -71,6 +76,15 @@ export type ChainAPI = Readonly<{ getEpochInfo: () => ReturnType; + getRecentPrioritizationFees: ( + accounts: string[], + ) => ReturnType; + + getSimulationComputeUnits: ( + instructions: Array, + payer: PublicKey, + ) => Promise; + config: Config; }>; @@ -87,7 +101,7 @@ export function getChainAPI( logger === undefined ? undefined : (url, options, fetch) => { - logger(url, options); + logger(url.toString(), options); fetch(url, options); }; @@ -207,6 +221,39 @@ export function getChainAPI( getEpochInfo: () => connection().getEpochInfo().catch(remapErrors), + getRecentPrioritizationFees: (accounts: string[]) => { + return connection() + .getRecentPrioritizationFees({ + lockedWritableAccounts: accounts.map(acc => new PublicKey(acc)), + }) + .catch(remapErrors); + }, + + getSimulationComputeUnits: async (instructions, payer) => { + // https://solana.com/developers/guides/advanced/how-to-request-optimal-compute + const testInstructions = [ + // Set an arbitrarily high number in simulation + // so we can be sure the transaction will succeed + // and get the real compute units used + ComputeBudgetProgram.setComputeUnitLimit({ units: 1_400_000 }), + ...instructions, + ]; + const testTransaction = new VersionedTransaction( + new TransactionMessage({ + instructions: testInstructions, + payerKey: payer, + // RecentBlockhash can by any public key during simulation + // since 'replaceRecentBlockhash' is set to 'true' below + recentBlockhash: PublicKey.default.toString(), + }).compileToV0Message(), + ); + const rpcResponse = await connection().simulateTransaction(testTransaction, { + replaceRecentBlockhash: true, + sigVerify: false, + }); + return rpcResponse.value.err ? null : rpcResponse.value.unitsConsumed || null; + }, + config, }; } diff --git a/libs/coin-modules/coin-solana/src/api/chain/web3.ts b/libs/coin-modules/coin-solana/src/api/chain/web3.ts index 1e6f193ef5ea..fff6da9b0e03 100644 --- a/libs/coin-modules/coin-solana/src/api/chain/web3.ts +++ b/libs/coin-modules/coin-solana/src/api/chain/web3.ts @@ -11,8 +11,10 @@ import { StakeProgram, SystemProgram, TransactionInstruction, + ComputeBudgetProgram, } from "@solana/web3.js"; import chunk from "lodash/chunk"; +import uniq from "lodash/uniq"; import { ChainAPI } from "."; import { Awaited } from "../../logic"; import { @@ -25,7 +27,7 @@ import { TokenTransferCommand, TransferCommand, } from "../../types"; -import { drainSeqAsyncGen } from "../../utils"; +import { drainSeqAsyncGen, median } from "../../utils"; import { parseTokenAccountInfo, tryParseAsTokenAccount, tryParseAsVoteAccount } from "./account"; import { parseStakeAccountInfo } from "./account/parser"; import { StakeAccountInfo } from "./account/stake"; @@ -127,12 +129,10 @@ export function getTransactions( return drainSeqAsyncGen(getTransactionsGen(address, untilTxSignature, api)); } -export const buildTransferInstructions = ({ - sender, - recipient, - amount, - memo, -}: TransferCommand): TransactionInstruction[] => { +export const buildTransferInstructions = async ( + api: ChainAPI, + { sender, recipient, amount, memo }: TransferCommand, +): Promise => { const fromPublicKey = new PublicKey(sender); const toPublicKey = new PublicKey(recipient); @@ -153,12 +153,13 @@ export const buildTransferInstructions = ({ instructions.push(memoIx); } - return instructions; + return appendMaybePriorityFeeInstructions(api, instructions, fromPublicKey); }; -export const buildTokenTransferInstructions = ( +export const buildTokenTransferInstructions = async ( + api: ChainAPI, command: TokenTransferCommand, -): TransactionInstruction[] => { +): Promise => { const { ownerAddress, ownerAssociatedTokenAccountAddress, @@ -271,6 +272,51 @@ export async function getStakeAccountAddressWithSeed({ return pubkey.toBase58(); } +export async function getPriorityFee(api: ChainAPI, accounts: string[]): Promise { + const recentFees = await api.getRecentPrioritizationFees(uniq(accounts)); + return median(recentFees.map(item => item.prioritizationFee)); +} + +export async function appendMaybePriorityFeeInstructions( + api: ChainAPI, + ixs: TransactionInstruction[], + payer: PublicKey, +): Promise { + const instructions = [...ixs]; + const writableAccs = instructions + .map(ix => ix.keys.filter(acc => acc.isWritable).map(acc => acc.pubkey.toBase58())) + .flat(); + const priorityFeeIx = await buildMaybePriorityFeeInstruction(api, writableAccs); + if (priorityFeeIx) instructions.unshift(priorityFeeIx); + const computeUnitsIx = await buildComputeUnitInstruction(api, instructions, payer); + + if (computeUnitsIx) instructions.unshift(computeUnitsIx); + return instructions; +} + +export async function buildMaybePriorityFeeInstruction( + api: ChainAPI, + accounts: string[], +): Promise { + const priorityFee = await getPriorityFee(api, accounts); + if (priorityFee === 0) return null; + + return ComputeBudgetProgram.setComputeUnitPrice({ + microLamports: priorityFee, + }); +} +export async function buildComputeUnitInstruction( + api: ChainAPI, + ixs: TransactionInstruction[], + payer: PublicKey, +): Promise { + const computeUnits = await api.getSimulationComputeUnits(ixs, payer); + // adding 10% more CPU to make sure it will work + return computeUnits + ? ComputeBudgetProgram.setComputeUnitLimit({ units: computeUnits * 0.1 + computeUnits }) + : null; +} + export function buildCreateAssociatedTokenAccountInstruction({ mint, owner, @@ -292,86 +338,82 @@ export function buildCreateAssociatedTokenAccountInstruction({ return instructions; } -export function buildStakeDelegateInstructions({ - authorizedAccAddr, - stakeAccAddr, - voteAccAddr, -}: StakeDelegateCommand): TransactionInstruction[] { +export async function buildStakeDelegateInstructions( + api: ChainAPI, + { authorizedAccAddr, stakeAccAddr, voteAccAddr }: StakeDelegateCommand, +): Promise { + const withdrawAuthority = new PublicKey(authorizedAccAddr); + const stakeAcc = new PublicKey(stakeAccAddr); + const voteAcc = new PublicKey(voteAccAddr); const tx = StakeProgram.delegate({ - authorizedPubkey: new PublicKey(authorizedAccAddr), - stakePubkey: new PublicKey(stakeAccAddr), - votePubkey: new PublicKey(voteAccAddr), + authorizedPubkey: withdrawAuthority, + stakePubkey: stakeAcc, + votePubkey: voteAcc, }); - return tx.instructions; + return appendMaybePriorityFeeInstructions(api, tx.instructions, withdrawAuthority); } -export function buildStakeUndelegateInstructions({ - authorizedAccAddr, - stakeAccAddr, -}: StakeUndelegateCommand): TransactionInstruction[] { +export async function buildStakeUndelegateInstructions( + api: ChainAPI, + { authorizedAccAddr, stakeAccAddr }: StakeUndelegateCommand, +): Promise { + const withdrawAuthority = new PublicKey(authorizedAccAddr); + const stakeAcc = new PublicKey(stakeAccAddr); const tx = StakeProgram.deactivate({ - authorizedPubkey: new PublicKey(authorizedAccAddr), - stakePubkey: new PublicKey(stakeAccAddr), + authorizedPubkey: withdrawAuthority, + stakePubkey: stakeAcc, }); - return tx.instructions; + return appendMaybePriorityFeeInstructions(api, tx.instructions, withdrawAuthority); } -export function buildStakeWithdrawInstructions({ - authorizedAccAddr, - stakeAccAddr, - amount, - toAccAddr, -}: StakeWithdrawCommand): TransactionInstruction[] { +export async function buildStakeWithdrawInstructions( + api: ChainAPI, + { authorizedAccAddr, stakeAccAddr, amount, toAccAddr }: StakeWithdrawCommand, +): Promise { + const withdrawAuthority = new PublicKey(authorizedAccAddr); + const stakeAcc = new PublicKey(stakeAccAddr); + const recipient = new PublicKey(toAccAddr); const tx = StakeProgram.withdraw({ - authorizedPubkey: new PublicKey(authorizedAccAddr), - stakePubkey: new PublicKey(stakeAccAddr), + authorizedPubkey: withdrawAuthority, + stakePubkey: stakeAcc, lamports: amount, - toPubkey: new PublicKey(toAccAddr), + toPubkey: recipient, }); - return tx.instructions; + return appendMaybePriorityFeeInstructions(api, tx.instructions, withdrawAuthority); } -export function buildStakeSplitInstructions({ - authorizedAccAddr, - stakeAccAddr, - seed, - amount, - splitStakeAccAddr, -}: StakeSplitCommand): TransactionInstruction[] { - // HACK: switch to split_with_seed when supported by @solana/web3.js - const splitIx = StakeProgram.split({ - authorizedPubkey: new PublicKey(authorizedAccAddr), +export async function buildStakeSplitInstructions( + api: ChainAPI, + { authorizedAccAddr, stakeAccAddr, seed, amount, splitStakeAccAddr }: StakeSplitCommand, +): Promise { + const basePk = new PublicKey(authorizedAccAddr); + const stakePk = new PublicKey(stakeAccAddr); + const splitStakePk = new PublicKey(splitStakeAccAddr); + const splitIx = StakeProgram.splitWithSeed({ + authorizedPubkey: basePk, lamports: amount, - stakePubkey: new PublicKey(stakeAccAddr), - splitStakePubkey: new PublicKey(splitStakeAccAddr), - }).instructions[1]; - - if (splitIx === undefined) { - throw new Error("expected split instruction"); - } - - const allocateIx = SystemProgram.allocate({ - accountPubkey: new PublicKey(splitStakeAccAddr), - basePubkey: new PublicKey(authorizedAccAddr), - programId: StakeProgram.programId, + stakePubkey: stakePk, + splitStakePubkey: splitStakePk, + basePubkey: basePk, seed, - space: StakeProgram.space, }); - - return [allocateIx, splitIx]; + return appendMaybePriorityFeeInstructions(api, splitIx.instructions, basePk); } -export function buildStakeCreateAccountInstructions({ - fromAccAddress, - stakeAccAddress, - seed, - amount, - stakeAccRentExemptAmount, - delegate, -}: StakeCreateAccountCommand): TransactionInstruction[] { +export async function buildStakeCreateAccountInstructions( + api: ChainAPI, + { + fromAccAddress, + stakeAccAddress, + seed, + amount, + stakeAccRentExemptAmount, + delegate, + }: StakeCreateAccountCommand, +): Promise { const fromPubkey = new PublicKey(fromAccAddress); const stakePubkey = new PublicKey(stakeAccAddress); @@ -394,6 +436,5 @@ export function buildStakeCreateAccountInstructions({ votePubkey: new PublicKey(delegate.voteAccAddress), }), ); - - return tx.instructions; + return appendMaybePriorityFeeInstructions(api, tx.instructions, fromPubkey); } diff --git a/libs/coin-modules/coin-solana/src/bridge.integration.test.ts b/libs/coin-modules/coin-solana/src/bridge.integration.test.ts index 014c7765a27a..124ae2f7bdfb 100644 --- a/libs/coin-modules/coin-solana/src/bridge.integration.test.ts +++ b/libs/coin-modules/coin-solana/src/bridge.integration.test.ts @@ -952,6 +952,19 @@ const baseTx = { const baseAPI = { getLatestBlockhash: () => Promise.resolve(LATEST_BLOCKHASH_MOCK), getFeeForMessage: (_msg: unknown) => Promise.resolve(testOnChainData.fees.lamportsPerSignature), + getRecentPrioritizationFees: (_: string[]) => { + return Promise.resolve([ + { + slot: 122422797, + prioritizationFee: 0, + }, + { + slot: 122422797, + prioritizationFee: 0, + }, + ]); + }, + getSimulationComputeUnits: (_ixs: any[], _payer: any) => Promise.resolve(1000), } as ChainAPI; type StakeTestSpec = { diff --git a/libs/coin-modules/coin-solana/src/buildTransaction.ts b/libs/coin-modules/coin-solana/src/buildTransaction.ts index cb7f9b17bad8..146b594ca7fc 100644 --- a/libs/coin-modules/coin-solana/src/buildTransaction.ts +++ b/libs/coin-modules/coin-solana/src/buildTransaction.ts @@ -23,7 +23,7 @@ export const buildTransactionWithAPI = async ( transaction: Transaction, api: ChainAPI, ): Promise OnChainTransaction]> => { - const instructions = buildInstructions(transaction); + const instructions = await buildInstructions(api, transaction); const recentBlockhash = await api.getLatestBlockhash(); @@ -46,7 +46,10 @@ export const buildTransactionWithAPI = async ( ]; }; -function buildInstructions(tx: Transaction): TransactionInstruction[] { +async function buildInstructions( + api: ChainAPI, + tx: Transaction, +): Promise { const { commandDescriptor } = tx.model; if (commandDescriptor === undefined) { throw new Error("missing command descriptor"); @@ -54,27 +57,30 @@ function buildInstructions(tx: Transaction): TransactionInstruction[] { if (Object.keys(commandDescriptor.errors).length > 0) { throw new Error("can not build invalid command"); } - return buildInstructionsForCommand(commandDescriptor.command); + return buildInstructionsForCommand(api, commandDescriptor.command); } -function buildInstructionsForCommand(command: Command): TransactionInstruction[] { +async function buildInstructionsForCommand( + api: ChainAPI, + command: Command, +): Promise { switch (command.kind) { case "transfer": - return buildTransferInstructions(command); + return buildTransferInstructions(api, command); case "token.transfer": - return buildTokenTransferInstructions(command); + return buildTokenTransferInstructions(api, command); case "token.createATA": return buildCreateAssociatedTokenAccountInstruction(command); case "stake.createAccount": - return buildStakeCreateAccountInstructions(command); + return buildStakeCreateAccountInstructions(api, command); case "stake.delegate": - return buildStakeDelegateInstructions(command); + return buildStakeDelegateInstructions(api, command); case "stake.undelegate": - return buildStakeUndelegateInstructions(command); + return buildStakeUndelegateInstructions(api, command); case "stake.withdraw": - return buildStakeWithdrawInstructions(command); + return buildStakeWithdrawInstructions(api, command); case "stake.split": - return buildStakeSplitInstructions(command); + return buildStakeSplitInstructions(api, command); default: return assertUnreachable(command); } diff --git a/libs/coin-modules/coin-solana/src/synchronization.ts b/libs/coin-modules/coin-solana/src/synchronization.ts index b324c9e635bd..348956645c07 100644 --- a/libs/coin-modules/coin-solana/src/synchronization.ts +++ b/libs/coin-modules/coin-solana/src/synchronization.ts @@ -542,7 +542,7 @@ function getMainAccOperationTypeFromTx(tx: ParsedTransaction): OperationType | u const parsedIxs = instructions .map(ix => parseQuiet(ix)) - .filter(({ program }) => program !== "spl-memo"); + .filter(({ program }) => program !== "spl-memo" && program !== "unknown"); if (parsedIxs.length === 3) { const [first, second, third] = parsedIxs; @@ -636,7 +636,7 @@ function getTokenAccOperationType({ const { instructions } = tx.message; const [mainIx, ...otherIxs] = instructions .map(ix => parseQuiet(ix)) - .filter(({ program }) => program !== "spl-memo"); + .filter(({ program }) => program !== "spl-memo" && program !== "unknown"); if (mainIx !== undefined && otherIxs.length === 0) { switch (mainIx.program) { diff --git a/libs/coin-modules/coin-solana/src/tx-fees.ts b/libs/coin-modules/coin-solana/src/tx-fees.ts index 4a05372c9f44..b25bdbfb363f 100644 --- a/libs/coin-modules/coin-solana/src/tx-fees.ts +++ b/libs/coin-modules/coin-solana/src/tx-fees.ts @@ -2,9 +2,10 @@ import { ChainAPI } from "./api"; import { buildTransactionWithAPI } from "./buildTransaction"; import createTransaction from "./createTransaction"; import { Transaction, TransactionModel } from "./types"; -import { assertUnreachable } from "./utils"; +import { LEDGER_VALIDATOR, assertUnreachable } from "./utils"; import { VersionedTransaction as OnChainTransaction } from "@solana/web3.js"; import { log } from "@ledgerhq/logs"; +import { getStakeAccountAddressWithSeed } from "./api/chain/web3"; const DEFAULT_TX_FEE = 5000; @@ -13,7 +14,7 @@ export async function estimateTxFee( address: string, kind: TransactionModel["kind"], ) { - const tx = createDummyTx(address, kind); + const tx = await createDummyTx(address, kind); const [onChainTx] = await buildTransactionWithAPI(address, tx, api); let fee = await api.getFeeForMessage(onChainTx.message); @@ -76,7 +77,7 @@ const createDummyTransferTx = (address: string): Transaction => { }; }; -const createDummyStakeCreateAccountTx = (address: string): Transaction => { +const createDummyStakeCreateAccountTx = async (address: string): Promise => { return { ...createTransaction({} as any), model: { @@ -87,12 +88,12 @@ const createDummyStakeCreateAccountTx = (address: string): Transaction => { kind: "stake.createAccount", amount: 0, delegate: { - voteAccAddress: randomAddresses[0], + voteAccAddress: LEDGER_VALIDATOR.voteAccount, }, fromAccAddress: address, seed: "", - stakeAccAddress: randomAddresses[1], - stakeAccRentExemptAmount: 0, + stakeAccAddress: await getStakeAccountAddressWithSeed({ fromAddress: address, seed: "" }), + stakeAccRentExemptAmount: 2282880, }, ...commandDescriptorCommons, }, @@ -149,7 +150,7 @@ const createDummyStakeWithdrawTx = (address: string): Transaction => { amount: 0, authorizedAccAddr: address, stakeAccAddr: randomAddresses[0], - toAccAddr: randomAddresses[1], + toAccAddr: address, }, ...commandDescriptorCommons, }, diff --git a/libs/coin-modules/coin-solana/src/utils.ts b/libs/coin-modules/coin-solana/src/utils.ts index 5e8fd41f437e..15b6f00492af 100644 --- a/libs/coin-modules/coin-solana/src/utils.ts +++ b/libs/coin-modules/coin-solana/src/utils.ts @@ -2,6 +2,7 @@ import { Cluster, clusterApiUrl } from "@solana/web3.js"; import { partition } from "lodash/fp"; import { getEnv } from "@ledgerhq/live-env"; import { ValidatorsAppValidator } from "./validator-app"; +import BigNumber from "bignumber.js"; // Hardcoding the Ledger validator info as backup, // because backend is flaky and sometimes doesn't return it anymore @@ -174,3 +175,17 @@ export const tupleOfUnion = export function sweetch(caze: T, cases: Record): R { return cases[caze]; } + +export function median(values: number[]): number { + const length = values.length; + if (!length) return 0; + + const sorted = values.sort((a, b) => a - b); + const middle = Math.floor(length / 2); + return length % 2 + ? BigNumber(sorted[middle]) + .plus(sorted[middle - 1]) + .div(2) + .toNumber() + : sorted[middle]; +} diff --git a/libs/ledger-live-common/package.json b/libs/ledger-live-common/package.json index fb35cf7d9dbb..9eb0f102cdca 100644 --- a/libs/ledger-live-common/package.json +++ b/libs/ledger-live-common/package.json @@ -253,7 +253,7 @@ "@ledgerhq/types-cryptoassets": "workspace:^", "@ledgerhq/types-devices": "workspace:^", "@ledgerhq/types-live": "workspace:^", - "@solana/web3.js": "1.77.3", + "@solana/web3.js": "1.91.6", "@svgr/core": "^5.5.0", "@tanstack/react-query": "^5.28.9", "@testing-library/react": "^14.1.2", diff --git a/libs/ledger-live-common/src/families/solana/bridge/mock-data.ts b/libs/ledger-live-common/src/families/solana/bridge/mock-data.ts index b21218e84d8b..900af12183f0 100644 --- a/libs/ledger-live-common/src/families/solana/bridge/mock-data.ts +++ b/libs/ledger-live-common/src/families/solana/bridge/mock-data.ts @@ -889,4 +889,20 @@ export const getMockedMethods = (): { }, // manual { method: "getLatestBlockhash", params: [], answer: LATEST_BLOCKHASH_MOCK }, + { + method: "getRecentPrioritizationFees", + params: [ + ["AQbkEagmPgmsdAfS4X8V8UyJnXXjVPMvjeD15etqQ3Jh"] + ], + answer: [[ + { + slot: 122422797, + prioritizationFee: 0, + }, + { + slot: 122422797, + prioritizationFee: 0, + }, + ]], + }, ]; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b502993c8961..b556279293e1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2643,11 +2643,11 @@ importers: specifier: workspace:^ version: link:../../ledgerjs/packages/types-live '@solana/spl-token': - specifier: ^0.3.7 - version: 0.3.8(@solana/web3.js@1.77.3) + specifier: 0.3.9 + version: 0.3.9(@solana/web3.js@1.91.6) '@solana/web3.js': - specifier: 1.77.3 - version: 1.77.3 + specifier: 1.91.6 + version: 1.91.6 bignumber.js: specifier: ^9.1.2 version: 9.1.2 @@ -3793,8 +3793,8 @@ importers: specifier: workspace:^ version: link:../ledgerjs/packages/types-live '@solana/web3.js': - specifier: 1.77.3 - version: 1.77.3 + specifier: 1.91.6 + version: 1.91.6 '@svgr/core': specifier: ^5.5.0 version: 5.5.0 @@ -7736,7 +7736,7 @@ packages: '@azure/core-http@3.0.4': resolution: {integrity: sha512-Fok9VVhMdxAFOtqiiAtg74fL0UJkt0z3D+ouUUxcRLzZNBioPRAMJFVxiWoJljYpXsRi4GDQHzQHDc9AiYaIUQ==} engines: {node: '>=14.0.0'} - deprecated: deprecating as we migrated to core v2 + deprecated: This package is no longer supported. Please migrate to use @azure/core-rest-pipeline '@azure/core-lro@2.7.1': resolution: {integrity: sha512-kXSlrNHOCTVZMxpXNRqzgh9/j4cnNXU5Hf2YjMyjddRhCXFiFRzmNaqwN+XO9rGTsCOIaaG7M67zZdyliXZG9g==} @@ -10744,7 +10744,7 @@ packages: resolution: {integrity: sha512-zQH//gdOmuu7nt2oJR29vFhDv88oGPmVw6BggmrHeMI+xgEkp1B2dX9/bMBSYtK0dyLX/aOmesKS09g222K1/g==} peerDependencies: '@types/react': '*' - react: '>=16' + react: '>=18.2.0' peerDependenciesMeta: '@types/react': optional: true @@ -11408,6 +11408,7 @@ packages: '@playwright/test@1.40.1': resolution: {integrity: sha512-EaaawMTOeEItCRvfmkI9v6rBkF1svM8wjl/YPRrg2N2Wmp+4qJYkWtJsbew1szfKKDm6fPLy4YAanBhIlf9dWw==} engines: {node: '>=16'} + deprecated: Please update to the latest version of Playwright to test up-to-date browsers. hasBin: true '@playwright/test@1.45.0': @@ -11757,7 +11758,7 @@ packages: resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==} peerDependencies: '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 + react: '>=18.2.0' peerDependenciesMeta: '@types/react': optional: true @@ -11793,8 +11794,8 @@ packages: peerDependencies: '@types/react': '*' '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + react: '>=18.2.0' + react-dom: '>=18.2.0' peerDependenciesMeta: '@types/react': optional: true @@ -13112,14 +13113,14 @@ packages: resolution: {integrity: sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==} engines: {node: '>=5.10'} - '@solana/spl-token@0.3.8': - resolution: {integrity: sha512-ogwGDcunP9Lkj+9CODOWMiVJEdRtqHAtX2rWF62KxnnSWtMZtV9rDhTrZFshiyJmxDnRL/1nKE1yJHg4jjs3gg==} + '@solana/spl-token@0.3.9': + resolution: {integrity: sha512-1EXHxKICMnab35MvvY/5DBc/K/uQAOJCYnDZXw83McCAYUAfi+rwq6qfd6MmITmSTEhcfBcl/zYxmW/OSN0RmA==} engines: {node: '>=16'} peerDependencies: '@solana/web3.js': ^1.47.4 - '@solana/web3.js@1.77.3': - resolution: {integrity: sha512-PHaO0BdoiQRPpieC1p31wJsBaxwIOWLh8j2ocXNKX8boCQVldt26Jqm2tZE4KlrvnCIV78owPLv1pEUgqhxZ3w==} + '@solana/web3.js@1.91.6': + resolution: {integrity: sha512-dm20nN6HQvXToo+kM51nxHdtaa2wMSRdeK37p+WIWESfeiVHqV8XbV4XnWupq6ngt5vIckhGFG7ZnTBxUgLzDA==} '@stablelib/binary@1.0.1': resolution: {integrity: sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==} @@ -13799,7 +13800,7 @@ packages: metro-react-native-babel-preset: '*' react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - webpack: '*' + webpack: '5' peerDependenciesMeta: react: optional: true @@ -16111,7 +16112,7 @@ packages: resolution: {integrity: sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==} engines: {node: '>=6'} peerDependencies: - rxjs: '*' + rxjs: ^5.5.10 zenObservable: '*' peerDependenciesMeta: rxjs: @@ -19316,7 +19317,7 @@ packages: peerDependencies: '@typescript-eslint/eslint-plugin': ^4.0.0 || ^5.0.0 eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - jest: '*' + jest: ^27.0.0 peerDependenciesMeta: '@typescript-eslint/eslint-plugin': optional: true @@ -20076,8 +20077,8 @@ packages: hasBin: true peerDependencies: expo-modules-core: '*' - react: '*' - react-native: '*' + react: 18.2.0 + react-native: 0.72.10 peerDependenciesMeta: expo-modules-core: optional: true @@ -20092,8 +20093,8 @@ packages: peerDependencies: expo-modules-autolinking: '*' expo-modules-core: '*' - react: '*' - react-native: '*' + react: 18.2.0 + react-native: 0.73.6 peerDependenciesMeta: expo-modules-autolinking: optional: true @@ -25879,6 +25880,7 @@ packages: engines: {node: '>=0.6.0', teleport: '>=0.2.0'} deprecated: |- You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other. + (For a CapTP with native promises, see @endo/eventual-send and @endo/captp) qrcode-terminal@0.11.0: @@ -26117,7 +26119,7 @@ packages: resolution: {integrity: sha512-kBGxI+MIZGBf4wZhNCWwHkMcVP+kbpmrLWH/SkO0qCKc7D7eSPcxQbfpsmsCo8v2KCBYjuGSou+xTqK44D/jMg==} engines: {npm: ^3.0.0} peerDependencies: - prop-types: '*' + prop-types: ^15.6.1 react: '>=15.0.0' peerDependenciesMeta: prop-types: @@ -26144,8 +26146,8 @@ packages: react-native-animatable@1.3.3: resolution: {integrity: sha512-2ckIxZQAsvWn25Ho+DK3d1mXIgj7tITkrS4pYDvx96WyOttSvzzFeQnM2od0+FUMzILbdHDsDEqZvnz1DYNQ1w==} peerDependencies: - react: '*' - react-native: '*' + react: 16.9.0 + react-native: 0.61.2 peerDependenciesMeta: react: optional: true @@ -26155,8 +26157,8 @@ packages: react-native-animatable@1.4.0: resolution: {integrity: sha512-DZwaDVWm2NBvBxf7I0wXKXLKb/TxDnkV53sWhCvei1pRyTX3MVFpkvdYBknNBqPrxYuAIlPxEp7gJOidIauUkw==} peerDependencies: - react: '*' - react-native: '*' + react: 18.2.0 + react-native: 0.72.6 peerDependenciesMeta: react: optional: true @@ -34705,7 +34707,7 @@ snapshots: '@ethersproject/transactions': 5.7.0 '@ethersproject/web': 5.7.1 bech32: 1.1.4 - ws: 7.4.6 + ws: 8.18.0 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -40963,7 +40965,7 @@ snapshots: '@solana/buffer-layout-utils@0.2.0': dependencies: '@solana/buffer-layout': 4.0.1 - '@solana/web3.js': 1.77.3 + '@solana/web3.js': 1.91.6 bigint-buffer: 1.1.5 bignumber.js: 9.1.2 transitivePeerDependencies: @@ -40975,18 +40977,18 @@ snapshots: dependencies: buffer: 6.0.3(patch_hash=2xnca52oxhztvr7iaoovwclcze) - '@solana/spl-token@0.3.8(@solana/web3.js@1.77.3)': + '@solana/spl-token@0.3.9(@solana/web3.js@1.91.6)': dependencies: '@solana/buffer-layout': 4.0.1 '@solana/buffer-layout-utils': 0.2.0 - '@solana/web3.js': 1.77.3 + '@solana/web3.js': 1.91.6 buffer: 6.0.3(patch_hash=2xnca52oxhztvr7iaoovwclcze) transitivePeerDependencies: - bufferutil - encoding - utf-8-validate - '@solana/web3.js@1.77.3': + '@solana/web3.js@1.91.6': dependencies: '@babel/runtime': 7.24.1 '@noble/curves': 1.4.0 @@ -47162,7 +47164,7 @@ snapshots: bs58check@2.1.2: dependencies: - bs58: 4.0.1 + bs58: 5.0.0 create-hash: 1.2.0 safe-buffer: 5.2.1 @@ -50042,8 +50044,8 @@ snapshots: '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.2.2) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.8.0(eslint@8.57.0) eslint-plugin-react: 7.34.1(eslint@8.57.0) eslint-plugin-react-hooks: 4.6.0(eslint@8.57.0) @@ -50100,23 +50102,6 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0): - dependencies: - debug: 4.3.4 - enhanced-resolve: 5.16.0 - eslint: 8.57.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) - fast-glob: 3.3.2 - get-tsconfig: 4.7.5 - is-core-module: 2.13.1 - is-glob: 4.0.3 - transitivePeerDependencies: - - '@typescript-eslint/parser' - - eslint-import-resolver-node - - eslint-import-resolver-webpack - - supports-color - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0): dependencies: debug: 4.3.4 @@ -50161,17 +50146,6 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0): - dependencies: - debug: 3.2.7 - optionalDependencies: - '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.2.2) - eslint: 8.57.0 - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) - transitivePeerDependencies: - - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 3.2.7 @@ -50261,33 +50235,6 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): - dependencies: - array-includes: 3.1.8 - array.prototype.findlastindex: 1.2.5 - array.prototype.flat: 1.3.2 - array.prototype.flatmap: 1.3.2 - debug: 3.2.7 - doctrine: 2.1.0 - eslint: 8.57.0 - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) - hasown: 2.0.2 - is-core-module: 2.13.1 - is-glob: 4.0.3 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - object.groupby: 1.0.3 - object.values: 1.2.0 - semver: 6.3.1 - tsconfig-paths: 3.15.0 - optionalDependencies: - '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.2.2) - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): dependencies: array-includes: 3.1.8 @@ -51104,12 +51051,12 @@ snapshots: react: 18.2.0 react-native: 0.73.6(@babel/core@7.24.3)(react@18.2.0) - expo-font@11.4.0(67hrxnryf4ycl5nhvln5laaykq): + expo-font@11.4.0(hxmq25pkdf6fvmayb5kzqynb3q): dependencies: expo: 49.0.23(@babel/core@7.24.3)(@expo/metro-config@0.10.7)(expo-modules-core@1.5.11)(glob@7.2.3)(metro-core@0.80.8)(metro@0.80.8)(minimatch@5.1.6)(react-native@0.73.6(@babel/core@7.24.3)(@babel/preset-env@7.24.3(@babel/core@7.24.3))(metro-resolver@0.80.8)(metro-transform-worker@0.80.8)(react@18.2.0))(react@18.2.0) fontfaceobserver: 2.3.0 optionalDependencies: - expo-asset: 8.10.1(tw2cl75ufjodqrrf2cewclqsqi) + expo-asset: 8.10.1(expo-constants@14.5.1(expo-modules-core@1.5.11)(expo@49.0.23)(react-native@0.73.6(@babel/core@7.24.3)(@babel/preset-env@7.24.3(@babel/core@7.24.3))(metro-resolver@0.80.8)(metro-transform-worker@0.80.8)(react@18.2.0))(react@18.2.0))(expo-modules-core@1.5.11(expo-constants@14.5.1)(react-native@0.73.6(@babel/core@7.24.3)(@babel/preset-env@7.24.3(@babel/core@7.24.3))(metro-resolver@0.80.8)(metro-transform-worker@0.80.8)(react@18.2.0))(react@18.2.0))(expo@49.0.23(@babel/core@7.24.3)(@expo/metro-config@0.10.7)(expo-modules-core@1.5.11)(glob@7.2.3)(metro-core@0.80.8)(metro@0.80.8)(minimatch@5.1.6)(react-native@0.73.6(@babel/core@7.24.3)(@babel/preset-env@7.24.3(@babel/core@7.24.3))(metro-resolver@0.80.8)(metro-transform-worker@0.80.8)(react@18.2.0))(react@18.2.0))(react-native@0.73.6(@babel/core@7.24.3)(@babel/preset-env@7.24.3(@babel/core@7.24.3))(metro-resolver@0.80.8)(metro-transform-worker@0.80.8)(react@18.2.0))(react@18.2.0) expo-constants: 14.4.2(expo-modules-core@1.5.11(expo-constants@14.5.1)(react-native@0.73.6(@babel/core@7.24.3)(@babel/preset-env@7.24.3(@babel/core@7.24.3))(metro-resolver@0.80.8)(metro-transform-worker@0.80.8)(react@18.2.0))(react@18.2.0))(expo@49.0.23(@babel/core@7.24.3)(@expo/metro-config@0.10.7)(expo-modules-core@1.5.11)(glob@7.2.3)(metro-core@0.80.8)(metro@0.80.8)(minimatch@5.1.6)(react-native@0.73.6(@babel/core@7.24.3)(@babel/preset-env@7.24.3(@babel/core@7.24.3))(metro-resolver@0.80.8)(metro-transform-worker@0.80.8)(react@18.2.0))(react@18.2.0))(react-native@0.73.6(@babel/core@7.24.3)(@babel/preset-env@7.24.3(@babel/core@7.24.3))(metro-resolver@0.80.8)(metro-transform-worker@0.80.8)(react@18.2.0))(react@18.2.0) expo-modules-core: 1.5.11(expo-constants@14.5.1)(react-native@0.73.6(@babel/core@7.24.3)(@babel/preset-env@7.24.3(@babel/core@7.24.3))(metro-resolver@0.80.8)(metro-transform-worker@0.80.8)(react@18.2.0))(react@18.2.0) react: 18.2.0 @@ -51234,7 +51181,7 @@ snapshots: expo-asset: 8.10.1(tw2cl75ufjodqrrf2cewclqsqi) expo-constants: 14.4.2(expo-modules-core@1.5.11(expo-constants@14.5.1)(react-native@0.73.6(@babel/core@7.24.3)(@babel/preset-env@7.24.3(@babel/core@7.24.3))(metro-resolver@0.80.8)(metro-transform-worker@0.80.8)(react@18.2.0))(react@18.2.0))(expo@49.0.23(@babel/core@7.24.3)(@expo/metro-config@0.10.7)(expo-modules-core@1.5.11)(glob@7.2.3)(metro-core@0.80.8)(metro@0.80.8)(minimatch@5.1.6)(react-native@0.73.6(@babel/core@7.24.3)(@babel/preset-env@7.24.3(@babel/core@7.24.3))(metro-resolver@0.80.8)(metro-transform-worker@0.80.8)(react@18.2.0))(react@18.2.0))(react-native@0.73.6(@babel/core@7.24.3)(@babel/preset-env@7.24.3(@babel/core@7.24.3))(metro-resolver@0.80.8)(metro-transform-worker@0.80.8)(react@18.2.0))(react@18.2.0) expo-file-system: 15.4.5(tw2cl75ufjodqrrf2cewclqsqi) - expo-font: 11.4.0(67hrxnryf4ycl5nhvln5laaykq) + expo-font: 11.4.0(hxmq25pkdf6fvmayb5kzqynb3q) expo-keep-awake: 12.3.0(tw2cl75ufjodqrrf2cewclqsqi) expo-modules-autolinking: 1.5.1(55fu4l7dolnnxrys6pt2pnhfne) fbemitter: 3.0.0