From ade7652a4842307cd0b1bdda597ce94f46bda767 Mon Sep 17 00:00:00 2001 From: kaladinlight <35275952+kaladinlight@users.noreply.github.com> Date: Thu, 10 Oct 2024 11:57:34 -0600 Subject: [PATCH 1/4] feat: solana standard tx parsing --- packages/unchained-client/openapitools.json | 13 ++ packages/unchained-client/src/index.ts | 1 + packages/unchained-client/src/solana/index.ts | 5 + .../parser/__tests__/mockData/solSelfSend.ts | 72 ++++++++++ .../parser/__tests__/mockData/solStandard.ts | 60 +++++++++ .../solana/parser/__tests__/solana.test.ts | 126 ++++++++++++++++++ .../src/solana/parser/index.ts | 98 ++++++++++++++ .../src/solana/parser/types.ts | 14 ++ 8 files changed, 389 insertions(+) create mode 100644 packages/unchained-client/src/solana/index.ts create mode 100644 packages/unchained-client/src/solana/parser/__tests__/mockData/solSelfSend.ts create mode 100644 packages/unchained-client/src/solana/parser/__tests__/mockData/solStandard.ts create mode 100644 packages/unchained-client/src/solana/parser/__tests__/solana.test.ts create mode 100644 packages/unchained-client/src/solana/parser/index.ts create mode 100644 packages/unchained-client/src/solana/parser/types.ts diff --git a/packages/unchained-client/openapitools.json b/packages/unchained-client/openapitools.json index 3e66633fe22..a8062c8f840 100644 --- a/packages/unchained-client/openapitools.json +++ b/packages/unchained-client/openapitools.json @@ -187,6 +187,19 @@ "useSingleRequestParameter": true } }, + "solana": { + "inputSpec": "https://raw.githubusercontent.com/shapeshift/unchained/develop/node/coinstacks/solana/api/src/swagger.json", + "generatorName": "typescript-fetch", + "output": "#{cwd}/src/generated/solana", + "enablePostProcessFile": true, + "reservedWordsMappings": { + "in": "in" + }, + "additionalProperties": { + "supportsES6": "true", + "useSingleRequestParameter": true + } + }, "thorchain": { "inputSpec": "https://raw.githubusercontent.com/shapeshift/unchained/develop/go/coinstacks/thorchain/api/swagger.json", "generatorName": "typescript-fetch", diff --git a/packages/unchained-client/src/index.ts b/packages/unchained-client/src/index.ts index 7ea558079ae..185b08a6e48 100644 --- a/packages/unchained-client/src/index.ts +++ b/packages/unchained-client/src/index.ts @@ -5,6 +5,7 @@ export * as ws from './websocket' export * as evm from './evm' export * as utxo from './utxo' export * as cosmossdk from './cosmossdk' +export * as solana from './solana' export * as ethereum from './evm/ethereum' export * as avalanche from './evm/avalanche' diff --git a/packages/unchained-client/src/solana/index.ts b/packages/unchained-client/src/solana/index.ts new file mode 100644 index 00000000000..f498d679d1c --- /dev/null +++ b/packages/unchained-client/src/solana/index.ts @@ -0,0 +1,5 @@ +import type { V1Api } from '../generated/solana' + +export type Api = V1Api + +export * from './parser' diff --git a/packages/unchained-client/src/solana/parser/__tests__/mockData/solSelfSend.ts b/packages/unchained-client/src/solana/parser/__tests__/mockData/solSelfSend.ts new file mode 100644 index 00000000000..53da088c75f --- /dev/null +++ b/packages/unchained-client/src/solana/parser/__tests__/mockData/solSelfSend.ts @@ -0,0 +1,72 @@ +import type { Tx } from '../../..' + +const tx: Tx = { + txid: '3owXWn8Em7FE7Dyao3kPLkTPySiGGSSo9e7VGiWDifk6GfQRrm2JYHdHStBzVRr6b6o1PztbGpuDsXb8o2yPxoV3', + blockHeight: 293321352, + description: + 'DsYwEVzeSNMkU5PVwjwtZ8EDRQxaR6paXfFAdhMQxmaV transferred 0.000000001 SOL to DsYwEVzeSNMkU5PVwjwtZ8EDRQxaR6paXfFAdhMQxmaV.', + type: 'TRANSFER', + source: 'SYSTEM_PROGRAM', + fee: 25000, + feePayer: 'DsYwEVzeSNMkU5PVwjwtZ8EDRQxaR6paXfFAdhMQxmaV', + signature: + '3owXWn8Em7FE7Dyao3kPLkTPySiGGSSo9e7VGiWDifk6GfQRrm2JYHdHStBzVRr6b6o1PztbGpuDsXb8o2yPxoV3', + slot: 293321352, + timestamp: 1727896282, + tokenTransfers: [], + nativeTransfers: [ + { + fromUserAccount: 'DsYwEVzeSNMkU5PVwjwtZ8EDRQxaR6paXfFAdhMQxmaV', + toUserAccount: 'DsYwEVzeSNMkU5PVwjwtZ8EDRQxaR6paXfFAdhMQxmaV', + amount: 1, + }, + ], + accountData: [ + { + account: 'DsYwEVzeSNMkU5PVwjwtZ8EDRQxaR6paXfFAdhMQxmaV', + nativeBalanceChange: -25000, + tokenBalanceChanges: [], + }, + { + account: 'ComputeBudget111111111111111111111111111111', + nativeBalanceChange: 0, + tokenBalanceChanges: [], + }, + { + account: '11111111111111111111111111111111', + nativeBalanceChange: 0, + tokenBalanceChanges: [], + }, + ], + transactionError: null, + instructions: [ + { + accounts: [], + data: '3gJqkocMWaMm', + programId: 'ComputeBudget111111111111111111111111111111', + innerInstructions: [], + }, + { + accounts: [], + data: 'Fj2Eoy', + programId: 'ComputeBudget111111111111111111111111111111', + innerInstructions: [], + }, + { + accounts: [ + 'DsYwEVzeSNMkU5PVwjwtZ8EDRQxaR6paXfFAdhMQxmaV', + 'DsYwEVzeSNMkU5PVwjwtZ8EDRQxaR6paXfFAdhMQxmaV', + ], + data: '3Bxs412MvVNQj175', + programId: '11111111111111111111111111111111', + innerInstructions: [], + }, + ], + events: { + compressed: null, + nft: null, + swap: null, + }, +} + +export default { tx } diff --git a/packages/unchained-client/src/solana/parser/__tests__/mockData/solStandard.ts b/packages/unchained-client/src/solana/parser/__tests__/mockData/solStandard.ts new file mode 100644 index 00000000000..6a2c59109a2 --- /dev/null +++ b/packages/unchained-client/src/solana/parser/__tests__/mockData/solStandard.ts @@ -0,0 +1,60 @@ +import type { Tx } from '../../..' + +const tx: Tx = { + txid: 'qN3jbqvw2ypfmTVJuUiohgLQgV4mq8oZ6QzuKhNeM8MX1bdAxCK7EoXJbvBUD61mhGmrFr1KQi5FqgcadfYi7CS', + blockHeight: 294850279, + description: + 'B1fnGVnz6Q2eZPXG1FPa8wix88yyNApwGhJTURHPh4qW transferred 0.010000388 SOL to DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL.', + type: 'TRANSFER', + source: 'SYSTEM_PROGRAM', + fee: 5000, + feePayer: 'B1fnGVnz6Q2eZPXG1FPa8wix88yyNApwGhJTURHPh4qW', + signature: + 'qN3jbqvw2ypfmTVJuUiohgLQgV4mq8oZ6QzuKhNeM8MX1bdAxCK7EoXJbvBUD61mhGmrFr1KQi5FqgcadfYi7CS', + slot: 294850279, + timestamp: 1728580091, + tokenTransfers: [], + nativeTransfers: [ + { + fromUserAccount: 'B1fnGVnz6Q2eZPXG1FPa8wix88yyNApwGhJTURHPh4qW', + toUserAccount: 'DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL', + amount: 10000388, + }, + ], + accountData: [ + { + account: 'B1fnGVnz6Q2eZPXG1FPa8wix88yyNApwGhJTURHPh4qW', + nativeBalanceChange: -10005388, + tokenBalanceChanges: [], + }, + { + account: 'DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL', + nativeBalanceChange: 10000388, + tokenBalanceChanges: [], + }, + { + account: '11111111111111111111111111111111', + nativeBalanceChange: 0, + tokenBalanceChanges: [], + }, + ], + transactionError: null, + instructions: [ + { + accounts: [ + 'B1fnGVnz6Q2eZPXG1FPa8wix88yyNApwGhJTURHPh4qW', + 'DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL', + ], + data: '3Bxs41dFLGCCYtUF', + programId: '11111111111111111111111111111111', + innerInstructions: [], + }, + ], + events: { + compressed: null, + nft: null, + swap: null, + }, +} + +export default { tx } diff --git a/packages/unchained-client/src/solana/parser/__tests__/solana.test.ts b/packages/unchained-client/src/solana/parser/__tests__/solana.test.ts new file mode 100644 index 00000000000..d6a5eb71511 --- /dev/null +++ b/packages/unchained-client/src/solana/parser/__tests__/solana.test.ts @@ -0,0 +1,126 @@ +import { solanaChainId, solAssetId } from '@shapeshiftoss/caip' +import { beforeAll, describe, expect, it, vi } from 'vitest' + +import { TransferType, TxStatus } from '../../../types' +import type { ParsedTx } from '../../parser' +import { TransactionParser } from '../index' +import solSelfSend from './mockData/solSelfSend' +import solStandard from './mockData/solStandard' + +const txParser = new TransactionParser({ assetId: solAssetId, chainId: solanaChainId }) + +describe('parseTx', () => { + beforeAll(() => { + vi.clearAllMocks() + }) + + describe('standard', () => { + it('should be able to parse sol send', async () => { + const { tx } = solStandard + const address = 'B1fnGVnz6Q2eZPXG1FPa8wix88yyNApwGhJTURHPh4qW' + + const expected: ParsedTx = { + address, + blockHash: tx.blockHash, + blockHeight: tx.blockHeight, + blockTime: tx.timestamp, + chainId: solanaChainId, + confirmations: 1, + fee: { + assetId: solAssetId, + value: '5000', + }, + status: TxStatus.Confirmed, + transfers: [ + { + assetId: solAssetId, + components: [{ value: '10000388' }], + from: address, + to: 'DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL', + totalValue: '10000388', + type: TransferType.Send, + }, + ], + txid: tx.txid, + } + + const actual = await txParser.parse(tx, address) + + expect(actual).toEqual(expected) + }) + + it('should be able to parse sol receive', async () => { + const { tx } = solStandard + const address = 'DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL' + + const expected: ParsedTx = { + address, + blockHash: tx.blockHash, + blockHeight: tx.blockHeight, + blockTime: tx.timestamp, + chainId: solanaChainId, + confirmations: 1, + status: TxStatus.Confirmed, + transfers: [ + { + assetId: solAssetId, + components: [{ value: '10000388' }], + from: 'B1fnGVnz6Q2eZPXG1FPa8wix88yyNApwGhJTURHPh4qW', + to: address, + totalValue: '10000388', + type: TransferType.Receive, + }, + ], + txid: tx.txid, + } + + const actual = await txParser.parse(tx, address) + + expect(actual).toEqual(expected) + }) + }) + + describe('self send', () => { + it('should be able to parse sol', async () => { + const { tx } = solSelfSend + const address = 'DsYwEVzeSNMkU5PVwjwtZ8EDRQxaR6paXfFAdhMQxmaV' + + const expected: ParsedTx = { + txid: tx.txid, + blockHash: tx.blockHash, + blockHeight: tx.blockHeight, + blockTime: tx.timestamp, + address, + chainId: solanaChainId, + confirmations: 1, + status: TxStatus.Confirmed, + fee: { + value: '25000', + assetId: solAssetId, + }, + transfers: [ + { + type: TransferType.Send, + from: address, + to: address, + assetId: solAssetId, + totalValue: '1', + components: [{ value: '1' }], + }, + { + type: TransferType.Receive, + from: address, + to: address, + assetId: solAssetId, + totalValue: '1', + components: [{ value: '1' }], + }, + ], + } + + const actual = await txParser.parse(tx, address) + + expect(actual).toEqual(expected) + }) + }) +}) diff --git a/packages/unchained-client/src/solana/parser/index.ts b/packages/unchained-client/src/solana/parser/index.ts new file mode 100644 index 00000000000..fa22d8e1dec --- /dev/null +++ b/packages/unchained-client/src/solana/parser/index.ts @@ -0,0 +1,98 @@ +import type { AssetId, ChainId } from '@shapeshiftoss/caip' + +import { TransferType, TxStatus } from '../../types' +import { aggregateTransfer } from '../../utils' +import type { ParsedTx, SubParser, Tx } from './types' + +export * from './types' + +export interface TransactionParserArgs { + chainId: ChainId + assetId: AssetId +} + +export class TransactionParser { + chainId: ChainId + assetId: AssetId + + private parsers: SubParser[] = [] + + constructor(args: TransactionParserArgs) { + this.chainId = args.chainId + this.assetId = args.assetId + } + + /** + * Register custom transaction sub parser to parse custom op return data + * + * _parsers should be registered from most generic first to most specific last_ + */ + registerParser(parser: SubParser): void { + this.parsers.unshift(parser) + } + + protected registerParsers(parsers: SubParser[]): void { + parsers.forEach(parser => this.registerParser(parser)) + } + + async parse(tx: T, address: string): Promise { + const parserResult = await (async () => { + for (const parser of this.parsers) { + const result = await parser.parse(tx, address) + if (result) return result + } + })() + + const parsedTx: ParsedTx = { + address, + blockHeight: tx.blockHeight, + blockTime: tx.timestamp, + chainId: this.chainId, + confirmations: 1, + status: this.getStatus(tx), + trade: parserResult?.trade, + transfers: parserResult?.transfers ?? [], + txid: tx.txid, + } + + // network fee + if (tx.feePayer === address && tx.fee) { + parsedTx.fee = { assetId: this.assetId, value: BigInt(tx.fee).toString() } + } + + tx.nativeTransfers?.forEach(nativeTransfer => { + const { amount, fromUserAccount, toUserAccount } = nativeTransfer + + // send amount + if (nativeTransfer.fromUserAccount === address) { + parsedTx.transfers = aggregateTransfer({ + assetId: this.assetId, + from: fromUserAccount ?? '', + to: toUserAccount ?? '', + transfers: parsedTx.transfers, + type: TransferType.Send, + value: BigInt(amount).toString(), + }) + } + + // receive amount + if (nativeTransfer.toUserAccount === address) { + parsedTx.transfers = aggregateTransfer({ + assetId: this.assetId, + from: fromUserAccount ?? '', + to: toUserAccount ?? '', + transfers: parsedTx.transfers, + type: TransferType.Receive, + value: BigInt(amount).toString(), + }) + } + }) + + return parsedTx + } + + private getStatus(tx: T): TxStatus { + if (tx.transactionError) return TxStatus.Failed + return TxStatus.Confirmed + } +} diff --git a/packages/unchained-client/src/solana/parser/types.ts b/packages/unchained-client/src/solana/parser/types.ts new file mode 100644 index 00000000000..de2b3774dc0 --- /dev/null +++ b/packages/unchained-client/src/solana/parser/types.ts @@ -0,0 +1,14 @@ +import type * as solana from '../../generated/solana' +import type { StandardTx } from '../../types' + +export * from '../../generated/solana' + +export type Tx = solana.Tx + +export interface ParsedTx extends StandardTx {} + +export type TxSpecific = Partial> + +export interface SubParser { + parse(tx: T, address: string): Promise +} From 4ab1c60423c8a42b1a79db32cca9269a670e038d Mon Sep 17 00:00:00 2001 From: kaladinlight <35275952+kaladinlight@users.noreply.github.com> Date: Mon, 14 Oct 2024 08:56:14 -0600 Subject: [PATCH 2/4] comment --- packages/unchained-client/src/solana/parser/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/unchained-client/src/solana/parser/index.ts b/packages/unchained-client/src/solana/parser/index.ts index fa22d8e1dec..c514f2e5ba9 100644 --- a/packages/unchained-client/src/solana/parser/index.ts +++ b/packages/unchained-client/src/solana/parser/index.ts @@ -48,6 +48,7 @@ export class TransactionParser { blockHeight: tx.blockHeight, blockTime: tx.timestamp, chainId: this.chainId, + // all transactions from unchained are finalized with at least 1 confirmation (unused throughout web) confirmations: 1, status: this.getStatus(tx), trade: parserResult?.trade, From eec4e5998b39f8d2a7bce30335569c6c67d13de9 Mon Sep 17 00:00:00 2001 From: kaladinlight <35275952+kaladinlight@users.noreply.github.com> Date: Fri, 11 Oct 2024 10:31:01 -0600 Subject: [PATCH 3/4] feat: solana chain adapter --- package.json | 28 +- packages/chain-adapters/package.json | 1 + packages/chain-adapters/src/index.ts | 1 + .../src/solana/SolanaChainAdapter.ts | 354 ++++++++++++++++++ packages/chain-adapters/src/solana/index.ts | 3 + packages/chain-adapters/src/solana/types.ts | 41 ++ packages/chain-adapters/src/solana/utils.ts | 5 + packages/chain-adapters/src/types.ts | 8 + yarn.lock | 174 ++++----- 9 files changed, 515 insertions(+), 100 deletions(-) create mode 100644 packages/chain-adapters/src/solana/SolanaChainAdapter.ts create mode 100644 packages/chain-adapters/src/solana/index.ts create mode 100644 packages/chain-adapters/src/solana/types.ts create mode 100644 packages/chain-adapters/src/solana/utils.ts diff --git a/package.json b/package.json index 3fdef3c3966..87ca5998080 100644 --- a/package.json +++ b/package.json @@ -93,20 +93,20 @@ "@shapeshiftoss/caip": "workspace:^", "@shapeshiftoss/chain-adapters": "workspace:^", "@shapeshiftoss/errors": "workspace:^", - "@shapeshiftoss/hdwallet-coinbase": "1.55.9", - "@shapeshiftoss/hdwallet-core": "1.55.9", - "@shapeshiftoss/hdwallet-keepkey": "1.55.9", - "@shapeshiftoss/hdwallet-keepkey-webusb": "1.55.9", - "@shapeshiftoss/hdwallet-keplr": "1.55.9", - "@shapeshiftoss/hdwallet-ledger": "1.55.9", - "@shapeshiftoss/hdwallet-ledger-webusb": "1.55.9", - "@shapeshiftoss/hdwallet-metamask": "1.55.9", - "@shapeshiftoss/hdwallet-native": "1.55.9", - "@shapeshiftoss/hdwallet-native-vault": "1.55.9", - "@shapeshiftoss/hdwallet-phantom": "1.55.9", - "@shapeshiftoss/hdwallet-shapeshift-multichain": "1.55.9", - "@shapeshiftoss/hdwallet-walletconnectv2": "1.55.9", - "@shapeshiftoss/hdwallet-xdefi": "1.55.9", + "@shapeshiftoss/hdwallet-coinbase": "1.55.10", + "@shapeshiftoss/hdwallet-core": "1.55.10", + "@shapeshiftoss/hdwallet-keepkey": "1.55.10", + "@shapeshiftoss/hdwallet-keepkey-webusb": "1.55.10", + "@shapeshiftoss/hdwallet-keplr": "1.55.10", + "@shapeshiftoss/hdwallet-ledger": "1.55.10", + "@shapeshiftoss/hdwallet-ledger-webusb": "1.55.10", + "@shapeshiftoss/hdwallet-metamask": "1.55.10", + "@shapeshiftoss/hdwallet-native": "1.55.10", + "@shapeshiftoss/hdwallet-native-vault": "1.55.10", + "@shapeshiftoss/hdwallet-phantom": "1.55.10", + "@shapeshiftoss/hdwallet-shapeshift-multichain": "1.55.10", + "@shapeshiftoss/hdwallet-walletconnectv2": "1.55.10", + "@shapeshiftoss/hdwallet-xdefi": "1.55.10", "@shapeshiftoss/swapper": "workspace:^", "@shapeshiftoss/types": "workspace:^", "@shapeshiftoss/unchained-client": "workspace:^", diff --git a/packages/chain-adapters/package.json b/packages/chain-adapters/package.json index 226578dcef8..65e5a6d6e43 100644 --- a/packages/chain-adapters/package.json +++ b/packages/chain-adapters/package.json @@ -21,6 +21,7 @@ "@shapeshiftoss/types": "workspace:^", "@shapeshiftoss/unchained-client": "workspace:^", "@shapeshiftoss/utils": "workspace:^", + "@solana/web3.js": "^1.95.3", "bech32": "^2.0.0", "coinselect": "^3.1.13", "multicoin-address-validator": "^0.5.12", diff --git a/packages/chain-adapters/src/index.ts b/packages/chain-adapters/src/index.ts index 58f466a97bd..f6399218305 100644 --- a/packages/chain-adapters/src/index.ts +++ b/packages/chain-adapters/src/index.ts @@ -5,3 +5,4 @@ export * from './types' export * from './evm' export * from './utxo' export * from './cosmossdk' +export * as solana from './solana' diff --git a/packages/chain-adapters/src/solana/SolanaChainAdapter.ts b/packages/chain-adapters/src/solana/SolanaChainAdapter.ts new file mode 100644 index 00000000000..d129c7aa87f --- /dev/null +++ b/packages/chain-adapters/src/solana/SolanaChainAdapter.ts @@ -0,0 +1,354 @@ +import type { AssetId, ChainId } from '@shapeshiftoss/caip' +import { ASSET_REFERENCE, solanaChainId, solAssetId, toAssetId } from '@shapeshiftoss/caip' +import type { SolanaSignTx } from '@shapeshiftoss/hdwallet-core' +import { supportsSolana } from '@shapeshiftoss/hdwallet-core' +import type { BIP44Params } from '@shapeshiftoss/types' +import { KnownChainIds } from '@shapeshiftoss/types' +import * as unchained from '@shapeshiftoss/unchained-client' +import { bn } from '@shapeshiftoss/utils' +import { Connection, PublicKey } from '@solana/web3.js' +import PQueue from 'p-queue' + +import type { ChainAdapter as IChainAdapter } from '../api' +import { ErrorHandler } from '../error/ErrorHandler' +import type { + Account, + BroadcastTransactionInput, + BuildSendTxInput, + FeeDataEstimate, + GetAddressInput, + GetBIP44ParamsInput, + GetFeeDataInput, + SignAndBroadcastTransactionInput, + SignTx, + SignTxInput, + SubscribeError, + SubscribeTxsInput, + Transaction, + TxHistoryInput, + TxHistoryResponse, + ValidAddressResult, +} from '../types' +import { ChainAdapterDisplayName, CONTRACT_INTERACTION, ValidAddressResultType } from '../types' +import { toAddressNList, toRootDerivationPath } from '../utils' +import { assertAddressNotSanctioned } from '../utils/validateAddress' +import { microLamportsToLamports } from './utils' + +export interface ChainAdapterArgs { + providers: { + http: unchained.solana.Api + ws: unchained.ws.Client + } + rpcUrl: string +} + +export class ChainAdapter implements IChainAdapter { + static readonly defaultBIP44Params: BIP44Params = { + purpose: 44, + coinType: Number(ASSET_REFERENCE.Solana), + accountNumber: 0, + } + + protected readonly chainId = solanaChainId + protected readonly assetId = solAssetId + + protected readonly providers: { + http: unchained.solana.Api + ws: unchained.ws.Client + } + + protected connection: Connection + protected parser: unchained.solana.TransactionParser + + constructor(args: ChainAdapterArgs) { + this.providers = args.providers + + this.connection = new Connection(args.rpcUrl) + + this.parser = new unchained.solana.TransactionParser({ + assetId: this.assetId, + chainId: this.chainId, + }) + } + + getName() { + const enumIndex = Object.values(ChainAdapterDisplayName).indexOf(ChainAdapterDisplayName.Solana) + return Object.keys(ChainAdapterDisplayName)[enumIndex] + } + + getDisplayName() { + return ChainAdapterDisplayName.Solana + } + + getType(): KnownChainIds.SolanaMainnet { + return KnownChainIds.SolanaMainnet + } + + getFeeAssetId(): AssetId { + return this.assetId + } + + getChainId(): ChainId { + return this.chainId + } + + getBIP44Params({ accountNumber }: GetBIP44ParamsInput): BIP44Params { + if (accountNumber < 0) { + throw new Error('accountNumber must be >= 0') + } + return { ...ChainAdapter.defaultBIP44Params, accountNumber } + } + + async getAddress(input: GetAddressInput): Promise { + try { + const { accountNumber, pubKey, wallet, showOnDevice = false } = input + + if (pubKey) return pubKey + + if (!supportsSolana(wallet)) throw new Error('Wallet does not support Solana.') + + const address = await wallet.solanaGetAddress({ + addressNList: toAddressNList(this.getBIP44Params({ accountNumber })), + showDisplay: showOnDevice, + }) + + if (!address) throw new Error('Unable to generate Solana address.') + + return address + } catch (err) { + return ErrorHandler(err) + } + } + + async getAccount(pubkey: string): Promise> { + try { + const data = await this.providers.http.getAccount({ pubkey }) + + const balance = BigInt(data.balance) + BigInt(data.unconfirmedBalance) + + return { + balance: balance.toString(), + chainId: this.chainId, + assetId: this.assetId, + chain: this.getType(), + chainSpecific: { + tokens: data.tokens.map(token => ({ + assetId: toAssetId({ + chainId: this.chainId, + assetNamespace: 'spl', + assetReference: token.id, + }), + balance: token.balance, + name: token.name, + precision: token.decimals, + symbol: token.symbol, + })), + }, + pubkey, + } + } catch (err) { + return ErrorHandler(err) + } + } + + async getTxHistory(input: TxHistoryInput): Promise { + const requestQueue = input.requestQueue ?? new PQueue() + + try { + const data = await requestQueue.add(() => + this.providers.http.getTxHistory({ + pubkey: input.pubkey, + pageSize: input.pageSize, + cursor: input.cursor, + }), + ) + + const txs = await Promise.all( + data.txs.map(tx => requestQueue.add(() => this.parseTx(tx, input.pubkey))), + ) + + return { + cursor: data.cursor ?? '', + pubkey: input.pubkey, + transactions: txs, + } + } catch (err) { + return ErrorHandler(err) + } + } + + async buildSendTransaction(input: BuildSendTxInput): Promise<{ + txToSign: SignTx + }> { + try { + const { accountNumber, to, value, chainSpecific } = input + + const { blockhash } = await this.connection.getLatestBlockhash() + + const computeUnitLimit = chainSpecific.computeUnitLimit + ? Number(chainSpecific.computeUnitLimit) + : undefined + + const computeUnitPrice = chainSpecific.computeUnitPrice + ? Number(chainSpecific.computeUnitPrice) + : undefined + + const txToSign: SignTx = { + addressNList: toAddressNList(this.getBIP44Params({ accountNumber })), + blockHash: blockhash, + computeUnitLimit, + computeUnitPrice, + // TODO: handle extra instructions + instructions: undefined, + to, + value, + } + + return { txToSign } + } catch (err) { + return ErrorHandler(err) + } + } + + async signTransaction(signTxInput: SignTxInput): Promise { + try { + const { txToSign, wallet } = signTxInput + + if (!supportsSolana(wallet)) throw new Error('Wallet does not support Solana.') + + const signedTx = await wallet.solanaSignTx(txToSign) + + if (!signedTx?.serialized) throw new Error('Error signing tx') + + return signedTx.serialized + } catch (err) { + return ErrorHandler(err) + } + } + + async signAndBroadcastTransaction({ + senderAddress, + receiverAddress, + signTxInput, + }: SignAndBroadcastTransactionInput): Promise { + try { + const { txToSign, wallet } = signTxInput + + await Promise.all([ + assertAddressNotSanctioned(senderAddress), + receiverAddress !== CONTRACT_INTERACTION && assertAddressNotSanctioned(receiverAddress), + ]) + + if (!supportsSolana(wallet)) throw new Error('Wallet does not support Solana.') + + const tx = await wallet.solanaSendTx?.(txToSign) + + if (!tx) throw new Error('Error signing & broadcasting tx') + + return tx.signature + } catch (err) { + return ErrorHandler(err) + } + } + + async broadcastTransaction({ + senderAddress, + receiverAddress, + hex, + }: BroadcastTransactionInput): Promise { + try { + await Promise.all([ + assertAddressNotSanctioned(senderAddress), + receiverAddress !== CONTRACT_INTERACTION && assertAddressNotSanctioned(receiverAddress), + ]) + + return this.providers.http.sendTx({ sendTxBody: { hex } }) + } catch (err) { + return ErrorHandler(err) + } + } + + async getFeeData( + input: GetFeeDataInput, + ): Promise> { + const { baseFee, fast, average, slow } = await this.providers.http.getPriorityFees() + + const computeUnits = await this.providers.http.estimateFees({ + estimateFeesBody: { message: input.chainSpecific?.message }, + }) + + return { + fast: { + txFee: bn(microLamportsToLamports(fast)).times(computeUnits).plus(baseFee).toFixed(), + chainSpecific: { computeUnits }, + }, + average: { + txFee: bn(microLamportsToLamports(average)).times(computeUnits).plus(baseFee).toFixed(), + chainSpecific: { computeUnits }, + }, + slow: { + txFee: bn(microLamportsToLamports(slow)).times(computeUnits).plus(baseFee).toFixed(), + chainSpecific: { computeUnits }, + }, + } + } + + // eslint-disable-next-line require-await + async validateAddress(address: string): Promise { + try { + new PublicKey(address) + return { valid: true, result: ValidAddressResultType.Valid } + } catch (err) { + return { valid: false, result: ValidAddressResultType.Invalid } + } + } + + async subscribeTxs( + input: SubscribeTxsInput, + onMessage: (msg: Transaction) => void, + onError: (err: SubscribeError) => void, + ): Promise { + const { pubKey, accountNumber, wallet } = input + + const bip44Params = this.getBIP44Params({ accountNumber }) + const address = await this.getAddress({ accountNumber, wallet, pubKey }) + const subscriptionId = toRootDerivationPath(bip44Params) + + await this.providers.ws.subscribeTxs( + subscriptionId, + { topic: 'txs', addresses: [address] }, + async msg => onMessage(await this.parseTx(msg.data, msg.address)), + err => onError({ message: err.message }), + ) + } + + unsubscribeTxs(input?: SubscribeTxsInput): void { + if (!input) return this.providers.ws.unsubscribeTxs() + + const { accountNumber } = input + const bip44Params = this.getBIP44Params({ accountNumber }) + const subscriptionId = toRootDerivationPath(bip44Params) + + this.providers.ws.unsubscribeTxs(subscriptionId, { topic: 'txs', addresses: [] }) + } + + closeTxs(): void { + this.providers.ws.close('txs') + } + + protected async parseTx(tx: unchained.solana.Tx, pubkey: string): Promise { + const { address: _, ...parsedTx } = await this.parser.parse(tx, pubkey) + + return { + ...parsedTx, + pubkey, + transfers: parsedTx.transfers.map(transfer => ({ + assetId: transfer.assetId, + from: [transfer.from], + to: [transfer.to], + type: transfer.type, + value: transfer.totalValue, + })), + } + } +} diff --git a/packages/chain-adapters/src/solana/index.ts b/packages/chain-adapters/src/solana/index.ts new file mode 100644 index 00000000000..79fd113c81a --- /dev/null +++ b/packages/chain-adapters/src/solana/index.ts @@ -0,0 +1,3 @@ +export { ChainAdapter } from './SolanaChainAdapter' + +export * from './types' diff --git a/packages/chain-adapters/src/solana/types.ts b/packages/chain-adapters/src/solana/types.ts new file mode 100644 index 00000000000..cc60abbd761 --- /dev/null +++ b/packages/chain-adapters/src/solana/types.ts @@ -0,0 +1,41 @@ +import type { SolanaTxInstruction } from '@shapeshiftoss/hdwallet-core' +import type { CosmosSdkChainId } from '@shapeshiftoss/types' + +import type * as types from '../types' + +export type Account = { + tokens?: Token[] +} + +export type Token = types.AssetBalance & { + symbol: string + name: string + precision: number +} + +export type BuildTransactionInput = { + account: types.Account + accountNumber: number + memo?: string +} & types.ChainSpecificBuildTxData + +export type BuildTxInput = { + computeUnitLimit?: string + computeUnitPrice?: string + instructions?: SolanaTxInstruction[] +} + +export type GetFeeDataInput = { + message: string +} + +export type FeeData = { + computeUnits: string +} + +export type PriorityFeeData = { + baseFee: string + [types.FeeDataKey.Fast]: string + [types.FeeDataKey.Average]: string + [types.FeeDataKey.Slow]: string +} diff --git a/packages/chain-adapters/src/solana/utils.ts b/packages/chain-adapters/src/solana/utils.ts new file mode 100644 index 00000000000..f8e6e1ed087 --- /dev/null +++ b/packages/chain-adapters/src/solana/utils.ts @@ -0,0 +1,5 @@ +import { bn, bnOrZero } from '@shapeshiftoss/utils' + +export const microLamportsToLamports = (microLamports: string): string => { + return bnOrZero(microLamports).div(bn(10).pow(6)).toFixed() +} diff --git a/packages/chain-adapters/src/types.ts b/packages/chain-adapters/src/types.ts index 29cc7a7e4fe..15158d40771 100644 --- a/packages/chain-adapters/src/types.ts +++ b/packages/chain-adapters/src/types.ts @@ -4,6 +4,7 @@ import type { CosmosSignTx, ETHSignTx, HDWallet, + SolanaSignTx, ThorchainSignTx, } from '@shapeshiftoss/hdwallet-core' import type { @@ -17,6 +18,7 @@ import type PQueue from 'p-queue' import type * as cosmossdk from './cosmossdk/types' import type * as evm from './evm/types' +import type * as solana from './solana/types' import type * as utxo from './utxo/types' // this placeholder forces us to be explicit about transactions not transferring funds to humans @@ -41,6 +43,7 @@ type ChainSpecificAccount = ChainSpecific< [KnownChainIds.LitecoinMainnet]: utxo.Account [KnownChainIds.CosmosMainnet]: cosmossdk.Account [KnownChainIds.ThorchainMainnet]: cosmossdk.Account + [KnownChainIds.SolanaMainnet]: solana.Account } > @@ -81,6 +84,7 @@ type ChainSpecificFeeData = ChainSpecific< [KnownChainIds.LitecoinMainnet]: utxo.FeeData [KnownChainIds.CosmosMainnet]: cosmossdk.FeeData [KnownChainIds.ThorchainMainnet]: cosmossdk.FeeData + [KnownChainIds.SolanaMainnet]: solana.FeeData } > @@ -154,6 +158,7 @@ export type ChainSignTx = { [KnownChainIds.LitecoinMainnet]: BTCSignTx [KnownChainIds.CosmosMainnet]: CosmosSignTx [KnownChainIds.ThorchainMainnet]: ThorchainSignTx + [KnownChainIds.SolanaMainnet]: SolanaSignTx } export type SignTx = T extends keyof ChainSignTx ? ChainSignTx[T] : never @@ -196,6 +201,7 @@ export type ChainSpecificBuildTxData = ChainSpecific< [KnownChainIds.LitecoinMainnet]: utxo.BuildTxInput [KnownChainIds.CosmosMainnet]: cosmossdk.BuildTxInput [KnownChainIds.ThorchainMainnet]: cosmossdk.BuildTxInput + [KnownChainIds.SolanaMainnet]: solana.BuildTxInput } > @@ -288,6 +294,7 @@ type ChainSpecificGetFeeDataInput = ChainSpecific< [KnownChainIds.BitcoinCashMainnet]: utxo.GetFeeDataInput [KnownChainIds.DogecoinMainnet]: utxo.GetFeeDataInput [KnownChainIds.LitecoinMainnet]: utxo.GetFeeDataInput + [KnownChainIds.SolanaMainnet]: solana.GetFeeDataInput } > export type GetFeeDataInput = { @@ -349,6 +356,7 @@ export enum ChainAdapterDisplayName { BitcoinCash = 'Bitcoin Cash', Dogecoin = 'Dogecoin', Litecoin = 'Litecoin', + Solana = 'Solana', } export type BroadcastTransactionInput = { diff --git a/yarn.lock b/yarn.lock index ec157436d3f..c7e8105599c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11233,6 +11233,7 @@ __metadata: "@shapeshiftoss/types": "workspace:^" "@shapeshiftoss/unchained-client": "workspace:^" "@shapeshiftoss/utils": "workspace:^" + "@solana/web3.js": ^1.95.3 "@types/multicoin-address-validator": ^0.5.0 bech32: ^2.0.0 coinselect: ^3.1.13 @@ -11308,15 +11309,15 @@ __metadata: languageName: node linkType: hard -"@shapeshiftoss/hdwallet-coinbase@npm:1.55.9": - version: 1.55.9 - resolution: "@shapeshiftoss/hdwallet-coinbase@npm:1.55.9" +"@shapeshiftoss/hdwallet-coinbase@npm:1.55.10": + version: 1.55.10 + resolution: "@shapeshiftoss/hdwallet-coinbase@npm:1.55.10" dependencies: "@coinbase/wallet-sdk": ^3.6.6 - "@shapeshiftoss/hdwallet-core": 1.55.9 + "@shapeshiftoss/hdwallet-core": 1.55.10 eth-rpc-errors: ^4.0.3 lodash: ^4.17.21 - checksum: 0d551f0e3a1b0787933aae756ecaca05e61b6a084fb000808ee2950ce44eeeebc45b95490ca370f06004b09784a1e14d4cf1ad574ad8502c7b4f0cba82ac9717 + checksum: ab6a11a68b3365f7197bcb1913b70430b4a5cf3104bf3667fd6c6682e7af442f88e27eb0c698981f2278dfcfad796c7925a5335cf5b68fbc608460a143a53249 languageName: node linkType: hard @@ -11346,9 +11347,9 @@ __metadata: languageName: node linkType: hard -"@shapeshiftoss/hdwallet-core@npm:1.55.9": - version: 1.55.9 - resolution: "@shapeshiftoss/hdwallet-core@npm:1.55.9" +"@shapeshiftoss/hdwallet-core@npm:1.55.10": + version: 1.55.10 + resolution: "@shapeshiftoss/hdwallet-core@npm:1.55.10" dependencies: "@shapeshiftoss/bitcoinjs-lib": 5.2.0-shapeshift.2 "@shapeshiftoss/proto-tx-builder": ^0.8.0 @@ -11358,30 +11359,30 @@ __metadata: lodash: ^4.17.21 rxjs: ^6.4.0 type-assertions: ^1.1.0 - checksum: e441830860c07518a871513b98d45ac881e2d1a80384d5fbc780c61c181c0f9a0131811633a93096fd4c48581ff2e097351be9bb6078d0821f270d7af24b0849 + checksum: cd85d7890ff6ffdcafd27a57169fa9cf589403362baf77804f2c9010e8a115c439910b6bf997ec718623b955dfc43e88ff44dde1d66bedfc98b1c26db0882ff2 languageName: node linkType: hard -"@shapeshiftoss/hdwallet-keepkey-webusb@npm:1.55.9": - version: 1.55.9 - resolution: "@shapeshiftoss/hdwallet-keepkey-webusb@npm:1.55.9" +"@shapeshiftoss/hdwallet-keepkey-webusb@npm:1.55.10": + version: 1.55.10 + resolution: "@shapeshiftoss/hdwallet-keepkey-webusb@npm:1.55.10" dependencies: - "@shapeshiftoss/hdwallet-core": 1.55.9 - "@shapeshiftoss/hdwallet-keepkey": 1.55.9 - checksum: fcb35e56f78390f7f654328943b59dee0cbbdcb0e3a037e8f0359c787c1f4e2dcabc576da3a8e6a9aab199c9108b0d3f72a3bf1b18c53d86ed08f8ba97e7e06b + "@shapeshiftoss/hdwallet-core": 1.55.10 + "@shapeshiftoss/hdwallet-keepkey": 1.55.10 + checksum: 5f109d8fa878a338ad67ca2468d172090e0c6e7e1db90672a0b11f7edbc3f681a4e439e373c9729369dc9c4f30fda00c3262071d5dec6506da65c61156b19b82 languageName: node linkType: hard -"@shapeshiftoss/hdwallet-keepkey@npm:1.55.9": - version: 1.55.9 - resolution: "@shapeshiftoss/hdwallet-keepkey@npm:1.55.9" +"@shapeshiftoss/hdwallet-keepkey@npm:1.55.10": + version: 1.55.10 + resolution: "@shapeshiftoss/hdwallet-keepkey@npm:1.55.10" dependencies: "@ethereumjs/common": ^2.4.0 "@ethereumjs/tx": ^3.3.0 "@keepkey/device-protocol": ^7.12.2 "@metamask/eth-sig-util": ^7.0.0 "@shapeshiftoss/bitcoinjs-lib": 5.2.0-shapeshift.2 - "@shapeshiftoss/hdwallet-core": 1.55.9 + "@shapeshiftoss/hdwallet-core": 1.55.10 "@shapeshiftoss/proto-tx-builder": ^0.8.0 bignumber.js: ^9.0.1 bnb-javascript-sdk-nobroadcast: ^2.16.14 @@ -11393,27 +11394,27 @@ __metadata: p-lazy: ^3.1.0 semver: ^7.3.8 tiny-secp256k1: ^1.1.6 - checksum: 7ea0164111c2bf4eb52067645ff3ee8ff0bcf05b95583b708dfb2f1aa4a07bee5bda5c1b0da7d4cc0abb20e4901998ba5ef78a9d1d99f687420378815a7f54c1 + checksum: 298f9c3589769cbc6d1e9d4fbb0176b4559ec5f0352f15762e2c3acea2ca8fd085ba472e3c34b58cf6a01df33e408f8fb9dd25e7c7a520adf84380dcd8d3b787 languageName: node linkType: hard -"@shapeshiftoss/hdwallet-keplr@npm:1.55.9": - version: 1.55.9 - resolution: "@shapeshiftoss/hdwallet-keplr@npm:1.55.9" +"@shapeshiftoss/hdwallet-keplr@npm:1.55.10": + version: 1.55.10 + resolution: "@shapeshiftoss/hdwallet-keplr@npm:1.55.10" dependencies: "@shapeshiftoss/caip": 8.15.0 - "@shapeshiftoss/hdwallet-core": 1.55.9 + "@shapeshiftoss/hdwallet-core": 1.55.10 "@shapeshiftoss/proto-tx-builder": ^0.8.0 "@shapeshiftoss/types": 3.1.3 base64-js: ^1.5.1 lodash: ^4.17.21 - checksum: 6fc0e10a19d520da7935fae0d3d1b21177a7ede3eb5305143b5a5208e6c86ca4b50f19d9a21afed756e451b244c9cb5cfe44e0fb79ff9ab5ffbba43a0f6b7e91 + checksum: a04c4a070e4db365ffb4a86c596ac37c17e3517a0e69c1bffcb3d90d3b9660393d3db6f7fad5e9c31478eea007938409ebf6a6559e1dfe2d5b475a273ffc379f languageName: node linkType: hard -"@shapeshiftoss/hdwallet-ledger-webusb@npm:1.55.9": - version: 1.55.9 - resolution: "@shapeshiftoss/hdwallet-ledger-webusb@npm:1.55.9" +"@shapeshiftoss/hdwallet-ledger-webusb@npm:1.55.10": + version: 1.55.10 + resolution: "@shapeshiftoss/hdwallet-ledger-webusb@npm:1.55.10" dependencies: "@ledgerhq/hw-app-btc": ^10.4.1 "@ledgerhq/hw-app-eth": ^6.38.0 @@ -11421,23 +11422,23 @@ __metadata: "@ledgerhq/hw-transport-webusb": ^6.29.2 "@ledgerhq/live-common": ^21.8.2 "@ledgerhq/logs": ^6.10.1 - "@shapeshiftoss/hdwallet-core": 1.55.9 - "@shapeshiftoss/hdwallet-ledger": 1.55.9 + "@shapeshiftoss/hdwallet-core": 1.55.10 + "@shapeshiftoss/hdwallet-ledger": 1.55.10 "@types/w3c-web-usb": ^1.0.4 p-queue: ^7.4.1 - checksum: aac21bbf07075063da25675f942421093850d1db702323e8e431d514d9c3c243f9d358be552e356e6df82d42ee064a817b0b77fac9612c17abeef24e70e7ff01 + checksum: 8c645914a42cd76e1b9c21b85aa6085e83c42aea32deff2a1bb06ca92613677f6d93b1a8f3e2dd7bcee1eddce08038612f06bd2b4088735c1aaa635584a4e4c3 languageName: node linkType: hard -"@shapeshiftoss/hdwallet-ledger@npm:1.55.9": - version: 1.55.9 - resolution: "@shapeshiftoss/hdwallet-ledger@npm:1.55.9" +"@shapeshiftoss/hdwallet-ledger@npm:1.55.10": + version: 1.55.10 + resolution: "@shapeshiftoss/hdwallet-ledger@npm:1.55.10" dependencies: "@ethereumjs/common": ^2.4.0 "@ethereumjs/tx": ^3.3.0 "@ledgerhq/hw-app-cosmos": ^6.29.1 "@shapeshiftoss/bitcoinjs-lib": 5.2.0-shapeshift.2 - "@shapeshiftoss/hdwallet-core": 1.55.9 + "@shapeshiftoss/hdwallet-core": 1.55.10 base64-js: ^1.5.1 bchaddrjs: ^0.4.4 bitcoinjs-message: ^2.0.0 @@ -11445,28 +11446,28 @@ __metadata: ethereumjs-tx: 1.3.7 ethereumjs-util: ^6.1.0 lodash: ^4.17.21 - checksum: 944b0277bb41aae5a8318b56f27285e36380cb6a536e4f23d0192a75930ee8c30b3bebbd01719bdf97a5c10fb603f22e1613df375680b4720275bc0ee1b9c41b + checksum: df341fe41c9cf75cc7c698cc7b36547c95c97c56d663ac939f516ebb7e9dbe74f682ac68fa841ae1d6fb1f5080fb9550eca932df59c721bea6fe15ceebd9d830 languageName: node linkType: hard -"@shapeshiftoss/hdwallet-metamask@npm:1.55.9": - version: 1.55.9 - resolution: "@shapeshiftoss/hdwallet-metamask@npm:1.55.9" +"@shapeshiftoss/hdwallet-metamask@npm:1.55.10": + version: 1.55.10 + resolution: "@shapeshiftoss/hdwallet-metamask@npm:1.55.10" dependencies: "@metamask/detect-provider": ^1.2.0 "@metamask/onboarding": ^1.0.1 - "@shapeshiftoss/hdwallet-core": 1.55.9 + "@shapeshiftoss/hdwallet-core": 1.55.10 eth-rpc-errors: ^4.0.3 lodash: ^4.17.21 - checksum: 2a80175faddf0a4f07ffe85a900d5a2763dfc5f15f2012fd8f168d8d4d02b78f02418a272e4f0cf4e5220f3d8e65f289c980b65bfc7d1f7890428dfb61e0b4f2 + checksum: db3803db2c49694052539aba19dd23df76fed55e5889708de21b6b912f53ecf8288e137d1a4ae34883897b2d02d7f6254ccb8c3cb641c3746bfd9ca76a10c137 languageName: node linkType: hard -"@shapeshiftoss/hdwallet-native-vault@npm:1.55.9": - version: 1.55.9 - resolution: "@shapeshiftoss/hdwallet-native-vault@npm:1.55.9" +"@shapeshiftoss/hdwallet-native-vault@npm:1.55.10": + version: 1.55.10 + resolution: "@shapeshiftoss/hdwallet-native-vault@npm:1.55.10" dependencies: - "@shapeshiftoss/hdwallet-native": 1.55.9 + "@shapeshiftoss/hdwallet-native": 1.55.10 bip39: ^3.0.4 hash-wasm: ^4.11.0 idb-keyval: ^6.0.3 @@ -11475,17 +11476,17 @@ __metadata: type-assertions: ^1.1.0 uuid: ^8.3.2 web-encoding: ^1.1.0 - checksum: 1f378982fb3a5368093eb7e6f1221e1e628d7fc3055667d33546b6e31f36d4a9e1255f3d5c9bbf15e02c46b856d07984de408abe4f61ba95b3765c60bb5a0f97 + checksum: 7713ed5ceeaea737d2d58de99e83f8c33b8bdad5450f420a95b82503cc1058ff5caf9e34de20e9c160182ca856ba9d042ddbf3afe0df79329af983d82aef959a languageName: node linkType: hard -"@shapeshiftoss/hdwallet-native@npm:1.55.9": - version: 1.55.9 - resolution: "@shapeshiftoss/hdwallet-native@npm:1.55.9" +"@shapeshiftoss/hdwallet-native@npm:1.55.10": + version: 1.55.10 + resolution: "@shapeshiftoss/hdwallet-native@npm:1.55.10" dependencies: "@shapeshiftoss/bitcoinjs-lib": 5.2.0-shapeshift.2 "@shapeshiftoss/fiosdk": 1.2.1-shapeshift.6 - "@shapeshiftoss/hdwallet-core": 1.55.9 + "@shapeshiftoss/hdwallet-core": 1.55.10 "@shapeshiftoss/proto-tx-builder": ^0.8.0 "@zxing/text-encoding": ^0.9.0 bchaddrjs: ^0.4.9 @@ -11507,7 +11508,7 @@ __metadata: tendermint-tx-builder: ^1.0.9 tiny-secp256k1: ^1.1.6 web-encoding: ^1.1.0 - checksum: a1e54cabc84fa28db420b20cde735a41720c43b14ccbf14108a519eaa637fee97caf48694ea95088eb801a05e1fc75b3d9ec8ccb56924426d822bcef8f810bd9 + checksum: 1e78caad03836d37233fb6edb3435286bc993573f068a2d96c980dffb1e3066e98834241599471b678b3f7fc7e1ca55fb16b0268e18398bc41cbae797d0cadc0 languageName: node linkType: hard @@ -11543,55 +11544,56 @@ __metadata: languageName: node linkType: hard -"@shapeshiftoss/hdwallet-phantom@npm:1.55.9": - version: 1.55.9 - resolution: "@shapeshiftoss/hdwallet-phantom@npm:1.55.9" +"@shapeshiftoss/hdwallet-phantom@npm:1.55.10": + version: 1.55.10 + resolution: "@shapeshiftoss/hdwallet-phantom@npm:1.55.10" dependencies: "@shapeshiftoss/bitcoinjs-lib": 5.2.0-shapeshift.2 - "@shapeshiftoss/hdwallet-core": 1.55.9 + "@shapeshiftoss/hdwallet-core": 1.55.10 + "@solana/web3.js": ^1.95.3 base64-js: ^1.5.1 bitcoinjs-message: ^2.0.0 ethers: 5.7.2 lodash: ^4.17.21 - checksum: db324c2ca0a79a6f499d7e7cbe4d8765849f75f9f5e4abcabd70752415036e4fdaaa75ee9b8d0c992df6cd612469357099f3f05ffb6e8d6df34d4fa5e79c32cf + checksum: 2a187ed9c2c48e3106068d1f3993631c980e21c2aabaf9361ca70b21c165b6ac384a6eed67b934f3fe53db3b27a4197cee09e51a6402739818076b21e5c7840f languageName: node linkType: hard -"@shapeshiftoss/hdwallet-shapeshift-multichain@npm:1.55.9": - version: 1.55.9 - resolution: "@shapeshiftoss/hdwallet-shapeshift-multichain@npm:1.55.9" +"@shapeshiftoss/hdwallet-shapeshift-multichain@npm:1.55.10": + version: 1.55.10 + resolution: "@shapeshiftoss/hdwallet-shapeshift-multichain@npm:1.55.10" dependencies: "@metamask/detect-provider": ^1.2.0 "@metamask/onboarding": ^1.0.1 "@shapeshiftoss/common-api": ^9.3.0 - "@shapeshiftoss/hdwallet-core": 1.55.9 + "@shapeshiftoss/hdwallet-core": 1.55.10 "@shapeshiftoss/metamask-snaps-adapter": ^1.0.10 "@shapeshiftoss/metamask-snaps-types": ^1.0.10 eth-rpc-errors: ^4.0.3 lodash: ^4.17.21 - checksum: eb1b1cd8d0a6724a3218aa0bd009524d828c7ba214b1b52d610fee42685b9af6845bf13db19358b6b52ebe3e8aee13c3d06430c429e33f91a4b03e6b19d99d44 + checksum: b0bf636b247dbf649576294dc066852a6049213b9b2e98bc33323c83534317837220020fd2333289859d84f86d39c5a53764291b9d4417f8cdc8e4f6f4c1aba3 languageName: node linkType: hard -"@shapeshiftoss/hdwallet-walletconnectv2@npm:1.55.9": - version: 1.55.9 - resolution: "@shapeshiftoss/hdwallet-walletconnectv2@npm:1.55.9" +"@shapeshiftoss/hdwallet-walletconnectv2@npm:1.55.10": + version: 1.55.10 + resolution: "@shapeshiftoss/hdwallet-walletconnectv2@npm:1.55.10" dependencies: - "@shapeshiftoss/hdwallet-core": 1.55.9 + "@shapeshiftoss/hdwallet-core": 1.55.10 "@walletconnect/ethereum-provider": ^2.10.1 "@walletconnect/modal": ^2.6.2 ethers: ^5.6.5 - checksum: bcc771b5e9215f0ac0a2b234ff0400ad927aefc548def9156e18c400b384ec882a464af11d6a52ba0e84fff630b04541937570c98baf5c92b246f9184bc27b3a + checksum: 43bffab85fdaaaad13987f8d0abfd1141fa1b4c8f86455f265e3b604ecf3be219b063f15041aa630292b1279eac53cae7188bc566b655914536092671bba47c2 languageName: node linkType: hard -"@shapeshiftoss/hdwallet-xdefi@npm:1.55.9": - version: 1.55.9 - resolution: "@shapeshiftoss/hdwallet-xdefi@npm:1.55.9" +"@shapeshiftoss/hdwallet-xdefi@npm:1.55.10": + version: 1.55.10 + resolution: "@shapeshiftoss/hdwallet-xdefi@npm:1.55.10" dependencies: - "@shapeshiftoss/hdwallet-core": 1.55.9 + "@shapeshiftoss/hdwallet-core": 1.55.10 lodash: ^4.17.21 - checksum: a1c22e0ce75484aef8a20311c1289c04ef18470b45c73da47f29ea019c017d645af0b3f7f76bf79b77c14f1f6b69fffd9373117e98c554d4215701c1349485c5 + checksum: 5058663f3f4f78a76a17322539c3a83fd708eb7606133d2f10b46d78fcee6ff00ba6cefcc3093c5c6d456bd00f1eb3247dd9edf33d665c7a3750d84e4a3756e0 languageName: node linkType: hard @@ -11786,20 +11788,20 @@ __metadata: "@shapeshiftoss/caip": "workspace:^" "@shapeshiftoss/chain-adapters": "workspace:^" "@shapeshiftoss/errors": "workspace:^" - "@shapeshiftoss/hdwallet-coinbase": 1.55.9 - "@shapeshiftoss/hdwallet-core": 1.55.9 - "@shapeshiftoss/hdwallet-keepkey": 1.55.9 - "@shapeshiftoss/hdwallet-keepkey-webusb": 1.55.9 - "@shapeshiftoss/hdwallet-keplr": 1.55.9 - "@shapeshiftoss/hdwallet-ledger": 1.55.9 - "@shapeshiftoss/hdwallet-ledger-webusb": 1.55.9 - "@shapeshiftoss/hdwallet-metamask": 1.55.9 - "@shapeshiftoss/hdwallet-native": 1.55.9 - "@shapeshiftoss/hdwallet-native-vault": 1.55.9 - "@shapeshiftoss/hdwallet-phantom": 1.55.9 - "@shapeshiftoss/hdwallet-shapeshift-multichain": 1.55.9 - "@shapeshiftoss/hdwallet-walletconnectv2": 1.55.9 - "@shapeshiftoss/hdwallet-xdefi": 1.55.9 + "@shapeshiftoss/hdwallet-coinbase": 1.55.10 + "@shapeshiftoss/hdwallet-core": 1.55.10 + "@shapeshiftoss/hdwallet-keepkey": 1.55.10 + "@shapeshiftoss/hdwallet-keepkey-webusb": 1.55.10 + "@shapeshiftoss/hdwallet-keplr": 1.55.10 + "@shapeshiftoss/hdwallet-ledger": 1.55.10 + "@shapeshiftoss/hdwallet-ledger-webusb": 1.55.10 + "@shapeshiftoss/hdwallet-metamask": 1.55.10 + "@shapeshiftoss/hdwallet-native": 1.55.10 + "@shapeshiftoss/hdwallet-native-vault": 1.55.10 + "@shapeshiftoss/hdwallet-phantom": 1.55.10 + "@shapeshiftoss/hdwallet-shapeshift-multichain": 1.55.10 + "@shapeshiftoss/hdwallet-walletconnectv2": 1.55.10 + "@shapeshiftoss/hdwallet-xdefi": 1.55.10 "@shapeshiftoss/swapper": "workspace:^" "@shapeshiftoss/types": "workspace:^" "@shapeshiftoss/unchained-client": "workspace:^" @@ -12133,7 +12135,7 @@ __metadata: languageName: node linkType: hard -"@solana/web3.js@npm:^1.95.2": +"@solana/web3.js@npm:^1.95.2, @solana/web3.js@npm:^1.95.3": version: 1.95.3 resolution: "@solana/web3.js@npm:1.95.3" dependencies: From ffa60130e017037b980f974baf64ce4eb622435d Mon Sep 17 00:00:00 2001 From: kaladinlight <35275952+kaladinlight@users.noreply.github.com> Date: Mon, 14 Oct 2024 10:18:54 -0600 Subject: [PATCH 4/4] update asset namespace --- .../chain-adapters/src/solana/SolanaChainAdapter.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/chain-adapters/src/solana/SolanaChainAdapter.ts b/packages/chain-adapters/src/solana/SolanaChainAdapter.ts index d129c7aa87f..e4ee69389de 100644 --- a/packages/chain-adapters/src/solana/SolanaChainAdapter.ts +++ b/packages/chain-adapters/src/solana/SolanaChainAdapter.ts @@ -1,5 +1,11 @@ import type { AssetId, ChainId } from '@shapeshiftoss/caip' -import { ASSET_REFERENCE, solanaChainId, solAssetId, toAssetId } from '@shapeshiftoss/caip' +import { + ASSET_NAMESPACE, + ASSET_REFERENCE, + solanaChainId, + solAssetId, + toAssetId, +} from '@shapeshiftoss/caip' import type { SolanaSignTx } from '@shapeshiftoss/hdwallet-core' import { supportsSolana } from '@shapeshiftoss/hdwallet-core' import type { BIP44Params } from '@shapeshiftoss/types' @@ -135,7 +141,7 @@ export class ChainAdapter implements IChainAdapter tokens: data.tokens.map(token => ({ assetId: toAssetId({ chainId: this.chainId, - assetNamespace: 'spl', + assetNamespace: ASSET_NAMESPACE.splToken, assetReference: token.id, }), balance: token.balance,