adapter.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. import {
  2. BaseMessageSignerWalletAdapter,
  3. isVersionedTransaction,
  4. scopePollingDetectionStrategy,
  5. WalletAccountError,
  6. WalletConnectionError,
  7. WalletDisconnectedError,
  8. WalletError,
  9. WalletNotConnectedError,
  10. WalletNotReadyError,
  11. WalletPublicKeyError,
  12. WalletReadyState,
  13. WalletSendTransactionError,
  14. WalletSignMessageError,
  15. WalletSignTransactionError,
  16. type EventEmitter,
  17. type SendTransactionOptions,
  18. type WalletName,
  19. } from '@solana/wallet-adapter-base';
  20. import {
  21. PublicKey,
  22. type Connection,
  23. type SendOptions,
  24. type Transaction,
  25. type TransactionSignature,
  26. type VersionedTransaction,
  27. } from '@solana/web3.js';
  28. interface TokenPocketWalletEvents {
  29. connect(...args: unknown[]): unknown;
  30. disconnect(...args: unknown[]): unknown;
  31. }
  32. interface TokenPocketWallet extends EventEmitter<TokenPocketWalletEvents> {
  33. isTokenPocket?: boolean;
  34. publicKey?: { toBytes(): Uint8Array };
  35. isConnected: boolean;
  36. signAndSendTransaction<T extends Transaction | VersionedTransaction>(
  37. transaction: T,
  38. options?: SendOptions
  39. ): Promise<{ signature: TransactionSignature }>;
  40. signTransaction<T extends Transaction | VersionedTransaction>(transaction: T): Promise<T>;
  41. signAllTransactions<T extends Transaction | VersionedTransaction>(transactions: T[]): Promise<T[]>;
  42. signMessage(message: Uint8Array): Promise<{ signature: Uint8Array }>;
  43. connect(): Promise<void>;
  44. disconnect(): Promise<void>;
  45. }
  46. interface TokenPocketWindow extends Window {
  47. solana?: TokenPocketWallet;
  48. }
  49. declare const window: TokenPocketWindow;
  50. export interface TokenPocketWalletAdapterConfig {}
  51. export const TokenPocketWalletName = 'TokenPocket' as WalletName<'TokenPocket'>;
  52. export class TokenPocketWalletAdapter extends BaseMessageSignerWalletAdapter {
  53. name = TokenPocketWalletName;
  54. url = 'https://tokenpocket.pro';
  55. icon =
  56. 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIwIiBoZWlnaHQ9IjEyMCIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGc+CjxwYXRoIGQ9Ik0xMDQxLjUyIDBILTI3VjEwMjRIMTA0MS41MlYwWiIgZmlsbD0iIzI5ODBGRSIvPgo8ZyBjbGlwLXBhdGg9InVybCgjY2xpcDBfNDA4XzIyNSkiPgo8cGF0aCBkPSJNNDA2Ljc5NiA0MzguNjQzSDQwNi45MjdDNDA2Ljc5NiA0MzcuODU3IDQwNi43OTYgNDM2Ljk0IDQwNi43OTYgNDM2LjE1NFY0MzguNjQzWiIgZmlsbD0iIzI5QUVGRiIvPgo8cGF0aCBkPSJNNjY3LjYwMiA0NjMuNTMzSDUyMy4yNDlWNzI0LjA3NkM1MjMuMjQ5IDczNi4zODkgNTMzLjIwNCA3NDYuMzQ1IDU0NS41MTcgNzQ2LjM0NUg2NDUuMzMzQzY1Ny42NDcgNzQ2LjM0NSA2NjcuNjAyIDczNi4zODkgNjY3LjYwMiA3MjQuMDc2VjQ2My41MzNaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNNDUzLjU2MyAyNzdINDQ4LjcxNkgxOTAuMjY5QzE3Ny45NTUgMjc3IDE2OCAyODYuOTU1IDE2OCAyOTkuMjY5VjM4OS42NTNDMTY4IDQwMS45NjcgMTc3Ljk1NSA0MTEuOTIyIDE5MC4yNjkgNDExLjkyMkgyNTAuOTE4SDI3NS4wMjFWNDM4LjY0NFY3MjQuNzMxQzI3NS4wMjEgNzM3LjA0NSAyODQuOTc2IDc0NyAyOTcuMjg5IDc0N0gzOTIuMTI4QzQwNC40NDEgNzQ3IDQxNC4zOTYgNzM3LjA0NSA0MTQuMzk2IDcyNC43MzFWNDM4LjY0NFY0MzYuMTU2VjQxMS45MjJINDM4LjQ5OUg0NDguMzIzSDQ1My4xN0M0OTAuMzcyIDQxMS45MjIgNTIwLjYzMSAzODEuNjYzIDUyMC42MzEgMzQ0LjQ2MUM1MjEuMDI0IDMwNy4yNTkgNDkwLjc2NSAyNzcgNDUzLjU2MyAyNzdaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNNjY3LjczNSA0NjMuNTMzVjY0NS4zNUM2NzIuNzEzIDY0Ni41MjkgNjc3LjgyMSA2NDcuNDQ2IDY4My4wNjEgNjQ4LjIzMkM2OTAuMzk3IDY0OS4yOCA2OTcuOTk0IDY0OS45MzUgNzA1LjU5MiA2NTAuMDY2QzcwNS45ODUgNjUwLjA2NiA3MDYuMzc4IDY1MC4wNjYgNzA2LjkwMiA2NTAuMDY2VjUwNS40NUM2ODUuMDI2IDUwNC4wMDkgNjY3LjczNSA0ODUuODAxIDY2Ny43MzUgNDYzLjUzM1oiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl80MDhfMjI1KSIvPgo8cGF0aCBkPSJNNzA5Ljc4MSAyNzdDNjA2LjgyMiAyNzcgNTIzLjI0OSAzNjAuNTczIDUyMy4yNDkgNDYzLjUzM0M1MjMuMjQ5IDU1Mi4wODQgNTg0Ljk0NiA2MjYuMjI1IDY2Ny43MzMgNjQ1LjM1VjQ2My41MzNDNjY3LjczMyA0NDAuMzQ3IDY4Ni41OTYgNDIxLjQ4NCA3MDkuNzgxIDQyMS40ODRDNzMyLjk2NyA0MjEuNDg0IDc1MS44MyA0NDAuMzQ3IDc1MS44MyA0NjMuNTMzQzc1MS44MyA0ODMuMDUxIDczOC42IDQ5OS40MjUgNzIwLjUyMyA1MDQuMTRDNzE3LjExNyA1MDUuMDU3IDcxMy40NDkgNTA1LjU4MSA3MDkuNzgxIDUwNS41ODFWNjUwLjA2NkM3MTMuNDQ5IDY1MC4wNjYgNzE2Ljk4NiA2NDkuOTM1IDcyMC41MjMgNjQ5LjgwNEM4MTguNTA1IDY0NC4xNzEgODk2LjMxNCA1NjIuOTU2IDg5Ni4zMTQgNDYzLjUzM0M4OTYuNDQ1IDM2MC41NzMgODEyLjg3MiAyNzcgNzA5Ljc4MSAyNzdaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNNzA5Ljc4IDY1MC4wNjZWNTA1LjU4MUM3MDguNzMzIDUwNS41ODEgNzA3LjgxNiA1MDUuNTgxIDcwNi43NjggNTA1LjQ1VjY1MC4wNjZDNzA3LjgxNiA2NTAuMDY2IDcwOC44NjQgNjUwLjA2NiA3MDkuNzggNjUwLjA2NloiIGZpbGw9IndoaXRlIi8+CjwvZz4KPC9nPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzQwOF8yMjUiIHgxPSI3MDkuODQ0IiB5MT0iNTU2LjgyNyIgeDI9IjY2Ny43NTMiIHkyPSI1NTYuODI3IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IndoaXRlIi8+CjxzdG9wIG9mZnNldD0iMC45NjY3IiBzdG9wLWNvbG9yPSJ3aGl0ZSIgc3RvcC1vcGFjaXR5PSIwLjMyMzMiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSJ3aGl0ZSIgc3RvcC1vcGFjaXR5PSIwLjMiLz4KPC9saW5lYXJHcmFkaWVudD4KPGNsaXBQYXRoIGlkPSJjbGlwMF80MDhfMjI1Ij4KPHJlY3Qgd2lkdGg9IjcyOC40NDgiIGhlaWdodD0iNDcwIiBmaWxsPSJ3aGl0ZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTY4IDI3NykiLz4KPC9jbGlwUGF0aD4KPC9kZWZzPgo8L3N2Zz4K';
  57. readonly supportedTransactionVersions = null;
  58. private _connecting: boolean;
  59. private _wallet: TokenPocketWallet | null;
  60. private _publicKey: PublicKey | null;
  61. private _readyState: WalletReadyState =
  62. typeof window === 'undefined' || typeof document === 'undefined'
  63. ? WalletReadyState.Unsupported
  64. : WalletReadyState.NotDetected;
  65. constructor(config: TokenPocketWalletAdapterConfig = {}) {
  66. super();
  67. this._connecting = false;
  68. this._wallet = null;
  69. this._publicKey = null;
  70. if (this._readyState !== WalletReadyState.Unsupported) {
  71. scopePollingDetectionStrategy(() => {
  72. if (window.solana?.isTokenPocket) {
  73. this._readyState = WalletReadyState.Installed;
  74. this.emit('readyStateChange', this._readyState);
  75. return true;
  76. }
  77. return false;
  78. });
  79. }
  80. }
  81. get publicKey() {
  82. return this._publicKey;
  83. }
  84. get connecting() {
  85. return this._connecting;
  86. }
  87. get connected() {
  88. return !!this._wallet?.isConnected;
  89. }
  90. get readyState() {
  91. return this._readyState;
  92. }
  93. async connect(): Promise<void> {
  94. try {
  95. if (this.connected || this.connecting) return;
  96. if (this._readyState !== WalletReadyState.Installed) throw new WalletNotReadyError();
  97. this._connecting = true;
  98. // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
  99. const wallet = window.solana!;
  100. try {
  101. await wallet.connect();
  102. } catch (error: any) {
  103. throw new WalletConnectionError(error?.message, error);
  104. }
  105. if (!wallet.publicKey) throw new WalletAccountError();
  106. let publicKey: PublicKey;
  107. try {
  108. publicKey = new PublicKey(wallet.publicKey.toBytes());
  109. } catch (error: any) {
  110. throw new WalletPublicKeyError(error?.message, error);
  111. }
  112. wallet.on('disconnect', this._disconnected);
  113. this._wallet = wallet;
  114. this._publicKey = publicKey;
  115. this.emit('connect', publicKey);
  116. } catch (error: any) {
  117. this.emit('error', error);
  118. throw error;
  119. } finally {
  120. this._connecting = false;
  121. }
  122. }
  123. async disconnect(): Promise<void> {
  124. const wallet = this._wallet;
  125. if (wallet) {
  126. wallet.off('disconnect', this._disconnected);
  127. this._wallet = null;
  128. this._publicKey = null;
  129. this.emit('disconnect');
  130. }
  131. }
  132. async sendTransaction<T extends Transaction | VersionedTransaction>(
  133. transaction: T,
  134. connection: Connection,
  135. options: SendTransactionOptions = {}
  136. ): Promise<TransactionSignature> {
  137. try {
  138. const wallet = this._wallet;
  139. if (!wallet) throw new WalletNotConnectedError();
  140. try {
  141. const { signers, ...sendOptions } = options;
  142. if (isVersionedTransaction(transaction)) {
  143. signers?.length && transaction.sign(signers);
  144. } else {
  145. transaction = (await this.prepareTransaction(transaction, connection, sendOptions)) as T;
  146. signers?.length && (transaction as Transaction).partialSign(...signers);
  147. }
  148. sendOptions.preflightCommitment = sendOptions.preflightCommitment || connection.commitment;
  149. const { signature } = await wallet.signAndSendTransaction(transaction, sendOptions);
  150. return signature;
  151. } catch (error: any) {
  152. if (error instanceof WalletError) throw error;
  153. throw new WalletSendTransactionError(error?.message, error);
  154. }
  155. } catch (error: any) {
  156. this.emit('error', error);
  157. throw error;
  158. }
  159. }
  160. async signTransaction<T extends Transaction | VersionedTransaction>(transaction: T): Promise<T> {
  161. try {
  162. const wallet = this._wallet;
  163. if (!wallet) throw new WalletNotConnectedError();
  164. try {
  165. return ((await wallet.signTransaction(transaction)) as T) || transaction;
  166. } catch (error: any) {
  167. throw new WalletSignTransactionError(error?.message, error);
  168. }
  169. } catch (error: any) {
  170. this.emit('error', error);
  171. throw error;
  172. }
  173. }
  174. async signAllTransactions<T extends Transaction | VersionedTransaction>(transactions: T[]): Promise<T[]> {
  175. try {
  176. const wallet = this._wallet;
  177. if (!wallet) throw new WalletNotConnectedError();
  178. try {
  179. return ((await wallet.signAllTransactions(transactions)) as T[]) || transactions;
  180. } catch (error: any) {
  181. throw new WalletSignTransactionError(error?.message, error);
  182. }
  183. } catch (error: any) {
  184. this.emit('error', error);
  185. throw error;
  186. }
  187. }
  188. async signMessage(message: Uint8Array): Promise<Uint8Array> {
  189. try {
  190. const wallet = this._wallet;
  191. if (!wallet) throw new WalletNotConnectedError();
  192. try {
  193. const { signature } = await wallet.signMessage(message);
  194. return signature;
  195. } catch (error: any) {
  196. throw new WalletSignMessageError(error?.message, error);
  197. }
  198. } catch (error: any) {
  199. this.emit('error', error);
  200. throw error;
  201. }
  202. }
  203. private _disconnected = () => {
  204. const wallet = this._wallet;
  205. if (wallet) {
  206. wallet.off('disconnect', this._disconnected);
  207. this._wallet = null;
  208. this._publicKey = null;
  209. this.emit('error', new WalletDisconnectedError());
  210. this.emit('disconnect');
  211. }
  212. };
  213. }