@ardrive/turbo-sdk 1.33.1 → 1.34.0

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 (44) hide show
  1. package/README.md +9 -1
  2. package/bundles/web.bundle.min.js +103581 -103456
  3. package/lib/cjs/cli/cli.js +1 -1
  4. package/lib/cjs/cli/commands/cryptoFund.js +3 -1
  5. package/lib/cjs/common/payment.js +17 -4
  6. package/lib/cjs/common/signer.js +13 -1
  7. package/lib/cjs/common/token/ario.js +27 -19
  8. package/lib/cjs/common/token/arweave.js +4 -1
  9. package/lib/cjs/common/token/ethereum.js +32 -13
  10. package/lib/cjs/common/token/solana.js +11 -2
  11. package/lib/cjs/utils/common.js +52 -0
  12. package/lib/cjs/version.js +1 -1
  13. package/lib/esm/cli/cli.js +1 -1
  14. package/lib/esm/cli/commands/cryptoFund.js +3 -1
  15. package/lib/esm/common/payment.js +17 -4
  16. package/lib/esm/common/signer.js +14 -2
  17. package/lib/esm/common/token/ario.js +27 -19
  18. package/lib/esm/common/token/arweave.js +4 -1
  19. package/lib/esm/common/token/ethereum.js +32 -14
  20. package/lib/esm/common/token/solana.js +11 -2
  21. package/lib/esm/utils/common.js +47 -1
  22. package/lib/esm/version.js +1 -1
  23. package/lib/types/cli/commands/cryptoFund.d.ts.map +1 -1
  24. package/lib/types/cli/types.d.ts +1 -0
  25. package/lib/types/cli/types.d.ts.map +1 -1
  26. package/lib/types/common/payment.d.ts +1 -1
  27. package/lib/types/common/payment.d.ts.map +1 -1
  28. package/lib/types/common/signer.d.ts +1 -1
  29. package/lib/types/common/signer.d.ts.map +1 -1
  30. package/lib/types/common/token/ario.d.ts +1 -1
  31. package/lib/types/common/token/ario.d.ts.map +1 -1
  32. package/lib/types/common/token/arweave.d.ts +1 -1
  33. package/lib/types/common/token/arweave.d.ts.map +1 -1
  34. package/lib/types/common/token/ethereum.d.ts +3 -2
  35. package/lib/types/common/token/ethereum.d.ts.map +1 -1
  36. package/lib/types/common/token/solana.d.ts +2 -1
  37. package/lib/types/common/token/solana.d.ts.map +1 -1
  38. package/lib/types/types.d.ts +5 -0
  39. package/lib/types/types.d.ts.map +1 -1
  40. package/lib/types/utils/common.d.ts +6 -0
  41. package/lib/types/utils/common.d.ts.map +1 -1
  42. package/lib/types/version.d.ts +1 -1
  43. package/lib/types/version.d.ts.map +1 -1
  44. package/package.json +1 -1
@@ -41,7 +41,7 @@ const utils_js_1 = require("./utils.js");
41
41
  (0, utils_js_1.applyOptions)(commander_1.program.command('top-up').description('Top up a Turbo address with Fiat'), [...options_js_1.walletOptions, options_js_1.optionMap.address, options_js_1.optionMap.value, options_js_1.optionMap.currency]).action(async (_commandOptions, command) => {
42
42
  await (0, utils_js_1.runCommand)(command, index_js_1.topUp);
43
43
  });
44
- (0, utils_js_1.applyOptions)(commander_1.program.command('crypto-fund').description('Top up a wallet with crypto'), [...options_js_1.walletOptions, options_js_1.optionMap.value, options_js_1.optionMap.txId]).action(async (_commandOptions, command) => {
44
+ (0, utils_js_1.applyOptions)(commander_1.program.command('crypto-fund').description('Top up a wallet with crypto'), [...options_js_1.walletOptions, options_js_1.optionMap.value, options_js_1.optionMap.txId, options_js_1.optionMap.address]).action(async (_commandOptions, command) => {
45
45
  await (0, utils_js_1.runCommand)(command, index_js_1.cryptoFund);
46
46
  });
47
47
  (0, utils_js_1.applyOptions)(commander_1.program.command('upload-folder').description('Upload a folder using Turbo'), options_js_1.uploadFolderOptions).action(async (_commandOptions, command) => {
@@ -28,6 +28,7 @@ const utils_js_1 = require("../utils.js");
28
28
  async function cryptoFund(options) {
29
29
  const value = options.value;
30
30
  const txId = options.txId;
31
+ const address = options.address;
31
32
  if (txId !== undefined) {
32
33
  const turbo = factory_js_1.TurboFactory.unauthenticated((0, utils_js_1.configFromOptions)(options));
33
34
  const result = await turbo.submitFundTransaction({ txId: txId });
@@ -47,7 +48,7 @@ async function cryptoFund(options) {
47
48
  const { confirm } = await (0, prompts_1.default)({
48
49
  type: 'confirm',
49
50
  name: 'confirm',
50
- message: `\nTransaction details:\n\n Amount: ${value} ${token}\n Target: ${targetWallet}\n Est Credits to receive: ${credits}\n Credit recipient: ${await turbo.signer.getNativeAddress()}\n Note: Network Dependent Gas Fees May Apply\n\nThis payment is non-refundable. Proceed with transaction?`,
51
+ message: `\nTransaction details:\n\n Amount: ${value} ${token}\n Target: ${targetWallet}\n Est Credits to receive: ${credits}\n Credit recipient: ${address ?? (await turbo.signer.getNativeAddress())}\n Note: Network Dependent Gas Fees May Apply\n\nThis payment is non-refundable. Proceed with transaction?`,
51
52
  initial: true,
52
53
  });
53
54
  if (!confirm) {
@@ -57,6 +58,7 @@ async function cryptoFund(options) {
57
58
  }
58
59
  const result = await turbo.topUpWithTokens({
59
60
  tokenAmount,
61
+ turboCreditDestinationAddress: address,
60
62
  });
61
63
  console.log('Sent crypto fund transaction: \n', JSON.stringify(result, null, 2));
62
64
  }
@@ -18,6 +18,7 @@ exports.TurboAuthenticatedPaymentService = exports.TurboUnauthenticatedPaymentSe
18
18
  */
19
19
  const bignumber_js_1 = require("bignumber.js");
20
20
  const axiosClient_js_1 = require("../utils/axiosClient.js");
21
+ const common_js_1 = require("../utils/common.js");
21
22
  const http_js_1 = require("./http.js");
22
23
  const logger_js_1 = require("./logger.js");
23
24
  const index_js_1 = require("./token/index.js");
@@ -151,31 +152,37 @@ class TurboUnauthenticatedPaymentService {
151
152
  return {
152
153
  id: response.creditedTransaction.transactionId,
153
154
  quantity: response.creditedTransaction.transactionQuantity,
154
- owner: response.creditedTransaction.destinationAddress,
155
+ owner: response.creditedTransaction.transactionSenderAddress ??
156
+ response.creditedTransaction.destinationAddress,
155
157
  winc: response.creditedTransaction.winstonCreditAmount,
156
158
  token: response.creditedTransaction.tokenType,
157
159
  status: 'confirmed',
158
160
  block: response.creditedTransaction.blockHeight,
161
+ recipient: response.creditedTransaction.destinationAddress,
159
162
  };
160
163
  }
161
164
  else if ('pendingTransaction' in response) {
162
165
  return {
163
166
  id: response.pendingTransaction.transactionId,
164
167
  quantity: response.pendingTransaction.transactionQuantity,
165
- owner: response.pendingTransaction.destinationAddress,
168
+ owner: response.pendingTransaction.transactionSenderAddress ??
169
+ response.pendingTransaction.destinationAddress,
166
170
  winc: response.pendingTransaction.winstonCreditAmount,
167
171
  token: response.pendingTransaction.tokenType,
168
172
  status: 'pending',
173
+ recipient: response.pendingTransaction.destinationAddress,
169
174
  };
170
175
  }
171
176
  else if ('failedTransaction' in response) {
172
177
  return {
173
178
  id: response.failedTransaction.transactionId,
174
179
  quantity: response.failedTransaction.transactionQuantity,
175
- owner: response.failedTransaction.destinationAddress,
180
+ owner: response.failedTransaction.transactionSenderAddress ??
181
+ response.failedTransaction.destinationAddress,
176
182
  winc: response.failedTransaction.winstonCreditAmount,
177
183
  token: response.failedTransaction.tokenType,
178
184
  status: 'failed',
185
+ recipient: response.failedTransaction.destinationAddress,
179
186
  };
180
187
  }
181
188
  throw new Error('Unknown response from payment service: ' + response);
@@ -271,10 +278,15 @@ class TurboAuthenticatedPaymentService extends TurboUnauthenticatedPaymentServic
271
278
  }
272
279
  return walletAddress;
273
280
  }
274
- async topUpWithTokens({ feeMultiplier = 1, tokenAmount: tokenAmountV, }) {
281
+ async topUpWithTokens({ feeMultiplier = 1, tokenAmount: tokenAmountV, turboCreditDestinationAddress, }) {
275
282
  if (!this.tokenTools) {
276
283
  throw new Error(`Token type not supported for crypto fund ${this.token}`);
277
284
  }
285
+ if (turboCreditDestinationAddress !== undefined) {
286
+ if ((0, common_js_1.isAnyValidUserAddress)(turboCreditDestinationAddress) === false) {
287
+ throw new Error(`Invalid turboCreditDestinationAddress provided: ${turboCreditDestinationAddress}`);
288
+ }
289
+ }
278
290
  const tokenAmount = new bignumber_js_1.BigNumber(tokenAmountV);
279
291
  const target = await this.getTargetWalletForFund();
280
292
  this.logger.debug('Funding account...', {
@@ -287,6 +299,7 @@ class TurboAuthenticatedPaymentService extends TurboUnauthenticatedPaymentServic
287
299
  tokenAmount,
288
300
  feeMultiplier,
289
301
  signer: this.signer,
302
+ turboCreditDestinationAddress,
290
303
  });
291
304
  const txId = fundTx.id;
292
305
  try {
@@ -34,6 +34,8 @@ const tweetnacl_1 = __importDefault(require("tweetnacl"));
34
34
  const types_js_1 = require("../types.js");
35
35
  const base64_js_1 = require("../utils/base64.js");
36
36
  const logger_js_1 = require("./logger.js");
37
+ const ethereum_js_1 = require("./token/ethereum.js");
38
+ const solana_js_1 = require("./token/solana.js");
37
39
  /**
38
40
  * Abstract class for signing TurboDataItems.
39
41
  */
@@ -81,7 +83,7 @@ class TurboDataItemAbstractSigner {
81
83
  return this.ownerToNativeAddress((0, base64_js_1.toB64Url)(await this.getPublicKey()), this.token);
82
84
  }
83
85
  /** Let the signer handle sending tx for better compat with cross chain libraries/web wallets */
84
- async sendTransaction({ target, amount, gatewayUrl, }) {
86
+ async sendTransaction({ target, amount, gatewayUrl, turboCreditDestinationAddress, }) {
85
87
  if (this.walletAdapter) {
86
88
  if ((0, types_js_1.isSolanaWalletAdapter)(this.walletAdapter)) {
87
89
  const connection = new web3_js_1.Connection(gatewayUrl, 'confirmed');
@@ -95,6 +97,14 @@ class TurboDataItemAbstractSigner {
95
97
  toPubkey: new web3_js_1.PublicKey(target),
96
98
  lamports: +new bignumber_js_1.BigNumber(amount),
97
99
  }));
100
+ if (turboCreditDestinationAddress !== undefined) {
101
+ tx.add(new web3_js_1.TransactionInstruction({
102
+ programId: new web3_js_1.PublicKey(solana_js_1.memoProgramId),
103
+ keys: [],
104
+ data: Buffer.from('turboCreditDestinationAddress=' +
105
+ turboCreditDestinationAddress),
106
+ }));
107
+ }
98
108
  const signedTx = await this.walletAdapter.signTransaction(tx);
99
109
  const id = await connection.sendRawTransaction(signedTx.serialize());
100
110
  return id;
@@ -109,6 +119,7 @@ class TurboDataItemAbstractSigner {
109
119
  const { hash } = await signer.sendTransaction({
110
120
  to: target,
111
121
  value: (0, ethers_1.parseEther)(amount.toFixed(18)),
122
+ data: (0, ethereum_js_1.ethDataFromTurboCreditDestinationAddress)(turboCreditDestinationAddress),
112
123
  });
113
124
  return hash;
114
125
  }
@@ -121,6 +132,7 @@ class TurboDataItemAbstractSigner {
121
132
  const tx = await ethWalletAndProvider.sendTransaction({
122
133
  to: target,
123
134
  value: (0, ethers_1.parseEther)(amount.toFixed(18)),
135
+ data: (0, ethereum_js_1.ethDataFromTurboCreditDestinationAddress)(turboCreditDestinationAddress),
124
136
  });
125
137
  this.logger.debug('Sent transaction', { tx });
126
138
  return tx.hash;
@@ -35,34 +35,42 @@ class ARIOToken {
35
35
  this.pollingOptions = pollingOptions;
36
36
  this.logger = logger;
37
37
  }
38
- async createAndSubmitTx({ target, signer: { signer }, tokenAmount, }) {
38
+ async createAndSubmitTx({ target, signer: { signer }, tokenAmount, turboCreditDestinationAddress, }) {
39
+ const tags = [
40
+ {
41
+ name: 'Action',
42
+ value: 'Transfer',
43
+ },
44
+ {
45
+ name: 'Recipient',
46
+ value: target,
47
+ },
48
+ {
49
+ name: 'Quantity',
50
+ value: tokenAmount.toString(),
51
+ },
52
+ {
53
+ name: 'Turbo-SDK',
54
+ value: version_js_1.version,
55
+ },
56
+ ];
57
+ if (turboCreditDestinationAddress !== undefined) {
58
+ tags.push({
59
+ name: 'Turbo-Credit-Destination-Address',
60
+ value: turboCreditDestinationAddress,
61
+ });
62
+ }
39
63
  const txId = await this.ao.message({
40
64
  signer: createAoSigner(signer),
41
65
  process: this.processId,
42
- tags: [
43
- {
44
- name: 'Action',
45
- value: 'Transfer',
46
- },
47
- {
48
- name: 'Recipient',
49
- value: target,
50
- },
51
- {
52
- name: 'Quantity',
53
- value: tokenAmount.toString(),
54
- },
55
- {
56
- name: 'Turbo-SDK',
57
- value: version_js_1.version,
58
- },
59
- ],
66
+ tags,
60
67
  });
61
68
  this.logger.debug('Submitted Transfer message to ARIO process...', {
62
69
  id: txId,
63
70
  target,
64
71
  tokenAmount,
65
72
  processId: this.processId,
73
+ tags,
66
74
  });
67
75
  return { id: txId, target, reward: '0' };
68
76
  }
@@ -46,7 +46,7 @@ class ArweaveToken {
46
46
  this.mintU = mintU;
47
47
  this.pollingOptions = pollingOptions;
48
48
  }
49
- async createAndSubmitTx({ feeMultiplier, target, tokenAmount, signer, }) {
49
+ async createAndSubmitTx({ feeMultiplier, target, tokenAmount, signer, turboCreditDestinationAddress, }) {
50
50
  const tx = await this.arweave.createTransaction({
51
51
  target,
52
52
  quantity: tokenAmount.toString(),
@@ -63,6 +63,9 @@ class ArweaveToken {
63
63
  tx.addTag('Contract', 'KTzTXT_ANmF84fWEKHzWURD1LWd9QaFR9yfYUwH2Lxw'); // cspell:enable
64
64
  tx.addTag('Input', JSON.stringify({ function: 'mint' }));
65
65
  }
66
+ if (turboCreditDestinationAddress !== undefined) {
67
+ tx.addTag('Turbo-Credit-Destination-Address', turboCreditDestinationAddress);
68
+ }
66
69
  const publicKeyB64Url = (0, base64_js_1.toB64Url)(await signer.getPublicKey());
67
70
  tx.setOwner(publicKeyB64Url);
68
71
  const dataToSign = await tx.getSignatureData();
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.EthereumToken = exports.ETHToTokenAmount = exports.weiToTokenAmount = void 0;
4
+ exports.ethDataFromTurboCreditDestinationAddress = ethDataFromTurboCreditDestinationAddress;
4
5
  /**
5
6
  * Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
6
7
  *
@@ -28,25 +29,37 @@ class EthereumToken {
28
29
  constructor({ logger = logger_js_1.TurboWinstonLogger.default, gatewayUrl = common_js_1.defaultProdGatewayUrls.ethereum, pollingOptions = {
29
30
  maxAttempts: 10,
30
31
  pollingIntervalMs: 4_000,
31
- initialBackoffMs: 10_000,
32
+ initialBackoffMs: 25_000,
32
33
  }, } = {}) {
33
34
  this.logger = logger;
34
35
  this.gatewayUrl = gatewayUrl;
35
36
  this.pollingOptions = pollingOptions;
36
37
  this.rpcProvider = new ethers_1.ethers.JsonRpcProvider(gatewayUrl);
37
38
  }
38
- async createAndSubmitTx({ target, tokenAmount, signer, }) {
39
- // convert wei to eth
40
- const eth = tokenAmount.shiftedBy(-18);
41
- const txId = await signer.sendTransaction({
42
- target,
43
- amount: eth,
44
- gatewayUrl: this.gatewayUrl,
45
- });
46
- return {
47
- id: txId,
48
- target,
49
- };
39
+ async createAndSubmitTx({ target, tokenAmount, signer, turboCreditDestinationAddress, }) {
40
+ try {
41
+ // convert wei to eth
42
+ const eth = tokenAmount.shiftedBy(-18);
43
+ const txId = await signer.sendTransaction({
44
+ target,
45
+ amount: eth,
46
+ gatewayUrl: this.gatewayUrl,
47
+ turboCreditDestinationAddress,
48
+ });
49
+ return {
50
+ id: txId,
51
+ target,
52
+ };
53
+ }
54
+ catch (e) {
55
+ this.logger.error('Error creating and submitting Ethereum tx', {
56
+ error: e instanceof Error ? e.message : e,
57
+ target,
58
+ tokenAmount,
59
+ rpcEndpoint: this.gatewayUrl,
60
+ });
61
+ throw e;
62
+ }
50
63
  }
51
64
  async getTxAvailability(txId) {
52
65
  const tx = await this.rpcProvider.getTransaction(txId);
@@ -77,3 +90,9 @@ class EthereumToken {
77
90
  }
78
91
  }
79
92
  exports.EthereumToken = EthereumToken;
93
+ function ethDataFromTurboCreditDestinationAddress(turboCreditDestinationAddress) {
94
+ if (turboCreditDestinationAddress !== undefined) {
95
+ return (0, ethers_1.hexlify)((0, ethers_1.toUtf8Bytes)('turboCreditDestinationAddress=' + turboCreditDestinationAddress));
96
+ }
97
+ return undefined;
98
+ }
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.SolanaToken = exports.SOLToTokenAmount = exports.lamportToTokenAmount = void 0;
6
+ exports.SolanaToken = exports.memoProgramId = exports.SOLToTokenAmount = exports.lamportToTokenAmount = void 0;
7
7
  /**
8
8
  * Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
9
9
  *
@@ -30,6 +30,7 @@ const lamportToTokenAmount = (winston) => winston;
30
30
  exports.lamportToTokenAmount = lamportToTokenAmount;
31
31
  const SOLToTokenAmount = (sol) => new bignumber_js_1.BigNumber(sol).times(1e9).valueOf();
32
32
  exports.SOLToTokenAmount = SOLToTokenAmount;
33
+ exports.memoProgramId = 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr';
33
34
  class SolanaToken {
34
35
  constructor({ logger = logger_js_1.TurboWinstonLogger.default, gatewayUrl = common_js_1.defaultProdGatewayUrls.solana, pollingOptions = {
35
36
  maxAttempts: 10,
@@ -41,12 +42,13 @@ class SolanaToken {
41
42
  this.connection = new web3_js_1.Connection(gatewayUrl, 'confirmed');
42
43
  this.pollingOptions = pollingOptions;
43
44
  }
44
- async createAndSubmitTx({ target, tokenAmount, signer, }) {
45
+ async createAndSubmitTx({ target, tokenAmount, signer, turboCreditDestinationAddress, }) {
45
46
  if (signer.signer instanceof arbundles_1.HexInjectedSolanaSigner) {
46
47
  const id = await signer.sendTransaction({
47
48
  amount: tokenAmount,
48
49
  target,
49
50
  gatewayUrl: this.gatewayUrl,
51
+ turboCreditDestinationAddress,
50
52
  });
51
53
  return { target, id };
52
54
  }
@@ -60,6 +62,13 @@ class SolanaToken {
60
62
  toPubkey: new web3_js_1.PublicKey(target),
61
63
  lamports: +new bignumber_js_1.BigNumber(tokenAmount),
62
64
  }));
65
+ if (turboCreditDestinationAddress !== undefined) {
66
+ tx.add(new web3_js_1.TransactionInstruction({
67
+ programId: new web3_js_1.PublicKey(exports.memoProgramId),
68
+ keys: [],
69
+ data: Buffer.from('turboCreditDestinationAddress=' + turboCreditDestinationAddress),
70
+ }));
71
+ }
63
72
  const serializedTx = tx.serializeMessage();
64
73
  const signature = await signer.signData(Uint8Array.from(serializedTx));
65
74
  tx.addSignature(publicKey, Buffer.from(signature));
@@ -7,6 +7,12 @@ exports.createTurboSigner = createTurboSigner;
7
7
  exports.signerFromKyvePrivateKey = signerFromKyvePrivateKey;
8
8
  exports.signerFromKyveMnemonic = signerFromKyveMnemonic;
9
9
  exports.isBlob = isBlob;
10
+ exports.isValidArweaveBase64URL = isValidArweaveBase64URL;
11
+ exports.isValidSolanaAddress = isValidSolanaAddress;
12
+ exports.isValidECDSAAddress = isValidECDSAAddress;
13
+ exports.isValidKyveAddress = isValidKyveAddress;
14
+ exports.isValidUserAddress = isValidUserAddress;
15
+ exports.isAnyValidUserAddress = isAnyValidUserAddress;
10
16
  /**
11
17
  * Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
12
18
  *
@@ -28,6 +34,7 @@ const encoding_1 = require("@cosmjs/encoding");
28
34
  const arbundles_1 = require("@dha-team/arbundles");
29
35
  const bytes_1 = require("@ethersproject/bytes");
30
36
  const signing_key_1 = require("@ethersproject/signing-key");
37
+ const web3_js_1 = require("@solana/web3.js");
31
38
  const ethers_1 = require("ethers");
32
39
  const types_js_1 = require("../types.js");
33
40
  function sleep(ms) {
@@ -123,3 +130,48 @@ async function signerFromKyveMnemonic(mnemonic) {
123
130
  function isBlob(val) {
124
131
  return typeof Blob !== 'undefined' && val instanceof Blob;
125
132
  }
133
+ // check if it is a valid arweave base64url for a wallet public address, transaction id or smartweave contract
134
+ function isValidArweaveBase64URL(base64URL) {
135
+ const base64URLRegex = new RegExp('^[a-zA-Z0-9_-]{43}$');
136
+ return base64URLRegex.test(base64URL);
137
+ }
138
+ function isValidSolanaAddress(address) {
139
+ try {
140
+ return web3_js_1.PublicKey.isOnCurve(address);
141
+ }
142
+ catch {
143
+ return false;
144
+ }
145
+ }
146
+ function isValidECDSAAddress(address) {
147
+ const ethAddressRegex = new RegExp('^0x[a-fA-F0-9]{40}$');
148
+ return ethAddressRegex.test(address);
149
+ }
150
+ function isValidKyveAddress(address) {
151
+ const kyveAddressRegex = new RegExp('^kyve[a-zA-Z0-9]{39}$');
152
+ return kyveAddressRegex.test(address);
153
+ }
154
+ function isValidUserAddress(address, type) {
155
+ switch (type) {
156
+ case 'arweave':
157
+ case 'ario':
158
+ return isValidArweaveBase64URL(address);
159
+ case 'solana':
160
+ return isValidSolanaAddress(address);
161
+ case 'ethereum':
162
+ case 'base-eth':
163
+ case 'matic':
164
+ case 'pol':
165
+ return isValidECDSAAddress(address);
166
+ case 'kyve':
167
+ return isValidKyveAddress(address);
168
+ }
169
+ }
170
+ function isAnyValidUserAddress(address) {
171
+ for (const type of types_js_1.tokenTypes) {
172
+ if (isValidUserAddress(address, type)) {
173
+ return type;
174
+ }
175
+ }
176
+ return false;
177
+ }
@@ -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 = '1.33.1';
20
+ exports.version = '1.34.0';
@@ -39,7 +39,7 @@ applyOptions(program.command('balance').description('Get balance of a Turbo addr
39
39
  applyOptions(program.command('top-up').description('Top up a Turbo address with Fiat'), [...walletOptions, optionMap.address, optionMap.value, optionMap.currency]).action(async (_commandOptions, command) => {
40
40
  await runCommand(command, topUp);
41
41
  });
42
- applyOptions(program.command('crypto-fund').description('Top up a wallet with crypto'), [...walletOptions, optionMap.value, optionMap.txId]).action(async (_commandOptions, command) => {
42
+ applyOptions(program.command('crypto-fund').description('Top up a wallet with crypto'), [...walletOptions, optionMap.value, optionMap.txId, optionMap.address]).action(async (_commandOptions, command) => {
43
43
  await runCommand(command, cryptoFund);
44
44
  });
45
45
  applyOptions(program.command('upload-folder').description('Upload a folder using Turbo'), uploadFolderOptions).action(async (_commandOptions, command) => {
@@ -22,6 +22,7 @@ import { configFromOptions, tokenFromOptions, turboFromOptions, } from '../utils
22
22
  export async function cryptoFund(options) {
23
23
  const value = options.value;
24
24
  const txId = options.txId;
25
+ const address = options.address;
25
26
  if (txId !== undefined) {
26
27
  const turbo = TurboFactory.unauthenticated(configFromOptions(options));
27
28
  const result = await turbo.submitFundTransaction({ txId: txId });
@@ -41,7 +42,7 @@ export async function cryptoFund(options) {
41
42
  const { confirm } = await prompts({
42
43
  type: 'confirm',
43
44
  name: 'confirm',
44
- message: `\nTransaction details:\n\n Amount: ${value} ${token}\n Target: ${targetWallet}\n Est Credits to receive: ${credits}\n Credit recipient: ${await turbo.signer.getNativeAddress()}\n Note: Network Dependent Gas Fees May Apply\n\nThis payment is non-refundable. Proceed with transaction?`,
45
+ message: `\nTransaction details:\n\n Amount: ${value} ${token}\n Target: ${targetWallet}\n Est Credits to receive: ${credits}\n Credit recipient: ${address ?? (await turbo.signer.getNativeAddress())}\n Note: Network Dependent Gas Fees May Apply\n\nThis payment is non-refundable. Proceed with transaction?`,
45
46
  initial: true,
46
47
  });
47
48
  if (!confirm) {
@@ -51,6 +52,7 @@ export async function cryptoFund(options) {
51
52
  }
52
53
  const result = await turbo.topUpWithTokens({
53
54
  tokenAmount,
55
+ turboCreditDestinationAddress: address,
54
56
  });
55
57
  console.log('Sent crypto fund transaction: \n', JSON.stringify(result, null, 2));
56
58
  }
@@ -15,6 +15,7 @@
15
15
  */
16
16
  import { BigNumber } from 'bignumber.js';
17
17
  import { defaultRetryConfig } from '../utils/axiosClient.js';
18
+ import { isAnyValidUserAddress } from '../utils/common.js';
18
19
  import { TurboHTTPService } from './http.js';
19
20
  import { TurboWinstonLogger } from './logger.js';
20
21
  import { exponentMap, tokenToBaseMap } from './token/index.js';
@@ -148,31 +149,37 @@ export class TurboUnauthenticatedPaymentService {
148
149
  return {
149
150
  id: response.creditedTransaction.transactionId,
150
151
  quantity: response.creditedTransaction.transactionQuantity,
151
- owner: response.creditedTransaction.destinationAddress,
152
+ owner: response.creditedTransaction.transactionSenderAddress ??
153
+ response.creditedTransaction.destinationAddress,
152
154
  winc: response.creditedTransaction.winstonCreditAmount,
153
155
  token: response.creditedTransaction.tokenType,
154
156
  status: 'confirmed',
155
157
  block: response.creditedTransaction.blockHeight,
158
+ recipient: response.creditedTransaction.destinationAddress,
156
159
  };
157
160
  }
158
161
  else if ('pendingTransaction' in response) {
159
162
  return {
160
163
  id: response.pendingTransaction.transactionId,
161
164
  quantity: response.pendingTransaction.transactionQuantity,
162
- owner: response.pendingTransaction.destinationAddress,
165
+ owner: response.pendingTransaction.transactionSenderAddress ??
166
+ response.pendingTransaction.destinationAddress,
163
167
  winc: response.pendingTransaction.winstonCreditAmount,
164
168
  token: response.pendingTransaction.tokenType,
165
169
  status: 'pending',
170
+ recipient: response.pendingTransaction.destinationAddress,
166
171
  };
167
172
  }
168
173
  else if ('failedTransaction' in response) {
169
174
  return {
170
175
  id: response.failedTransaction.transactionId,
171
176
  quantity: response.failedTransaction.transactionQuantity,
172
- owner: response.failedTransaction.destinationAddress,
177
+ owner: response.failedTransaction.transactionSenderAddress ??
178
+ response.failedTransaction.destinationAddress,
173
179
  winc: response.failedTransaction.winstonCreditAmount,
174
180
  token: response.failedTransaction.tokenType,
175
181
  status: 'failed',
182
+ recipient: response.failedTransaction.destinationAddress,
176
183
  };
177
184
  }
178
185
  throw new Error('Unknown response from payment service: ' + response);
@@ -267,10 +274,15 @@ export class TurboAuthenticatedPaymentService extends TurboUnauthenticatedPaymen
267
274
  }
268
275
  return walletAddress;
269
276
  }
270
- async topUpWithTokens({ feeMultiplier = 1, tokenAmount: tokenAmountV, }) {
277
+ async topUpWithTokens({ feeMultiplier = 1, tokenAmount: tokenAmountV, turboCreditDestinationAddress, }) {
271
278
  if (!this.tokenTools) {
272
279
  throw new Error(`Token type not supported for crypto fund ${this.token}`);
273
280
  }
281
+ if (turboCreditDestinationAddress !== undefined) {
282
+ if (isAnyValidUserAddress(turboCreditDestinationAddress) === false) {
283
+ throw new Error(`Invalid turboCreditDestinationAddress provided: ${turboCreditDestinationAddress}`);
284
+ }
285
+ }
274
286
  const tokenAmount = new BigNumber(tokenAmountV);
275
287
  const target = await this.getTargetWalletForFund();
276
288
  this.logger.debug('Funding account...', {
@@ -283,6 +295,7 @@ export class TurboAuthenticatedPaymentService extends TurboUnauthenticatedPaymen
283
295
  tokenAmount,
284
296
  feeMultiplier,
285
297
  signer: this.signer,
298
+ turboCreditDestinationAddress,
286
299
  });
287
300
  const txId = fundTx.id;
288
301
  try {
@@ -18,7 +18,7 @@ import { Secp256k1 } from '@cosmjs/crypto';
18
18
  import { toBase64 } from '@cosmjs/encoding';
19
19
  import { EthereumSigner, HexSolanaSigner } from '@dha-team/arbundles';
20
20
  import { computePublicKey } from '@ethersproject/signing-key';
21
- import { Connection, PublicKey, SystemProgram, Transaction, } from '@solana/web3.js';
21
+ import { Connection, PublicKey, SystemProgram, Transaction, TransactionInstruction, } from '@solana/web3.js';
22
22
  import { BigNumber } from 'bignumber.js';
23
23
  import bs58 from 'bs58';
24
24
  import { randomBytes } from 'crypto';
@@ -28,6 +28,8 @@ import nacl from 'tweetnacl';
28
28
  import { isEthereumWalletAdapter, isSolanaWalletAdapter, } from '../types.js';
29
29
  import { fromB64Url, ownerToAddress as ownerToB64Address, toB64Url, } from '../utils/base64.js';
30
30
  import { TurboWinstonLogger } from './logger.js';
31
+ import { ethDataFromTurboCreditDestinationAddress } from './token/ethereum.js';
32
+ import { memoProgramId } from './token/solana.js';
31
33
  /**
32
34
  * Abstract class for signing TurboDataItems.
33
35
  */
@@ -75,7 +77,7 @@ export class TurboDataItemAbstractSigner {
75
77
  return this.ownerToNativeAddress(toB64Url(await this.getPublicKey()), this.token);
76
78
  }
77
79
  /** Let the signer handle sending tx for better compat with cross chain libraries/web wallets */
78
- async sendTransaction({ target, amount, gatewayUrl, }) {
80
+ async sendTransaction({ target, amount, gatewayUrl, turboCreditDestinationAddress, }) {
79
81
  if (this.walletAdapter) {
80
82
  if (isSolanaWalletAdapter(this.walletAdapter)) {
81
83
  const connection = new Connection(gatewayUrl, 'confirmed');
@@ -89,6 +91,14 @@ export class TurboDataItemAbstractSigner {
89
91
  toPubkey: new PublicKey(target),
90
92
  lamports: +new BigNumber(amount),
91
93
  }));
94
+ if (turboCreditDestinationAddress !== undefined) {
95
+ tx.add(new TransactionInstruction({
96
+ programId: new PublicKey(memoProgramId),
97
+ keys: [],
98
+ data: Buffer.from('turboCreditDestinationAddress=' +
99
+ turboCreditDestinationAddress),
100
+ }));
101
+ }
92
102
  const signedTx = await this.walletAdapter.signTransaction(tx);
93
103
  const id = await connection.sendRawTransaction(signedTx.serialize());
94
104
  return id;
@@ -103,6 +113,7 @@ export class TurboDataItemAbstractSigner {
103
113
  const { hash } = await signer.sendTransaction({
104
114
  to: target,
105
115
  value: parseEther(amount.toFixed(18)),
116
+ data: ethDataFromTurboCreditDestinationAddress(turboCreditDestinationAddress),
106
117
  });
107
118
  return hash;
108
119
  }
@@ -115,6 +126,7 @@ export class TurboDataItemAbstractSigner {
115
126
  const tx = await ethWalletAndProvider.sendTransaction({
116
127
  to: target,
117
128
  value: parseEther(amount.toFixed(18)),
129
+ data: ethDataFromTurboCreditDestinationAddress(turboCreditDestinationAddress),
118
130
  });
119
131
  this.logger.debug('Sent transaction', { tx });
120
132
  return tx.hash;
@@ -32,34 +32,42 @@ export class ARIOToken {
32
32
  this.pollingOptions = pollingOptions;
33
33
  this.logger = logger;
34
34
  }
35
- async createAndSubmitTx({ target, signer: { signer }, tokenAmount, }) {
35
+ async createAndSubmitTx({ target, signer: { signer }, tokenAmount, turboCreditDestinationAddress, }) {
36
+ const tags = [
37
+ {
38
+ name: 'Action',
39
+ value: 'Transfer',
40
+ },
41
+ {
42
+ name: 'Recipient',
43
+ value: target,
44
+ },
45
+ {
46
+ name: 'Quantity',
47
+ value: tokenAmount.toString(),
48
+ },
49
+ {
50
+ name: 'Turbo-SDK',
51
+ value: version,
52
+ },
53
+ ];
54
+ if (turboCreditDestinationAddress !== undefined) {
55
+ tags.push({
56
+ name: 'Turbo-Credit-Destination-Address',
57
+ value: turboCreditDestinationAddress,
58
+ });
59
+ }
36
60
  const txId = await this.ao.message({
37
61
  signer: createAoSigner(signer),
38
62
  process: this.processId,
39
- tags: [
40
- {
41
- name: 'Action',
42
- value: 'Transfer',
43
- },
44
- {
45
- name: 'Recipient',
46
- value: target,
47
- },
48
- {
49
- name: 'Quantity',
50
- value: tokenAmount.toString(),
51
- },
52
- {
53
- name: 'Turbo-SDK',
54
- value: version,
55
- },
56
- ],
63
+ tags,
57
64
  });
58
65
  this.logger.debug('Submitted Transfer message to ARIO process...', {
59
66
  id: txId,
60
67
  target,
61
68
  tokenAmount,
62
69
  processId: this.processId,
70
+ tags,
63
71
  });
64
72
  return { id: txId, target, reward: '0' };
65
73
  }