@ardrive/turbo-sdk 1.4.2 → 1.5.0-alpha.1
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/README.md +27 -1
- package/bundles/web.bundle.min.js +4116 -348
- package/lib/cjs/common/index.js +1 -0
- package/lib/cjs/common/payment.js +104 -3
- package/lib/cjs/common/signer.js +45 -0
- package/lib/cjs/common/token.js +132 -0
- package/lib/cjs/common/turbo.js +13 -0
- package/lib/cjs/node/factory.js +2 -1
- package/lib/cjs/node/signer.js +4 -17
- package/lib/cjs/types.js +2 -0
- package/lib/cjs/version.js +1 -1
- package/lib/cjs/web/factory.js +3 -1
- package/lib/cjs/web/signer.js +22 -26
- package/lib/esm/common/index.js +1 -0
- package/lib/esm/common/payment.js +104 -3
- package/lib/esm/common/signer.js +41 -0
- package/lib/esm/common/token.js +123 -0
- package/lib/esm/common/turbo.js +13 -0
- package/lib/esm/node/factory.js +2 -1
- package/lib/esm/node/signer.js +5 -18
- package/lib/esm/types.js +1 -1
- package/lib/esm/version.js +1 -1
- package/lib/esm/web/factory.js +3 -1
- package/lib/esm/web/signer.js +22 -26
- package/lib/types/common/index.d.ts +1 -0
- package/lib/types/common/index.d.ts.map +1 -1
- package/lib/types/common/payment.d.ts +10 -19
- package/lib/types/common/payment.d.ts.map +1 -1
- package/lib/types/common/signer.d.ts +16 -0
- package/lib/types/common/signer.d.ts.map +1 -0
- package/lib/types/common/token.d.ts +56 -0
- package/lib/types/common/token.d.ts.map +1 -0
- package/lib/types/common/turbo.d.ts +12 -1
- package/lib/types/common/turbo.d.ts.map +1 -1
- package/lib/types/node/factory.d.ts +1 -1
- package/lib/types/node/factory.d.ts.map +1 -1
- package/lib/types/node/signer.d.ts +4 -15
- package/lib/types/node/signer.d.ts.map +1 -1
- package/lib/types/types.d.ts +108 -5
- package/lib/types/types.d.ts.map +1 -1
- package/lib/types/version.d.ts +1 -1
- package/lib/types/web/factory.d.ts +1 -1
- package/lib/types/web/factory.d.ts.map +1 -1
- package/lib/types/web/signer.d.ts +8 -15
- package/lib/types/web/signer.d.ts.map +1 -1
- package/package.json +6 -3
package/lib/cjs/common/index.js
CHANGED
@@ -1,18 +1,37 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
3
|
exports.TurboAuthenticatedPaymentService = exports.TurboUnauthenticatedPaymentService = exports.defaultPaymentServiceURL = exports.developmentPaymentServiceURL = void 0;
|
4
|
+
/**
|
5
|
+
* Copyright (C) 2022-2023 Permanent Data Solutions, Inc. All Rights Reserved.
|
6
|
+
*
|
7
|
+
* This program is free software: you can redistribute it and/or modify
|
8
|
+
* it under the terms of the GNU Affero General Public License as published by
|
9
|
+
* the Free Software Foundation, either version 3 of the License, or
|
10
|
+
* (at your option) any later version.
|
11
|
+
*
|
12
|
+
* This program is distributed in the hope that it will be useful,
|
13
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
* GNU Affero General Public License for more details.
|
16
|
+
*
|
17
|
+
* You should have received a copy of the GNU Affero General Public License
|
18
|
+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
*/
|
20
|
+
const bignumber_js_1 = require("bignumber.js");
|
4
21
|
const http_js_1 = require("./http.js");
|
5
22
|
const logger_js_1 = require("./logger.js");
|
23
|
+
const token_js_1 = require("./token.js");
|
6
24
|
exports.developmentPaymentServiceURL = 'https://payment.ardrive.dev';
|
7
25
|
exports.defaultPaymentServiceURL = 'https://payment.ardrive.io';
|
8
26
|
class TurboUnauthenticatedPaymentService {
|
9
|
-
constructor({ url = exports.defaultPaymentServiceURL, retryConfig, logger = new logger_js_1.TurboWinstonLogger(), }) {
|
27
|
+
constructor({ url = exports.defaultPaymentServiceURL, retryConfig, logger = new logger_js_1.TurboWinstonLogger(), token = 'arweave', }) {
|
10
28
|
this.logger = logger;
|
11
29
|
this.httpService = new http_js_1.TurboHTTPService({
|
12
30
|
url: `${url}/v1`,
|
13
31
|
retryConfig,
|
14
32
|
logger: this.logger,
|
15
33
|
});
|
34
|
+
this.token = token;
|
16
35
|
}
|
17
36
|
getFiatRates() {
|
18
37
|
return this.httpService.get({
|
@@ -73,13 +92,56 @@ class TurboUnauthenticatedPaymentService {
|
|
73
92
|
createCheckoutSession(params) {
|
74
93
|
return this.getCheckout(params);
|
75
94
|
}
|
95
|
+
async submitFundTransaction({ txId, }) {
|
96
|
+
const response = await this.httpService.post({
|
97
|
+
endpoint: `/account/balance/${this.token}`,
|
98
|
+
data: Buffer.from(JSON.stringify({ tx_id: txId })),
|
99
|
+
});
|
100
|
+
if ('creditedTransaction' in response) {
|
101
|
+
return {
|
102
|
+
id: response.creditedTransaction.transactionId,
|
103
|
+
quantity: response.creditedTransaction.transactionQuantity,
|
104
|
+
owner: response.creditedTransaction.destinationAddress,
|
105
|
+
winc: response.creditedTransaction.winstonCreditAmount,
|
106
|
+
token: response.creditedTransaction.tokenType,
|
107
|
+
status: 'confirmed',
|
108
|
+
block: response.creditedTransaction.blockHeight,
|
109
|
+
};
|
110
|
+
}
|
111
|
+
else if ('pendingTransaction' in response) {
|
112
|
+
return {
|
113
|
+
id: response.pendingTransaction.transactionId,
|
114
|
+
quantity: response.pendingTransaction.transactionQuantity,
|
115
|
+
owner: response.pendingTransaction.destinationAddress,
|
116
|
+
winc: response.pendingTransaction.winstonCreditAmount,
|
117
|
+
token: response.pendingTransaction.tokenType,
|
118
|
+
status: 'pending',
|
119
|
+
};
|
120
|
+
}
|
121
|
+
else if ('failedTransaction' in response) {
|
122
|
+
return {
|
123
|
+
id: response.failedTransaction.transactionId,
|
124
|
+
quantity: response.failedTransaction.transactionQuantity,
|
125
|
+
owner: response.failedTransaction.destinationAddress,
|
126
|
+
winc: response.failedTransaction.winstonCreditAmount,
|
127
|
+
token: response.failedTransaction.tokenType,
|
128
|
+
status: 'failed',
|
129
|
+
};
|
130
|
+
}
|
131
|
+
throw new Error('Unknown response from payment service: ' + response);
|
132
|
+
}
|
76
133
|
}
|
77
134
|
exports.TurboUnauthenticatedPaymentService = TurboUnauthenticatedPaymentService;
|
78
135
|
// NOTE: to avoid redundancy, we use inheritance here - but generally prefer composition over inheritance
|
79
136
|
class TurboAuthenticatedPaymentService extends TurboUnauthenticatedPaymentService {
|
80
|
-
constructor({ url = exports.defaultPaymentServiceURL, retryConfig, signer, logger
|
81
|
-
|
137
|
+
constructor({ url = exports.defaultPaymentServiceURL, retryConfig, signer, logger = new logger_js_1.TurboWinstonLogger(), token = 'arweave', tokenMap = {
|
138
|
+
arweave: new token_js_1.ArweaveToken({
|
139
|
+
logger,
|
140
|
+
}),
|
141
|
+
}, }) {
|
142
|
+
super({ url, retryConfig, logger, token });
|
82
143
|
this.signer = signer;
|
144
|
+
this.tokenMap = tokenMap;
|
83
145
|
}
|
84
146
|
async getBalance() {
|
85
147
|
const headers = await this.signer.generateSignedRequestHeaders();
|
@@ -100,5 +162,44 @@ class TurboAuthenticatedPaymentService extends TurboUnauthenticatedPaymentServic
|
|
100
162
|
async createCheckoutSession(params) {
|
101
163
|
return this.getCheckout(params, await this.signer.generateSignedRequestHeaders());
|
102
164
|
}
|
165
|
+
async getTargetWalletForFund() {
|
166
|
+
const { addresses } = await this.httpService.get({
|
167
|
+
endpoint: '/info',
|
168
|
+
});
|
169
|
+
const walletAddress = addresses[this.token];
|
170
|
+
if (!walletAddress) {
|
171
|
+
throw new Error(`No wallet address found for token type: ${this.token}`);
|
172
|
+
}
|
173
|
+
return walletAddress;
|
174
|
+
}
|
175
|
+
async topUpWithTokens({ feeMultiplier = 1, tokenAmount: tokenAmountV, }) {
|
176
|
+
this.logger.debug('Funding account...');
|
177
|
+
const tokenAmount = new bignumber_js_1.BigNumber(tokenAmountV);
|
178
|
+
const target = await this.getTargetWalletForFund();
|
179
|
+
const fundTx = await this.tokenMap[this.token].createTx({
|
180
|
+
target,
|
181
|
+
tokenAmount,
|
182
|
+
feeMultiplier,
|
183
|
+
});
|
184
|
+
const signedTx = await this.tokenMap[this.token].signTx({
|
185
|
+
tx: fundTx,
|
186
|
+
signer: this.signer,
|
187
|
+
});
|
188
|
+
await this.tokenMap[this.token].submitTx({ tx: signedTx });
|
189
|
+
const txId = signedTx.id;
|
190
|
+
try {
|
191
|
+
// Let transaction settle some time
|
192
|
+
await this.tokenMap[this.token].pollForTxBeingAvailable({ txId });
|
193
|
+
return {
|
194
|
+
...(await this.submitFundTransaction({ txId })),
|
195
|
+
target: signedTx.target,
|
196
|
+
reward: signedTx.reward,
|
197
|
+
};
|
198
|
+
}
|
199
|
+
catch (e) {
|
200
|
+
this.logger.error('Failed to submit fund transaction...', e);
|
201
|
+
throw Error(`Failed to submit fund transaction! Save this Transaction ID and try again with 'turbo.submitFundTransaction(id)': ${txId}`);
|
202
|
+
}
|
203
|
+
}
|
103
204
|
}
|
104
205
|
exports.TurboAuthenticatedPaymentService = TurboAuthenticatedPaymentService;
|
@@ -0,0 +1,45 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.TurboDataItemAbstractSigner = void 0;
|
4
|
+
/**
|
5
|
+
* Copyright (C) 2022-2023 Permanent Data Solutions, Inc. All Rights Reserved.
|
6
|
+
*
|
7
|
+
* This program is free software: you can redistribute it and/or modify
|
8
|
+
* it under the terms of the GNU Affero General Public License as published by
|
9
|
+
* the Free Software Foundation, either version 3 of the License, or
|
10
|
+
* (at your option) any later version.
|
11
|
+
*
|
12
|
+
* This program is distributed in the hope that it will be useful,
|
13
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
* GNU Affero General Public License for more details.
|
16
|
+
*
|
17
|
+
* You should have received a copy of the GNU Affero General Public License
|
18
|
+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
*/
|
20
|
+
const crypto_1 = require("crypto");
|
21
|
+
const base64_js_1 = require("../utils/base64.js");
|
22
|
+
class TurboDataItemAbstractSigner {
|
23
|
+
constructor({ signer, logger }) {
|
24
|
+
this.logger = logger;
|
25
|
+
this.signer = signer;
|
26
|
+
}
|
27
|
+
async generateSignedRequestHeaders() {
|
28
|
+
const nonce = (0, crypto_1.randomBytes)(16).toString('hex');
|
29
|
+
const buffer = Buffer.from(nonce);
|
30
|
+
const signature = await this.signer.sign(buffer);
|
31
|
+
const publicKey = (0, base64_js_1.toB64Url)(this.signer.publicKey);
|
32
|
+
return {
|
33
|
+
'x-public-key': publicKey,
|
34
|
+
'x-nonce': nonce,
|
35
|
+
'x-signature': (0, base64_js_1.toB64Url)(Buffer.from(signature)),
|
36
|
+
};
|
37
|
+
}
|
38
|
+
async getPublicKey() {
|
39
|
+
return this.signer.publicKey;
|
40
|
+
}
|
41
|
+
async signData(dataToSign) {
|
42
|
+
return this.signer.sign(dataToSign);
|
43
|
+
}
|
44
|
+
}
|
45
|
+
exports.TurboDataItemAbstractSigner = TurboDataItemAbstractSigner;
|
@@ -0,0 +1,132 @@
|
|
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.ARToTokenAmount = exports.WinstonToTokenAmount = exports.ArweaveToken = void 0;
|
7
|
+
/**
|
8
|
+
* Copyright (C) 2022-2023 Permanent Data Solutions, Inc. All Rights Reserved.
|
9
|
+
*
|
10
|
+
* This program is free software: you can redistribute it and/or modify
|
11
|
+
* it under the terms of the GNU Affero General Public License as published by
|
12
|
+
* the Free Software Foundation, either version 3 of the License, or
|
13
|
+
* (at your option) any later version.
|
14
|
+
*
|
15
|
+
* This program is distributed in the hope that it will be useful,
|
16
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
17
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
18
|
+
* GNU Affero General Public License for more details.
|
19
|
+
*
|
20
|
+
* You should have received a copy of the GNU Affero General Public License
|
21
|
+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
22
|
+
*/
|
23
|
+
const arweave_1 = __importDefault(require("arweave"));
|
24
|
+
const bignumber_js_1 = require("bignumber.js");
|
25
|
+
const base64_js_1 = require("../utils/base64.js");
|
26
|
+
const logger_js_1 = require("./logger.js");
|
27
|
+
class ArweaveToken {
|
28
|
+
constructor({ arweave = arweave_1.default.init({
|
29
|
+
host: 'arweave.net',
|
30
|
+
port: 443,
|
31
|
+
protocol: 'https',
|
32
|
+
}), logger = new logger_js_1.TurboWinstonLogger(), mintU = true, pollingOptions = {
|
33
|
+
maxAttempts: 10,
|
34
|
+
pollingIntervalMs: 3000,
|
35
|
+
initialBackoffMs: 7000,
|
36
|
+
}, }) {
|
37
|
+
this.arweave = arweave;
|
38
|
+
this.logger = logger;
|
39
|
+
this.mintU = mintU;
|
40
|
+
this.pollingOptions = pollingOptions;
|
41
|
+
}
|
42
|
+
async createTx({ feeMultiplier, target, tokenAmount, }) {
|
43
|
+
const tx = await this.arweave.createTransaction({
|
44
|
+
target,
|
45
|
+
quantity: tokenAmount.toString(),
|
46
|
+
data: '',
|
47
|
+
});
|
48
|
+
if (feeMultiplier !== 1) {
|
49
|
+
tx.reward = (0, bignumber_js_1.BigNumber)(tx.reward)
|
50
|
+
.times((0, bignumber_js_1.BigNumber)(feeMultiplier))
|
51
|
+
.toFixed(0, bignumber_js_1.BigNumber.ROUND_UP);
|
52
|
+
}
|
53
|
+
if (this.mintU) {
|
54
|
+
tx.addTag('App-Name', 'SmartWeaveAction');
|
55
|
+
tx.addTag('App-Version', '0.3.0'); // cspell:disable
|
56
|
+
tx.addTag('Contract', 'KTzTXT_ANmF84fWEKHzWURD1LWd9QaFR9yfYUwH2Lxw'); // cspell:enable
|
57
|
+
tx.addTag('Input', JSON.stringify({ function: 'mint' }));
|
58
|
+
}
|
59
|
+
return tx;
|
60
|
+
}
|
61
|
+
async signTx({ tx, signer, }) {
|
62
|
+
const publicKeyB64Url = (0, base64_js_1.toB64Url)(await signer.getPublicKey());
|
63
|
+
tx.setOwner(publicKeyB64Url);
|
64
|
+
const dataToSign = await tx.getSignatureData();
|
65
|
+
const signatureBuffer = Buffer.from(await signer.signData(dataToSign));
|
66
|
+
const id = (0, base64_js_1.sha256B64Url)(signatureBuffer);
|
67
|
+
tx.setSignature({
|
68
|
+
id: id,
|
69
|
+
owner: publicKeyB64Url,
|
70
|
+
signature: (0, base64_js_1.toB64Url)(signatureBuffer),
|
71
|
+
});
|
72
|
+
return tx;
|
73
|
+
}
|
74
|
+
async pollForTxBeingAvailable({ txId, }) {
|
75
|
+
const { maxAttempts, pollingIntervalMs, initialBackoffMs } = this.pollingOptions;
|
76
|
+
await new Promise((resolve) => setTimeout(resolve, initialBackoffMs));
|
77
|
+
let attempts = 0;
|
78
|
+
while (attempts < maxAttempts) {
|
79
|
+
const response = await this.arweave.api
|
80
|
+
.post('/graphql', {
|
81
|
+
query: `
|
82
|
+
query {
|
83
|
+
transaction(id: "${txId}") {
|
84
|
+
recipient
|
85
|
+
owner {
|
86
|
+
address
|
87
|
+
}
|
88
|
+
quantity {
|
89
|
+
winston
|
90
|
+
}
|
91
|
+
}
|
92
|
+
}
|
93
|
+
`,
|
94
|
+
})
|
95
|
+
.catch((err) => {
|
96
|
+
// Continue retries when request errors
|
97
|
+
this.logger.error('Failed to poll for transaction...', { err });
|
98
|
+
return undefined;
|
99
|
+
});
|
100
|
+
const transaction = response?.data?.data?.transaction;
|
101
|
+
if (transaction) {
|
102
|
+
return;
|
103
|
+
}
|
104
|
+
attempts++;
|
105
|
+
this.logger.debug('Transaction not found after polling...', {
|
106
|
+
txId,
|
107
|
+
attempts,
|
108
|
+
});
|
109
|
+
await new Promise((resolve) => setTimeout(resolve, pollingIntervalMs));
|
110
|
+
}
|
111
|
+
throw new Error('Transaction not found after polling, transaction id: ' + txId);
|
112
|
+
}
|
113
|
+
async submitTx({ tx }) {
|
114
|
+
const response = await this.arweave.transactions.post(tx).catch((err) => {
|
115
|
+
this.logger.error('Failed to post transaction...', { err });
|
116
|
+
return {
|
117
|
+
status: 500,
|
118
|
+
statusText: err instanceof Error ? err.message : 'Failed to post Arweave Tx!',
|
119
|
+
};
|
120
|
+
});
|
121
|
+
if (response.status !== 200) {
|
122
|
+
throw new Error('Failed to post transaction -- ' +
|
123
|
+
`Status ${response.status}, ${response.statusText}`);
|
124
|
+
}
|
125
|
+
this.logger.debug('Posted transaction...', { tx });
|
126
|
+
}
|
127
|
+
}
|
128
|
+
exports.ArweaveToken = ArweaveToken;
|
129
|
+
const WinstonToTokenAmount = (winston) => winston;
|
130
|
+
exports.WinstonToTokenAmount = WinstonToTokenAmount;
|
131
|
+
const ARToTokenAmount = (ar) => new bignumber_js_1.BigNumber(ar).times(1e12).valueOf();
|
132
|
+
exports.ARToTokenAmount = ARToTokenAmount;
|
package/lib/cjs/common/turbo.js
CHANGED
@@ -85,6 +85,12 @@ class TurboUnauthenticatedClient {
|
|
85
85
|
createCheckoutSession(params) {
|
86
86
|
return this.paymentService.createCheckoutSession(params);
|
87
87
|
}
|
88
|
+
/**
|
89
|
+
* Submits a transaction ID to the Turbo Payment Service for processing.
|
90
|
+
*/
|
91
|
+
submitFundTransaction(p) {
|
92
|
+
return this.paymentService.submitFundTransaction(p);
|
93
|
+
}
|
88
94
|
}
|
89
95
|
exports.TurboUnauthenticatedClient = TurboUnauthenticatedClient;
|
90
96
|
class TurboAuthenticatedClient extends TurboUnauthenticatedClient {
|
@@ -108,5 +114,12 @@ class TurboAuthenticatedClient extends TurboUnauthenticatedClient {
|
|
108
114
|
dataItemOpts,
|
109
115
|
});
|
110
116
|
}
|
117
|
+
/**
|
118
|
+
* Submits fund transaction to the token's blockchain then sends
|
119
|
+
* the transaction ID to the Turbo Payment Service for processing.
|
120
|
+
*/
|
121
|
+
topUpWithTokens(p) {
|
122
|
+
return this.paymentService.topUpWithTokens(p);
|
123
|
+
}
|
111
124
|
}
|
112
125
|
exports.TurboAuthenticatedClient = TurboAuthenticatedClient;
|
package/lib/cjs/node/factory.js
CHANGED
@@ -22,7 +22,7 @@ const factory_js_1 = require("../common/factory.js");
|
|
22
22
|
const index_js_1 = require("../common/index.js");
|
23
23
|
const signer_js_1 = require("./signer.js");
|
24
24
|
class TurboFactory extends factory_js_1.TurboBaseFactory {
|
25
|
-
static authenticated({ privateKey, signer: providedSigner, paymentServiceConfig = {}, uploadServiceConfig = {}, }) {
|
25
|
+
static authenticated({ privateKey, signer: providedSigner, paymentServiceConfig = {}, uploadServiceConfig = {}, tokenMap, }) {
|
26
26
|
let signer;
|
27
27
|
if (providedSigner) {
|
28
28
|
signer = providedSigner;
|
@@ -41,6 +41,7 @@ class TurboFactory extends factory_js_1.TurboBaseFactory {
|
|
41
41
|
...paymentServiceConfig,
|
42
42
|
signer: turboSigner,
|
43
43
|
logger: this.logger,
|
44
|
+
tokenMap,
|
44
45
|
});
|
45
46
|
const uploadService = new index_js_1.TurboAuthenticatedUploadService({
|
46
47
|
...uploadServiceConfig,
|
package/lib/cjs/node/signer.js
CHANGED
@@ -18,12 +18,11 @@ exports.TurboNodeArweaveSigner = void 0;
|
|
18
18
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
19
19
|
*/
|
20
20
|
const arbundles_1 = require("arbundles");
|
21
|
-
const
|
21
|
+
const signer_js_1 = require("../common/signer.js");
|
22
22
|
const base64_js_1 = require("../utils/base64.js");
|
23
|
-
class TurboNodeArweaveSigner {
|
24
|
-
constructor(
|
25
|
-
|
26
|
-
this.signer = signer;
|
23
|
+
class TurboNodeArweaveSigner extends signer_js_1.TurboDataItemAbstractSigner {
|
24
|
+
constructor(p) {
|
25
|
+
super(p);
|
27
26
|
}
|
28
27
|
async signDataItem({ fileStreamFactory, fileSizeFactory, dataItemOpts, }) {
|
29
28
|
// TODO: replace with our own signer implementation
|
@@ -41,18 +40,6 @@ class TurboNodeArweaveSigner {
|
|
41
40
|
dataItemSizeFactory: () => signedDataItemSize,
|
42
41
|
};
|
43
42
|
}
|
44
|
-
// NOTE: this might be better in a parent class or elsewhere - easy enough to leave in here now and does require specific environment version of crypto
|
45
|
-
async generateSignedRequestHeaders() {
|
46
|
-
const nonce = (0, node_crypto_1.randomBytes)(16).toString('hex');
|
47
|
-
const buffer = Buffer.from(nonce);
|
48
|
-
const signature = await this.signer.sign(buffer);
|
49
|
-
const publicKey = (0, base64_js_1.toB64Url)(this.signer.publicKey);
|
50
|
-
return {
|
51
|
-
'x-public-key': publicKey,
|
52
|
-
'x-nonce': nonce,
|
53
|
-
'x-signature': (0, base64_js_1.toB64Url)(Buffer.from(signature)),
|
54
|
-
};
|
55
|
-
}
|
56
43
|
// TODO: make dynamic that accepts anchor and target and tags to return the size of the headers + data
|
57
44
|
// reference https://github.com/ArweaveTeam/arweave-standards/blob/master/ans/ANS-104.md#13-dataitem-format
|
58
45
|
calculateSignedDataHeadersSize({ dataSize, dataItemOpts, }) {
|
package/lib/cjs/types.js
CHANGED
package/lib/cjs/version.js
CHANGED
package/lib/cjs/web/factory.js
CHANGED
@@ -22,7 +22,7 @@ const factory_js_1 = require("../common/factory.js");
|
|
22
22
|
const index_js_1 = require("../common/index.js");
|
23
23
|
const signer_js_1 = require("./signer.js");
|
24
24
|
class TurboFactory extends factory_js_1.TurboBaseFactory {
|
25
|
-
static authenticated({ privateKey, signer: providedSigner, paymentServiceConfig = {}, uploadServiceConfig = {}, }) {
|
25
|
+
static authenticated({ privateKey, signer: providedSigner, paymentServiceConfig = {}, uploadServiceConfig = {}, tokenMap, token, }) {
|
26
26
|
let signer;
|
27
27
|
if (providedSigner) {
|
28
28
|
signer = providedSigner;
|
@@ -41,6 +41,8 @@ class TurboFactory extends factory_js_1.TurboBaseFactory {
|
|
41
41
|
...paymentServiceConfig,
|
42
42
|
signer: turboSigner,
|
43
43
|
logger: this.logger,
|
44
|
+
tokenMap,
|
45
|
+
token,
|
44
46
|
});
|
45
47
|
const uploadService = new index_js_1.TurboAuthenticatedUploadService({
|
46
48
|
...uploadServiceConfig,
|
package/lib/cjs/web/signer.js
CHANGED
@@ -18,25 +18,30 @@ exports.TurboWebArweaveSigner = void 0;
|
|
18
18
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
19
19
|
*/
|
20
20
|
const arbundles_1 = require("arbundles");
|
21
|
-
const
|
22
|
-
const base64_js_1 = require("../utils/base64.js");
|
21
|
+
const signer_js_1 = require("../common/signer.js");
|
23
22
|
const readableStream_js_1 = require("../utils/readableStream.js");
|
24
|
-
class TurboWebArweaveSigner {
|
25
|
-
constructor(
|
26
|
-
|
27
|
-
|
23
|
+
class TurboWebArweaveSigner extends signer_js_1.TurboDataItemAbstractSigner {
|
24
|
+
constructor(p) {
|
25
|
+
super(p);
|
26
|
+
}
|
27
|
+
async setPublicKey() {
|
28
|
+
// for arconnect, we need to make sure we have the public key before create data
|
29
|
+
if (this.signer.publicKey === undefined &&
|
30
|
+
this.signer instanceof arbundles_1.ArconnectSigner) {
|
31
|
+
await this.signer.setPublicKey();
|
32
|
+
}
|
33
|
+
}
|
34
|
+
async getPublicKey() {
|
35
|
+
await this.setPublicKey();
|
36
|
+
return super.getPublicKey();
|
28
37
|
}
|
29
38
|
async signDataItem({ fileStreamFactory, fileSizeFactory, dataItemOpts, }) {
|
39
|
+
await this.setPublicKey();
|
30
40
|
// TODO: converts the readable stream to a buffer bc incrementally signing ReadableStreams is not trivial
|
31
41
|
const buffer = await (0, readableStream_js_1.readableStreamToBuffer)({
|
32
42
|
stream: fileStreamFactory(),
|
33
43
|
size: fileSizeFactory(),
|
34
44
|
});
|
35
|
-
// for arconnect, we need to make sure we have the public key before create data
|
36
|
-
if (this.signer.publicKey === undefined &&
|
37
|
-
this.signer instanceof arbundles_1.ArconnectSigner) {
|
38
|
-
await this.signer.setPublicKey();
|
39
|
-
}
|
40
45
|
this.logger.debug('Signing data item...');
|
41
46
|
const signedDataItem = (0, arbundles_1.createData)(buffer, this.signer, dataItemOpts);
|
42
47
|
await signedDataItem.sign(this.signer);
|
@@ -47,22 +52,13 @@ class TurboWebArweaveSigner {
|
|
47
52
|
dataItemSizeFactory: () => signedDataItem.getRaw().length,
|
48
53
|
};
|
49
54
|
}
|
50
|
-
// NOTE: this might be better in a parent class or elsewhere - easy enough to leave in here now and does require specific environment version of crypto
|
51
55
|
async generateSignedRequestHeaders() {
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
await this.signer.setPublicKey();
|
59
|
-
}
|
60
|
-
const publicKey = (0, base64_js_1.toB64Url)(this.signer.publicKey);
|
61
|
-
return {
|
62
|
-
'x-public-key': publicKey,
|
63
|
-
'x-nonce': nonce,
|
64
|
-
'x-signature': (0, base64_js_1.toB64Url)(Buffer.from(signature)),
|
65
|
-
};
|
56
|
+
await this.setPublicKey();
|
57
|
+
return super.generateSignedRequestHeaders();
|
58
|
+
}
|
59
|
+
async signData(dataToSign) {
|
60
|
+
await this.setPublicKey();
|
61
|
+
return super.signData(dataToSign);
|
66
62
|
}
|
67
63
|
}
|
68
64
|
exports.TurboWebArweaveSigner = TurboWebArweaveSigner;
|
package/lib/esm/common/index.js
CHANGED
@@ -1,15 +1,34 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright (C) 2022-2023 Permanent Data Solutions, Inc. All Rights Reserved.
|
3
|
+
*
|
4
|
+
* This program is free software: you can redistribute it and/or modify
|
5
|
+
* it under the terms of the GNU Affero General Public License as published by
|
6
|
+
* the Free Software Foundation, either version 3 of the License, or
|
7
|
+
* (at your option) any later version.
|
8
|
+
*
|
9
|
+
* This program is distributed in the hope that it will be useful,
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
* GNU Affero General Public License for more details.
|
13
|
+
*
|
14
|
+
* You should have received a copy of the GNU Affero General Public License
|
15
|
+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
*/
|
17
|
+
import { BigNumber } from 'bignumber.js';
|
1
18
|
import { TurboHTTPService } from './http.js';
|
2
19
|
import { TurboWinstonLogger } from './logger.js';
|
20
|
+
import { ArweaveToken } from './token.js';
|
3
21
|
export const developmentPaymentServiceURL = 'https://payment.ardrive.dev';
|
4
22
|
export const defaultPaymentServiceURL = 'https://payment.ardrive.io';
|
5
23
|
export class TurboUnauthenticatedPaymentService {
|
6
|
-
constructor({ url = defaultPaymentServiceURL, retryConfig, logger = new TurboWinstonLogger(), }) {
|
24
|
+
constructor({ url = defaultPaymentServiceURL, retryConfig, logger = new TurboWinstonLogger(), token = 'arweave', }) {
|
7
25
|
this.logger = logger;
|
8
26
|
this.httpService = new TurboHTTPService({
|
9
27
|
url: `${url}/v1`,
|
10
28
|
retryConfig,
|
11
29
|
logger: this.logger,
|
12
30
|
});
|
31
|
+
this.token = token;
|
13
32
|
}
|
14
33
|
getFiatRates() {
|
15
34
|
return this.httpService.get({
|
@@ -70,12 +89,55 @@ export class TurboUnauthenticatedPaymentService {
|
|
70
89
|
createCheckoutSession(params) {
|
71
90
|
return this.getCheckout(params);
|
72
91
|
}
|
92
|
+
async submitFundTransaction({ txId, }) {
|
93
|
+
const response = await this.httpService.post({
|
94
|
+
endpoint: `/account/balance/${this.token}`,
|
95
|
+
data: Buffer.from(JSON.stringify({ tx_id: txId })),
|
96
|
+
});
|
97
|
+
if ('creditedTransaction' in response) {
|
98
|
+
return {
|
99
|
+
id: response.creditedTransaction.transactionId,
|
100
|
+
quantity: response.creditedTransaction.transactionQuantity,
|
101
|
+
owner: response.creditedTransaction.destinationAddress,
|
102
|
+
winc: response.creditedTransaction.winstonCreditAmount,
|
103
|
+
token: response.creditedTransaction.tokenType,
|
104
|
+
status: 'confirmed',
|
105
|
+
block: response.creditedTransaction.blockHeight,
|
106
|
+
};
|
107
|
+
}
|
108
|
+
else if ('pendingTransaction' in response) {
|
109
|
+
return {
|
110
|
+
id: response.pendingTransaction.transactionId,
|
111
|
+
quantity: response.pendingTransaction.transactionQuantity,
|
112
|
+
owner: response.pendingTransaction.destinationAddress,
|
113
|
+
winc: response.pendingTransaction.winstonCreditAmount,
|
114
|
+
token: response.pendingTransaction.tokenType,
|
115
|
+
status: 'pending',
|
116
|
+
};
|
117
|
+
}
|
118
|
+
else if ('failedTransaction' in response) {
|
119
|
+
return {
|
120
|
+
id: response.failedTransaction.transactionId,
|
121
|
+
quantity: response.failedTransaction.transactionQuantity,
|
122
|
+
owner: response.failedTransaction.destinationAddress,
|
123
|
+
winc: response.failedTransaction.winstonCreditAmount,
|
124
|
+
token: response.failedTransaction.tokenType,
|
125
|
+
status: 'failed',
|
126
|
+
};
|
127
|
+
}
|
128
|
+
throw new Error('Unknown response from payment service: ' + response);
|
129
|
+
}
|
73
130
|
}
|
74
131
|
// NOTE: to avoid redundancy, we use inheritance here - but generally prefer composition over inheritance
|
75
132
|
export class TurboAuthenticatedPaymentService extends TurboUnauthenticatedPaymentService {
|
76
|
-
constructor({ url = defaultPaymentServiceURL, retryConfig, signer, logger
|
77
|
-
|
133
|
+
constructor({ url = defaultPaymentServiceURL, retryConfig, signer, logger = new TurboWinstonLogger(), token = 'arweave', tokenMap = {
|
134
|
+
arweave: new ArweaveToken({
|
135
|
+
logger,
|
136
|
+
}),
|
137
|
+
}, }) {
|
138
|
+
super({ url, retryConfig, logger, token });
|
78
139
|
this.signer = signer;
|
140
|
+
this.tokenMap = tokenMap;
|
79
141
|
}
|
80
142
|
async getBalance() {
|
81
143
|
const headers = await this.signer.generateSignedRequestHeaders();
|
@@ -96,4 +158,43 @@ export class TurboAuthenticatedPaymentService extends TurboUnauthenticatedPaymen
|
|
96
158
|
async createCheckoutSession(params) {
|
97
159
|
return this.getCheckout(params, await this.signer.generateSignedRequestHeaders());
|
98
160
|
}
|
161
|
+
async getTargetWalletForFund() {
|
162
|
+
const { addresses } = await this.httpService.get({
|
163
|
+
endpoint: '/info',
|
164
|
+
});
|
165
|
+
const walletAddress = addresses[this.token];
|
166
|
+
if (!walletAddress) {
|
167
|
+
throw new Error(`No wallet address found for token type: ${this.token}`);
|
168
|
+
}
|
169
|
+
return walletAddress;
|
170
|
+
}
|
171
|
+
async topUpWithTokens({ feeMultiplier = 1, tokenAmount: tokenAmountV, }) {
|
172
|
+
this.logger.debug('Funding account...');
|
173
|
+
const tokenAmount = new BigNumber(tokenAmountV);
|
174
|
+
const target = await this.getTargetWalletForFund();
|
175
|
+
const fundTx = await this.tokenMap[this.token].createTx({
|
176
|
+
target,
|
177
|
+
tokenAmount,
|
178
|
+
feeMultiplier,
|
179
|
+
});
|
180
|
+
const signedTx = await this.tokenMap[this.token].signTx({
|
181
|
+
tx: fundTx,
|
182
|
+
signer: this.signer,
|
183
|
+
});
|
184
|
+
await this.tokenMap[this.token].submitTx({ tx: signedTx });
|
185
|
+
const txId = signedTx.id;
|
186
|
+
try {
|
187
|
+
// Let transaction settle some time
|
188
|
+
await this.tokenMap[this.token].pollForTxBeingAvailable({ txId });
|
189
|
+
return {
|
190
|
+
...(await this.submitFundTransaction({ txId })),
|
191
|
+
target: signedTx.target,
|
192
|
+
reward: signedTx.reward,
|
193
|
+
};
|
194
|
+
}
|
195
|
+
catch (e) {
|
196
|
+
this.logger.error('Failed to submit fund transaction...', e);
|
197
|
+
throw Error(`Failed to submit fund transaction! Save this Transaction ID and try again with 'turbo.submitFundTransaction(id)': ${txId}`);
|
198
|
+
}
|
199
|
+
}
|
99
200
|
}
|