@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.
- package/lib/cjs/cli/utils.js +1 -1
- package/lib/cjs/common/factory.js +1 -1
- package/lib/cjs/common/signer.js +4 -0
- package/lib/cjs/common/token/ario.js +11 -114
- package/lib/cjs/common/token/index.js +6 -0
- package/lib/cjs/common/token/solanaUsdc.js +29 -0
- package/lib/cjs/common/token/spl.js +186 -0
- package/lib/cjs/common/upload.js +1 -0
- package/lib/cjs/types.js +13 -1
- package/lib/cjs/utils/common.js +4 -0
- package/lib/cjs/version.js +1 -1
- package/lib/esm/cli/utils.js +1 -1
- package/lib/esm/common/factory.js +2 -2
- package/lib/esm/common/signer.js +4 -0
- package/lib/esm/common/token/ario.js +12 -112
- package/lib/esm/common/token/index.js +6 -0
- package/lib/esm/common/token/solanaUsdc.js +25 -0
- package/lib/esm/common/token/spl.js +179 -0
- package/lib/esm/common/upload.js +1 -0
- package/lib/esm/types.js +12 -0
- package/lib/esm/utils/common.js +4 -0
- package/lib/esm/version.js +1 -1
- package/lib/types/common/factory.d.ts.map +1 -1
- package/lib/types/common/signer.d.ts.map +1 -1
- package/lib/types/common/token/ario.d.ts +19 -22
- package/lib/types/common/token/ario.d.ts.map +1 -1
- package/lib/types/common/token/index.d.ts +2 -0
- package/lib/types/common/token/index.d.ts.map +1 -1
- package/lib/types/common/token/solanaUsdc.d.ts +40 -0
- package/lib/types/common/token/solanaUsdc.d.ts.map +1 -0
- package/lib/types/common/token/spl.d.ts +64 -0
- package/lib/types/common/token/spl.d.ts.map +1 -0
- package/lib/types/common/upload.d.ts.map +1 -1
- package/lib/types/types.d.ts +7 -1
- package/lib/types/types.d.ts.map +1 -1
- package/lib/types/utils/common.d.ts.map +1 -1
- package/lib/types/version.d.ts +1 -1
- package/lib/types/version.d.ts.map +1 -1
- package/package.json +2 -1
package/lib/cjs/cli/utils.js
CHANGED
|
@@ -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
|
|
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
|
}
|
package/lib/cjs/common/signer.js
CHANGED
|
@@ -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
|
|
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.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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;
|
package/lib/cjs/common/upload.js
CHANGED
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',
|
package/lib/cjs/utils/common.js
CHANGED
|
@@ -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':
|
package/lib/cjs/version.js
CHANGED
package/lib/esm/cli/utils.js
CHANGED
|
@@ -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
|
|
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
|
}
|
package/lib/esm/common/signer.js
CHANGED
|
@@ -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':
|