@ardrive/turbo-sdk 2.0.0 → 2.1.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/lib/cjs/cli/utils.js +1 -1
  2. package/lib/cjs/common/factory.js +1 -1
  3. package/lib/cjs/common/signer.js +4 -0
  4. package/lib/cjs/common/token/ario.js +11 -114
  5. package/lib/cjs/common/token/index.js +6 -0
  6. package/lib/cjs/common/token/solanaUsdc.js +29 -0
  7. package/lib/cjs/common/token/spl.js +186 -0
  8. package/lib/cjs/common/upload.js +1 -0
  9. package/lib/cjs/types.js +13 -1
  10. package/lib/cjs/utils/common.js +4 -0
  11. package/lib/cjs/version.js +1 -1
  12. package/lib/esm/cli/utils.js +1 -1
  13. package/lib/esm/common/factory.js +2 -2
  14. package/lib/esm/common/signer.js +4 -0
  15. package/lib/esm/common/token/ario.js +12 -112
  16. package/lib/esm/common/token/index.js +6 -0
  17. package/lib/esm/common/token/solanaUsdc.js +25 -0
  18. package/lib/esm/common/token/spl.js +179 -0
  19. package/lib/esm/common/upload.js +1 -0
  20. package/lib/esm/types.js +12 -0
  21. package/lib/esm/utils/common.js +4 -0
  22. package/lib/esm/version.js +1 -1
  23. package/lib/types/common/factory.d.ts.map +1 -1
  24. package/lib/types/common/signer.d.ts.map +1 -1
  25. package/lib/types/common/token/ario.d.ts +19 -22
  26. package/lib/types/common/token/ario.d.ts.map +1 -1
  27. package/lib/types/common/token/index.d.ts +2 -0
  28. package/lib/types/common/token/index.d.ts.map +1 -1
  29. package/lib/types/common/token/solanaUsdc.d.ts +40 -0
  30. package/lib/types/common/token/solanaUsdc.d.ts.map +1 -0
  31. package/lib/types/common/token/spl.d.ts +64 -0
  32. package/lib/types/common/token/spl.d.ts.map +1 -0
  33. package/lib/types/common/upload.d.ts.map +1 -1
  34. package/lib/types/types.d.ts +7 -1
  35. package/lib/types/types.d.ts.map +1 -1
  36. package/lib/types/utils/common.d.ts.map +1 -1
  37. package/lib/types/version.d.ts +1 -1
  38. package/lib/types/version.d.ts.map +1 -1
  39. package/package.json +2 -1
@@ -115,7 +115,7 @@ async function optionalPrivateKeyFromOptions(options) {
115
115
  async function privateKeyFromOptions({ privateKey, walletFile, token, }) {
116
116
  if (walletFile !== undefined) {
117
117
  const wallet = JSON.parse((0, fs_1.readFileSync)(walletFile, 'utf-8'));
118
- return token === 'solana' || token === 'ario'
118
+ return token === 'solana' || token === 'ario' || token === 'solana-usdc'
119
119
  ? bs58_1.default.encode(wallet)
120
120
  : wallet;
121
121
  }
@@ -126,7 +126,7 @@ class TurboBaseFactory {
126
126
  });
127
127
  }
128
128
  signerFromAdapter(walletAdapter, token) {
129
- if (token === 'solana') {
129
+ if (types_js_1.supportedSolanaSignerTokens.has(token)) {
130
130
  if (!(0, types_js_1.isSolanaWalletAdapter)(walletAdapter)) {
131
131
  throw new Error('Unsupported wallet adapter -- must implement publicKey and signMessage');
132
132
  }
@@ -48,7 +48,11 @@ class TurboDataItemAbstractSigner {
48
48
  }
49
49
  ownerToNativeAddress(owner, token) {
50
50
  switch (token) {
51
+ // `solana-usdc` is an ordinary Solana wallet paying with an SPL token,
52
+ // and the payment service credits the base58 sender address it reads off
53
+ // the transfer — so its native address is the same bs58 form as `solana`.
51
54
  case 'solana':
55
+ case 'solana-usdc':
52
56
  return bs58_1.default.encode(Uint8Array.from((0, base64_js_1.fromB64Url)(owner)));
53
57
  case 'ethereum':
54
58
  case 'matic':
@@ -1,7 +1,4 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.ARIOToTokenAmount = exports.mARIOToTokenAmount = exports.ARIOToken = void 0;
7
4
  /**
@@ -19,123 +16,23 @@ exports.ARIOToTokenAmount = exports.mARIOToTokenAmount = exports.ARIOToken = voi
19
16
  * See the License for the specific language governing permissions and
20
17
  * limitations under the License.
21
18
  */
22
- const spl_token_1 = require("@solana/spl-token");
23
- const web3_js_1 = require("@solana/web3.js");
24
19
  const bignumber_js_1 = require("bignumber.js");
25
- const bs58_1 = __importDefault(require("bs58"));
26
20
  const common_js_1 = require("../../utils/common.js");
27
- const logger_js_1 = require("../logger.js");
28
- const solana_js_1 = require("./solana.js");
21
+ const spl_js_1 = require("./spl.js");
29
22
  const ARIO_SPL_MINT_ADDRESS = 'DcNnMuFxwhgV4WY1HVSaSEgr92bv2b1vUvEKiNxWqHdF';
30
23
  const DEVNET_ARIO_SPL_MINT_ADDRESS = '6vTw5CysRXQ4ybbHkDUiisHWVsBeMtUzYvJqs2iqHyaN';
31
24
  const ARIO_TOKEN_DECIMALS = 6;
32
- class ARIOToken {
33
- constructor({ gatewayUrl = common_js_1.defaultProdGatewayUrls.solana, logger = logger_js_1.Logger.default, pollingOptions = {
34
- maxAttempts: 10,
35
- pollingIntervalMs: 2_500,
36
- initialBackoffMs: 500,
37
- }, } = {}) {
38
- this.gatewayUrl = gatewayUrl;
39
- this.connection = new web3_js_1.Connection(gatewayUrl, 'confirmed');
40
- this.pollingOptions = pollingOptions;
41
- this.logger = logger;
42
- if (gatewayUrl.includes('devnet')) {
43
- this.mintAddress = DEVNET_ARIO_SPL_MINT_ADDRESS;
44
- }
45
- else {
46
- this.mintAddress = ARIO_SPL_MINT_ADDRESS;
47
- }
48
- }
49
- async createAndSubmitTx({ target, signer, tokenAmount, turboCreditDestinationAddress, }) {
50
- const ownerPublicKey = new web3_js_1.PublicKey(bs58_1.default.encode(Uint8Array.from(await signer.getPublicKey())));
51
- const recipient = new web3_js_1.PublicKey(target);
52
- const mint = new web3_js_1.PublicKey(this.mintAddress);
53
- const fromAta = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, ownerPublicKey);
54
- const toAta = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, recipient);
55
- const tx = new web3_js_1.Transaction({
56
- feePayer: ownerPublicKey,
57
- ...(await this.connection.getLatestBlockhash()),
58
- });
59
- tx.add((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(ownerPublicKey, toAta, recipient, mint));
60
- tx.add((0, spl_token_1.createTransferCheckedInstruction)(fromAta, mint, toAta, ownerPublicKey, BigInt(new bignumber_js_1.BigNumber(tokenAmount).toFixed(0)), ARIO_TOKEN_DECIMALS));
61
- if (turboCreditDestinationAddress !== undefined) {
62
- tx.add(new web3_js_1.TransactionInstruction({
63
- programId: new web3_js_1.PublicKey(solana_js_1.memoProgramId),
64
- keys: [],
65
- data: Buffer.from('turboCreditDestinationAddress=' + turboCreditDestinationAddress),
66
- }));
67
- }
68
- const serializedTx = tx.serializeMessage();
69
- const signature = await signer.signData(Uint8Array.from(serializedTx));
70
- tx.addSignature(ownerPublicKey, Buffer.from(signature));
71
- const txId = bs58_1.default.encode(signature);
72
- await this.submitTx(tx, txId);
73
- this.logger.debug('Submitted ARIO SPL transfer transaction...', {
74
- id: txId,
75
- target,
76
- tokenAmount,
77
- fromAta: fromAta.toBase58(),
78
- toAta: toAta.toBase58(),
79
- mint: mint.toBase58(),
80
- });
81
- return { id: txId, target, reward: '0' };
82
- }
83
- async submitTx(tx, id) {
84
- this.logger.debug('Submitting ARIO fund transaction...', { id });
85
- await this.connection.sendRawTransaction(tx.serialize(), {
86
- maxRetries: this.pollingOptions.maxAttempts,
87
- });
88
- if (tx.recentBlockhash === undefined ||
89
- tx.lastValidBlockHeight === undefined) {
90
- throw new Error('Failed to submit Transaction -- missing blockhash or lastValidBlockHeight from transaction creation. Solana Gateway Url:' +
91
- this.gatewayUrl);
92
- }
93
- await this.connection.confirmTransaction({
94
- signature: id,
95
- blockhash: tx.recentBlockhash,
96
- lastValidBlockHeight: tx.lastValidBlockHeight,
97
- }, 'finalized');
98
- }
99
- async pollTxAvailability({ txId }) {
100
- const { maxAttempts, pollingIntervalMs, initialBackoffMs } = this.pollingOptions;
101
- this.logger.debug('Polling for ARIO SPL transaction...', {
102
- txId,
103
- pollingOptions: this.pollingOptions,
104
- gatewayUrl: this.gatewayUrl,
25
+ class ARIOToken extends spl_js_1.SplToken {
26
+ constructor({ gatewayUrl = common_js_1.defaultProdGatewayUrls.ario, ...config } = {}) {
27
+ super({
28
+ ...config,
29
+ gatewayUrl,
30
+ tokenName: 'ARIO',
31
+ decimals: ARIO_TOKEN_DECIMALS,
32
+ mintAddress: gatewayUrl.includes('devnet')
33
+ ? DEVNET_ARIO_SPL_MINT_ADDRESS
34
+ : ARIO_SPL_MINT_ADDRESS,
105
35
  });
106
- await (0, common_js_1.sleep)(initialBackoffMs);
107
- let attempts = 0;
108
- while (attempts < maxAttempts) {
109
- let status;
110
- attempts++;
111
- try {
112
- const statuses = await this.connection.getSignatureStatuses([txId], {
113
- searchTransactionHistory: true,
114
- });
115
- status = {
116
- context: statuses.context,
117
- value: statuses.value[0],
118
- };
119
- }
120
- catch (err) {
121
- this.logger.debug('Failed to poll ARIO SPL transaction...', { err });
122
- }
123
- if (status && status.value && status.value.err !== null) {
124
- throw new Error(`Transaction failed: ${status.value.err}`);
125
- }
126
- if (status && status.value && status.value.slot !== null) {
127
- this.logger.debug('Transaction found!', { txId, status });
128
- return;
129
- }
130
- this.logger.debug('ARIO SPL transaction not found, polling...', {
131
- txId,
132
- attempts,
133
- maxAttempts,
134
- pollingIntervalMs,
135
- });
136
- await (0, common_js_1.sleep)(pollingIntervalMs);
137
- }
138
- throw new Error('Transaction not found after polling, transaction id: ' + txId);
139
36
  }
140
37
  }
141
38
  exports.ARIOToken = ARIOToken;
@@ -23,6 +23,7 @@ const baseEth_js_1 = require("./baseEth.js");
23
23
  const ethereum_js_1 = require("./ethereum.js");
24
24
  const polygon_js_1 = require("./polygon.js");
25
25
  const solana_js_1 = require("./solana.js");
26
+ const solanaUsdc_js_1 = require("./solanaUsdc.js");
26
27
  const usdc_js_1 = require("./usdc.js");
27
28
  exports.defaultTokenMap = {
28
29
  arweave: (config) => new arweave_js_1.ArweaveToken(config),
@@ -35,6 +36,7 @@ exports.defaultTokenMap = {
35
36
  usdc: (config) => new usdc_js_1.USDCToken({ network: 'ethereum', ...config }),
36
37
  'base-usdc': (config) => new usdc_js_1.USDCToken({ network: 'base', ...config }),
37
38
  'polygon-usdc': (config) => new usdc_js_1.USDCToken({ network: 'polygon', ...config }),
39
+ 'solana-usdc': (config) => new solanaUsdc_js_1.SolanaUsdcToken(config),
38
40
  };
39
41
  const ethExponent = 18;
40
42
  const usdcExponent = 6;
@@ -50,6 +52,7 @@ exports.exponentMap = {
50
52
  usdc: usdcExponent,
51
53
  'base-usdc': usdcExponent,
52
54
  'polygon-usdc': usdcExponent,
55
+ 'solana-usdc': usdcExponent,
53
56
  };
54
57
  exports.tokenToBaseMap = {
55
58
  arweave: (a) => (0, arweave_js_1.ARToTokenAmount)(a),
@@ -62,6 +65,7 @@ exports.tokenToBaseMap = {
62
65
  usdc: (a) => (0, usdc_js_1.USDCToTokenAmount)(a),
63
66
  'base-usdc': (a) => (0, usdc_js_1.USDCToTokenAmount)(a),
64
67
  'polygon-usdc': (a) => (0, usdc_js_1.USDCToTokenAmount)(a),
68
+ 'solana-usdc': (a) => (0, usdc_js_1.USDCToTokenAmount)(a),
65
69
  };
66
70
  function isTokenType(token) {
67
71
  return types_js_1.tokenTypes.includes(token);
@@ -72,3 +76,5 @@ __exportStar(require("./solana.js"), exports);
72
76
  __exportStar(require("./ethereum.js"), exports);
73
77
  __exportStar(require("./baseEth.js"), exports);
74
78
  __exportStar(require("./polygon.js"), exports);
79
+ __exportStar(require("./spl.js"), exports);
80
+ __exportStar(require("./solanaUsdc.js"), exports);
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SolanaUsdcToken = exports.DEVNET_SOLANA_USDC_MINT_ADDRESS = exports.SOLANA_USDC_MINT_ADDRESS = void 0;
4
+ const common_js_1 = require("../../utils/common.js");
5
+ const spl_js_1 = require("./spl.js");
6
+ /** Circle's USDC mints on Solana. Both are 6-decimal SPL Token mints. */
7
+ exports.SOLANA_USDC_MINT_ADDRESS = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';
8
+ exports.DEVNET_SOLANA_USDC_MINT_ADDRESS = '4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU';
9
+ const USDC_TOKEN_DECIMALS = 6;
10
+ /**
11
+ * USDC (SPL) on Solana. Signed with the ordinary Solana signer — the payer's
12
+ * USDC lives in an associated token account derived from the same keypair, so
13
+ * no extra wallet or approval step is involved.
14
+ */
15
+ class SolanaUsdcToken extends spl_js_1.SplToken {
16
+ constructor({ gatewayUrl = common_js_1.defaultProdGatewayUrls['solana-usdc'], mintAddress, ...config } = {}) {
17
+ super({
18
+ ...config,
19
+ gatewayUrl,
20
+ tokenName: 'USDC',
21
+ decimals: USDC_TOKEN_DECIMALS,
22
+ mintAddress: mintAddress ??
23
+ (gatewayUrl.includes('devnet')
24
+ ? exports.DEVNET_SOLANA_USDC_MINT_ADDRESS
25
+ : exports.SOLANA_USDC_MINT_ADDRESS),
26
+ });
27
+ }
28
+ }
29
+ exports.SolanaUsdcToken = SolanaUsdcToken;
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SplToken = void 0;
7
+ /**
8
+ * Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
9
+ *
10
+ * Licensed under the Apache License, Version 2.0 (the "License");
11
+ * you may not use this file except in compliance with the License.
12
+ * You may obtain a copy of the License at
13
+ *
14
+ * http://www.apache.org/licenses/LICENSE-2.0
15
+ *
16
+ * Unless required by applicable law or agreed to in writing, software
17
+ * distributed under the License is distributed on an "AS IS" BASIS,
18
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
+ * See the License for the specific language governing permissions and
20
+ * limitations under the License.
21
+ */
22
+ const spl_token_1 = require("@solana/spl-token");
23
+ const web3_js_1 = require("@solana/web3.js");
24
+ const bignumber_js_1 = require("bignumber.js");
25
+ const bs58_1 = __importDefault(require("bs58"));
26
+ const types_js_1 = require("../../types.js");
27
+ const common_js_1 = require("../../utils/common.js");
28
+ const logger_js_1 = require("../logger.js");
29
+ const solana_js_1 = require("./solana.js");
30
+ /**
31
+ * Funding by an **SPL token transfer on Solana**: an idempotent
32
+ * create-associated-token-account, a `transferChecked` to the recipient's ATA,
33
+ * and an optional `turboCreditDestinationAddress=` memo when the credit should
34
+ * land on a different Turbo account than the payer.
35
+ *
36
+ * This is the generic shape of what `ARIOToken` has always done — ARIO is an SPL
37
+ * token — so a second SPL token (USDC) is a mint and a decimals value, not a
38
+ * second implementation. The payment service verifies both with one class too
39
+ * (`SolanaSplGateway`).
40
+ *
41
+ * Two constraints worth knowing before pointing this at a third token:
42
+ * - The associated token accounts are derived for the CLASSIC SPL Token
43
+ * program (Tokenkeg), which is what ARIO and USDC use. A Token-2022 mint
44
+ * derives a different ATA, so it would need its own program id threading
45
+ * through `getAssociatedTokenAddressSync`. It fails loudly on-chain rather
46
+ * than silently paying the wrong account.
47
+ * - `decimals` must match the mint's own. `transferChecked` carries the value
48
+ * and the chain rejects a mismatch, so a wrong value fails the transaction
49
+ * instead of transferring a wrong amount.
50
+ */
51
+ class SplToken {
52
+ constructor({ mintAddress, decimals, tokenName = 'SPL', gatewayUrl = common_js_1.defaultProdGatewayUrls.solana, logger = logger_js_1.Logger.default, pollingOptions = {
53
+ maxAttempts: 10,
54
+ pollingIntervalMs: 2_500,
55
+ initialBackoffMs: 500,
56
+ }, }) {
57
+ this.gatewayUrl = gatewayUrl;
58
+ this.connection = new web3_js_1.Connection(gatewayUrl, 'confirmed');
59
+ this.pollingOptions = pollingOptions;
60
+ this.logger = logger;
61
+ this.mintAddress = mintAddress;
62
+ this.decimals = decimals;
63
+ this.tokenName = tokenName;
64
+ }
65
+ async createAndSubmitTx({ target, signer, tokenAmount, turboCreditDestinationAddress, }) {
66
+ const ownerPublicKey = new web3_js_1.PublicKey(bs58_1.default.encode(Uint8Array.from(await signer.getPublicKey())));
67
+ const recipient = new web3_js_1.PublicKey(target);
68
+ const mint = new web3_js_1.PublicKey(this.mintAddress);
69
+ const fromAta = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, ownerPublicKey);
70
+ const toAta = (0, spl_token_1.getAssociatedTokenAddressSync)(mint, recipient);
71
+ const tx = new web3_js_1.Transaction({
72
+ feePayer: ownerPublicKey,
73
+ ...(await this.connection.getLatestBlockhash()),
74
+ });
75
+ tx.add((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(ownerPublicKey, toAta, recipient, mint));
76
+ tx.add((0, spl_token_1.createTransferCheckedInstruction)(fromAta, mint, toAta, ownerPublicKey, BigInt(new bignumber_js_1.BigNumber(tokenAmount).toFixed(0)), this.decimals));
77
+ if (turboCreditDestinationAddress !== undefined) {
78
+ tx.add(new web3_js_1.TransactionInstruction({
79
+ programId: new web3_js_1.PublicKey(solana_js_1.memoProgramId),
80
+ keys: [],
81
+ data: Buffer.from('turboCreditDestinationAddress=' + turboCreditDestinationAddress),
82
+ }));
83
+ }
84
+ const { signedTx, signature } = signer.walletAdapter !== undefined &&
85
+ (0, types_js_1.isSolanaWalletAdapter)(signer.walletAdapter)
86
+ ? await this.signWithWallet(tx, signer.walletAdapter)
87
+ : await this.signWithKey(tx, ownerPublicKey, signer);
88
+ const txId = bs58_1.default.encode(signature);
89
+ await this.submitTx(signedTx, txId);
90
+ this.logger.debug(`Submitted ${this.tokenName} SPL transfer transaction...`, {
91
+ id: txId,
92
+ target,
93
+ tokenAmount,
94
+ fromAta: fromAta.toBase58(),
95
+ toAta: toAta.toBase58(),
96
+ mint: mint.toBase58(),
97
+ });
98
+ return { id: txId, target, reward: '0' };
99
+ }
100
+ /** Sign the message bytes directly: a private-key signer holds the key. */
101
+ async signWithKey(tx, ownerPublicKey, signer) {
102
+ const signature = await signer.signData(Uint8Array.from(tx.serializeMessage()));
103
+ tx.addSignature(ownerPublicKey, Buffer.from(signature));
104
+ return { signedTx: tx, signature };
105
+ }
106
+ /**
107
+ * Have a browser wallet sign the transaction itself, as the SOL path does.
108
+ *
109
+ * A wallet signs through `signTransaction`, not `signMessage`: it refuses, or
110
+ * cannot display, a serialized transaction handed to it as a message. It may
111
+ * also add instructions of its own before signing (compute budget, guards),
112
+ * so the transaction submitted is the one it returns, never ours with its
113
+ * signature attached. Deserializing drops `lastValidBlockHeight`, so that is
114
+ * carried over for confirmation.
115
+ */
116
+ async signWithWallet(tx, walletAdapter) {
117
+ const signedTx = await walletAdapter.signTransaction(tx);
118
+ const signature = signedTx.signature;
119
+ if (signature === null) {
120
+ throw new Error(`Wallet returned an unsigned ${this.tokenName} transfer transaction`);
121
+ }
122
+ signedTx.recentBlockhash ??= tx.recentBlockhash;
123
+ signedTx.lastValidBlockHeight ??= tx.lastValidBlockHeight;
124
+ return { signedTx, signature: Uint8Array.from(signature) };
125
+ }
126
+ async submitTx(tx, id) {
127
+ this.logger.debug(`Submitting ${this.tokenName} fund transaction...`, {
128
+ id,
129
+ });
130
+ await this.connection.sendRawTransaction(tx.serialize(), {
131
+ maxRetries: this.pollingOptions.maxAttempts,
132
+ });
133
+ if (tx.recentBlockhash === undefined ||
134
+ tx.lastValidBlockHeight === undefined) {
135
+ throw new Error('Failed to submit Transaction -- missing blockhash or lastValidBlockHeight from transaction creation. Solana Gateway Url:' +
136
+ this.gatewayUrl);
137
+ }
138
+ await this.connection.confirmTransaction({
139
+ signature: id,
140
+ blockhash: tx.recentBlockhash,
141
+ lastValidBlockHeight: tx.lastValidBlockHeight,
142
+ }, 'finalized');
143
+ }
144
+ async pollTxAvailability({ txId }) {
145
+ const { maxAttempts, pollingIntervalMs, initialBackoffMs } = this.pollingOptions;
146
+ this.logger.debug(`Polling for ${this.tokenName} SPL transaction...`, {
147
+ txId,
148
+ pollingOptions: this.pollingOptions,
149
+ gatewayUrl: this.gatewayUrl,
150
+ });
151
+ await (0, common_js_1.sleep)(initialBackoffMs);
152
+ let attempts = 0;
153
+ while (attempts < maxAttempts) {
154
+ let status;
155
+ attempts++;
156
+ try {
157
+ const statuses = await this.connection.getSignatureStatuses([txId], {
158
+ searchTransactionHistory: true,
159
+ });
160
+ status = {
161
+ context: statuses.context,
162
+ value: statuses.value[0],
163
+ };
164
+ }
165
+ catch (err) {
166
+ this.logger.debug(`Failed to poll ${this.tokenName} SPL transaction...`, { err });
167
+ }
168
+ if (status && status.value && status.value.err !== null) {
169
+ throw new Error(`Transaction failed: ${status.value.err}`);
170
+ }
171
+ if (status && status.value && status.value.slot !== null) {
172
+ this.logger.debug('Transaction found!', { txId, status });
173
+ return;
174
+ }
175
+ this.logger.debug(`${this.tokenName} SPL transaction not found, polling...`, {
176
+ txId,
177
+ attempts,
178
+ maxAttempts,
179
+ pollingIntervalMs,
180
+ });
181
+ await (0, common_js_1.sleep)(pollingIntervalMs);
182
+ }
183
+ throw new Error('Transaction not found after polling, transaction id: ' + txId);
184
+ }
185
+ }
186
+ exports.SplToken = SplToken;
@@ -249,6 +249,7 @@ class TurboAuthenticatedBaseUploadService extends TurboUnauthenticatedUploadServ
249
249
  'solana',
250
250
  'base-eth',
251
251
  'base-usdc',
252
+ 'solana-usdc',
252
253
  ];
253
254
  this.signer = signer;
254
255
  this.paymentService = paymentService;
package/lib/cjs/types.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.arNSFiatPurchaseMethods = exports.arNSActions = exports.arNSPurchaseIntents = exports.validChunkingModes = exports.isJWK = exports.isWebUploadFolderParams = exports.isNodeUploadFolderParams = exports.multipartFinalizedStatus = exports.multipartFailedStatus = exports.multipartPendingStatus = exports.X402Funding = exports.OnDemandFunding = exports.ExistingBalanceFunding = exports.supportedEvmSignerTokens = exports.tokenTypes = exports.fiatCurrencyTypes = void 0;
3
+ exports.arNSFiatPurchaseMethods = exports.arNSActions = exports.arNSPurchaseIntents = exports.validChunkingModes = exports.isJWK = exports.isWebUploadFolderParams = exports.isNodeUploadFolderParams = exports.multipartFinalizedStatus = exports.multipartFailedStatus = exports.multipartPendingStatus = exports.X402Funding = exports.OnDemandFunding = exports.ExistingBalanceFunding = exports.supportedEvmSignerTokens = exports.supportedSolanaSignerTokens = exports.tokenTypes = exports.fiatCurrencyTypes = void 0;
4
4
  exports.isCurrency = isCurrency;
5
5
  exports.isEthPrivateKey = isEthPrivateKey;
6
6
  exports.isSolanaWalletAdapter = isSolanaWalletAdapter;
@@ -32,7 +32,19 @@ exports.tokenTypes = [
32
32
  'usdc',
33
33
  'base-usdc',
34
34
  'polygon-usdc',
35
+ // USDC (SPL) on Solana — signed with the Solana signer, not an EVM one.
36
+ 'solana-usdc',
35
37
  ];
38
+ /**
39
+ * Tokens paid from a Solana wallet: SOL natively, and ARIO and USDC as SPL
40
+ * transfers signed by the same ed25519 key. A private key for any of these
41
+ * builds a Solana signer, so a Solana wallet adapter must as well.
42
+ */
43
+ exports.supportedSolanaSignerTokens = new Set([
44
+ 'solana',
45
+ 'ario',
46
+ 'solana-usdc',
47
+ ]);
36
48
  exports.supportedEvmSignerTokens = new Set([
37
49
  'ethereum',
38
50
  'base-eth',
@@ -50,6 +50,7 @@ exports.tokenToDevGatewayMap = {
50
50
  usdc: ethTestnetRpc,
51
51
  'base-usdc': baseTestnetRpc,
52
52
  'polygon-usdc': polygonTestnetRpc,
53
+ 'solana-usdc': 'https://api.devnet.solana.com',
53
54
  };
54
55
  exports.defaultProdGatewayUrls = {
55
56
  arweave: 'https://turbo-gateway.com',
@@ -62,6 +63,7 @@ exports.defaultProdGatewayUrls = {
62
63
  usdc: 'https://cloudflare-eth.com/',
63
64
  'base-usdc': baseMainnetRpc,
64
65
  'polygon-usdc': 'https://polygon-rpc.com/',
66
+ 'solana-usdc': 'https://api.mainnet-beta.solana.com',
65
67
  };
66
68
  function createTurboSigner({ signer: clientProvidedSigner, privateKey: clientProvidedPrivateKey, token = 'arweave', }) {
67
69
  if (clientProvidedSigner !== undefined) {
@@ -83,6 +85,7 @@ function createTurboSigner({ signer: clientProvidedSigner, privateKey: clientPro
83
85
  switch (token) {
84
86
  case 'solana':
85
87
  case 'ario':
88
+ case 'solana-usdc':
86
89
  return new arbundles_1.HexSolanaSigner(clientProvidedPrivateKey);
87
90
  case 'ethereum':
88
91
  case 'pol':
@@ -128,6 +131,7 @@ function isValidUserAddress(address, type) {
128
131
  return isValidArweaveBase64URL(address);
129
132
  case 'ario':
130
133
  case 'solana':
134
+ case 'solana-usdc':
131
135
  return isValidSolanaAddress(address);
132
136
  case 'ethereum':
133
137
  case 'base-eth':
@@ -17,4 +17,4 @@
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.version = void 0;
19
19
  // AUTOMATICALLY GENERATED FILE - DO NOT TOUCH
20
- exports.version = '2.0.0';
20
+ exports.version = '2.1.0-alpha.2';
@@ -91,7 +91,7 @@ export async function optionalPrivateKeyFromOptions(options) {
91
91
  export async function privateKeyFromOptions({ privateKey, walletFile, token, }) {
92
92
  if (walletFile !== undefined) {
93
93
  const wallet = JSON.parse(readFileSync(walletFile, 'utf-8'));
94
- return token === 'solana' || token === 'ario'
94
+ return token === 'solana' || token === 'ario' || token === 'solana-usdc'
95
95
  ? bs58.encode(wallet)
96
96
  : wallet;
97
97
  }
@@ -14,7 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import { HexInjectedSolanaSigner, InjectedEthereumSigner, SignatureConfig, } from '@dha-team/arbundles';
17
- import { isEthereumWalletAdapter, isSolanaWalletAdapter, supportedEvmSignerTokens, } from '../types.js';
17
+ import { isEthereumWalletAdapter, isSolanaWalletAdapter, supportedEvmSignerTokens, supportedSolanaSignerTokens, } from '../types.js';
18
18
  import { Logger } from './logger.js';
19
19
  import { TurboAuthenticatedPaymentService, TurboUnauthenticatedPaymentService, } from './payment.js';
20
20
  import { defaultTokenMap } from './token/index.js';
@@ -123,7 +123,7 @@ export class TurboBaseFactory {
123
123
  });
124
124
  }
125
125
  signerFromAdapter(walletAdapter, token) {
126
- if (token === 'solana') {
126
+ if (supportedSolanaSignerTokens.has(token)) {
127
127
  if (!isSolanaWalletAdapter(walletAdapter)) {
128
128
  throw new Error('Unsupported wallet adapter -- must implement publicKey and signMessage');
129
129
  }
@@ -41,7 +41,11 @@ export class TurboDataItemAbstractSigner {
41
41
  }
42
42
  ownerToNativeAddress(owner, token) {
43
43
  switch (token) {
44
+ // `solana-usdc` is an ordinary Solana wallet paying with an SPL token,
45
+ // and the payment service credits the base58 sender address it reads off
46
+ // the transfer — so its native address is the same bs58 form as `solana`.
44
47
  case 'solana':
48
+ case 'solana-usdc':
45
49
  return bs58.encode(Uint8Array.from(fromB64Url(owner)));
46
50
  case 'ethereum':
47
51
  case 'matic':