@ardrive/turbo-sdk 1.33.1 → 1.34.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/README.md +49 -6
- package/bundles/web.bundle.min.js +103598 -103276
- package/lib/cjs/cli/cli.js +1 -1
- package/lib/cjs/cli/commands/cryptoFund.js +3 -1
- package/lib/cjs/common/payment.js +17 -4
- package/lib/cjs/common/signer.js +16 -1
- package/lib/cjs/common/token/ario.js +27 -19
- package/lib/cjs/common/token/arweave.js +4 -1
- package/lib/cjs/common/token/baseEth.js +7 -6
- package/lib/cjs/common/token/erc20.js +91 -0
- package/lib/cjs/common/token/ethereum.js +38 -18
- package/lib/cjs/common/token/index.js +11 -0
- package/lib/cjs/common/token/polygon.js +7 -6
- package/lib/cjs/common/token/solana.js +11 -2
- package/lib/cjs/common/token/usdc.js +83 -0
- package/lib/cjs/common/upload.js +6 -1
- package/lib/cjs/types.js +3 -0
- package/lib/cjs/utils/common.js +71 -4
- package/lib/cjs/version.js +1 -1
- package/lib/esm/cli/cli.js +1 -1
- package/lib/esm/cli/commands/cryptoFund.js +3 -1
- package/lib/esm/common/payment.js +17 -4
- package/lib/esm/common/signer.js +17 -2
- package/lib/esm/common/token/ario.js +27 -19
- package/lib/esm/common/token/arweave.js +4 -1
- package/lib/esm/common/token/baseEth.js +6 -5
- package/lib/esm/common/token/erc20.js +87 -0
- package/lib/esm/common/token/ethereum.js +37 -18
- package/lib/esm/common/token/index.js +11 -0
- package/lib/esm/common/token/polygon.js +6 -5
- package/lib/esm/common/token/solana.js +11 -2
- package/lib/esm/common/token/usdc.js +76 -0
- package/lib/esm/common/upload.js +6 -1
- package/lib/esm/types.js +3 -0
- package/lib/esm/utils/common.js +66 -5
- package/lib/esm/version.js +1 -1
- package/lib/types/cli/commands/cryptoFund.d.ts.map +1 -1
- package/lib/types/cli/types.d.ts +1 -0
- package/lib/types/cli/types.d.ts.map +1 -1
- package/lib/types/common/payment.d.ts +1 -1
- package/lib/types/common/payment.d.ts.map +1 -1
- package/lib/types/common/signer.d.ts +2 -2
- package/lib/types/common/signer.d.ts.map +1 -1
- package/lib/types/common/token/ario.d.ts +1 -1
- package/lib/types/common/token/ario.d.ts.map +1 -1
- package/lib/types/common/token/arweave.d.ts +1 -1
- package/lib/types/common/token/arweave.d.ts.map +1 -1
- package/lib/types/common/token/baseEth.d.ts +5 -0
- package/lib/types/common/token/baseEth.d.ts.map +1 -1
- package/lib/types/common/token/erc20.d.ts +19 -0
- package/lib/types/common/token/erc20.d.ts.map +1 -0
- package/lib/types/common/token/ethereum.d.ts +4 -2
- package/lib/types/common/token/ethereum.d.ts.map +1 -1
- package/lib/types/common/token/index.d.ts.map +1 -1
- package/lib/types/common/token/polygon.d.ts +5 -0
- package/lib/types/common/token/polygon.d.ts.map +1 -1
- package/lib/types/common/token/solana.d.ts +2 -1
- package/lib/types/common/token/solana.d.ts.map +1 -1
- package/lib/types/common/token/usdc.d.ts +36 -0
- package/lib/types/common/token/usdc.d.ts.map +1 -0
- package/lib/types/common/upload.d.ts.map +1 -1
- package/lib/types/types.d.ts +8 -2
- package/lib/types/types.d.ts.map +1 -1
- package/lib/types/utils/common.d.ts +6 -0
- 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 +1 -1
package/lib/cjs/utils/common.js
CHANGED
|
@@ -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) {
|
|
@@ -36,15 +43,21 @@ function sleep(ms) {
|
|
|
36
43
|
function isWeb() {
|
|
37
44
|
return typeof window !== 'undefined';
|
|
38
45
|
}
|
|
46
|
+
const ethTestnetRpc = 'https://eth-sepolia.public.blastapi.io';
|
|
47
|
+
const baseTestnetRpc = 'https://sepolia.base.org';
|
|
48
|
+
const polygonTestnetRpc = 'https://rpc-amoy.polygon.technology';
|
|
39
49
|
exports.tokenToDevGatewayMap = {
|
|
40
50
|
arweave: 'https://arweave.net', // No arweave test net
|
|
41
51
|
ario: 'https://arweave.net', // No arweave test net
|
|
42
52
|
solana: 'https://api.devnet.solana.com',
|
|
43
|
-
ethereum:
|
|
44
|
-
'base-eth':
|
|
53
|
+
ethereum: ethTestnetRpc,
|
|
54
|
+
'base-eth': baseTestnetRpc,
|
|
45
55
|
kyve: 'https://api.korellia.kyve.network',
|
|
46
|
-
matic:
|
|
47
|
-
pol:
|
|
56
|
+
matic: polygonTestnetRpc,
|
|
57
|
+
pol: polygonTestnetRpc,
|
|
58
|
+
usdc: ethTestnetRpc,
|
|
59
|
+
'base-usdc': baseTestnetRpc,
|
|
60
|
+
'polygon-usdc': polygonTestnetRpc,
|
|
48
61
|
};
|
|
49
62
|
exports.tokenToDevAoConfigMap = {
|
|
50
63
|
ario: {
|
|
@@ -61,6 +74,9 @@ exports.defaultProdGatewayUrls = {
|
|
|
61
74
|
kyve: 'https://api.kyve.network/',
|
|
62
75
|
matic: 'https://polygon-rpc.com/',
|
|
63
76
|
pol: 'https://polygon-rpc.com/',
|
|
77
|
+
usdc: 'https://cloudflare-eth.com/',
|
|
78
|
+
'base-usdc': 'https://mainnet.base.org',
|
|
79
|
+
'polygon-usdc': 'https://polygon-rpc.com/',
|
|
64
80
|
};
|
|
65
81
|
exports.defaultProdAoConfigs = {
|
|
66
82
|
ario: {
|
|
@@ -92,6 +108,9 @@ function createTurboSigner({ signer: clientProvidedSigner, privateKey: clientPro
|
|
|
92
108
|
case 'pol':
|
|
93
109
|
case 'matic':
|
|
94
110
|
case 'base-eth':
|
|
111
|
+
case 'usdc':
|
|
112
|
+
case 'base-usdc':
|
|
113
|
+
case 'polygon-usdc':
|
|
95
114
|
if (!(0, types_js_1.isEthPrivateKey)(clientProvidedPrivateKey)) {
|
|
96
115
|
throw new Error('A valid Ethereum private key must be provided for EthereumSigner.');
|
|
97
116
|
}
|
|
@@ -123,3 +142,51 @@ async function signerFromKyveMnemonic(mnemonic) {
|
|
|
123
142
|
function isBlob(val) {
|
|
124
143
|
return typeof Blob !== 'undefined' && val instanceof Blob;
|
|
125
144
|
}
|
|
145
|
+
// check if it is a valid arweave base64url for a wallet public address, transaction id or smartweave contract
|
|
146
|
+
function isValidArweaveBase64URL(base64URL) {
|
|
147
|
+
const base64URLRegex = new RegExp('^[a-zA-Z0-9_-]{43}$');
|
|
148
|
+
return base64URLRegex.test(base64URL);
|
|
149
|
+
}
|
|
150
|
+
function isValidSolanaAddress(address) {
|
|
151
|
+
try {
|
|
152
|
+
return web3_js_1.PublicKey.isOnCurve(address);
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function isValidECDSAAddress(address) {
|
|
159
|
+
const ethAddressRegex = new RegExp('^0x[a-fA-F0-9]{40}$');
|
|
160
|
+
return ethAddressRegex.test(address);
|
|
161
|
+
}
|
|
162
|
+
function isValidKyveAddress(address) {
|
|
163
|
+
const kyveAddressRegex = new RegExp('^kyve[a-zA-Z0-9]{39}$');
|
|
164
|
+
return kyveAddressRegex.test(address);
|
|
165
|
+
}
|
|
166
|
+
function isValidUserAddress(address, type) {
|
|
167
|
+
switch (type) {
|
|
168
|
+
case 'arweave':
|
|
169
|
+
case 'ario':
|
|
170
|
+
return isValidArweaveBase64URL(address);
|
|
171
|
+
case 'solana':
|
|
172
|
+
return isValidSolanaAddress(address);
|
|
173
|
+
case 'ethereum':
|
|
174
|
+
case 'base-eth':
|
|
175
|
+
case 'matic':
|
|
176
|
+
case 'pol':
|
|
177
|
+
case 'base-usdc':
|
|
178
|
+
case 'usdc':
|
|
179
|
+
case 'polygon-usdc':
|
|
180
|
+
return isValidECDSAAddress(address);
|
|
181
|
+
case 'kyve':
|
|
182
|
+
return isValidKyveAddress(address);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function isAnyValidUserAddress(address) {
|
|
186
|
+
for (const type of types_js_1.tokenTypes) {
|
|
187
|
+
if (isValidUserAddress(address, type)) {
|
|
188
|
+
return type;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return false;
|
|
192
|
+
}
|
package/lib/cjs/version.js
CHANGED
package/lib/esm/cli/cli.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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 {
|
package/lib/esm/common/signer.js
CHANGED
|
@@ -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
|
*/
|
|
@@ -46,6 +48,9 @@ export class TurboDataItemAbstractSigner {
|
|
|
46
48
|
case 'matic':
|
|
47
49
|
case 'pol':
|
|
48
50
|
case 'base-eth':
|
|
51
|
+
case 'usdc':
|
|
52
|
+
case 'base-usdc':
|
|
53
|
+
case 'polygon-usdc':
|
|
49
54
|
return computeAddress(computePublicKey(fromB64Url(owner)));
|
|
50
55
|
case 'kyve':
|
|
51
56
|
return pubkeyToAddress({
|
|
@@ -75,7 +80,7 @@ export class TurboDataItemAbstractSigner {
|
|
|
75
80
|
return this.ownerToNativeAddress(toB64Url(await this.getPublicKey()), this.token);
|
|
76
81
|
}
|
|
77
82
|
/** Let the signer handle sending tx for better compat with cross chain libraries/web wallets */
|
|
78
|
-
async sendTransaction({ target, amount, gatewayUrl, }) {
|
|
83
|
+
async sendTransaction({ target, amount, gatewayUrl, turboCreditDestinationAddress, }) {
|
|
79
84
|
if (this.walletAdapter) {
|
|
80
85
|
if (isSolanaWalletAdapter(this.walletAdapter)) {
|
|
81
86
|
const connection = new Connection(gatewayUrl, 'confirmed');
|
|
@@ -89,6 +94,14 @@ export class TurboDataItemAbstractSigner {
|
|
|
89
94
|
toPubkey: new PublicKey(target),
|
|
90
95
|
lamports: +new BigNumber(amount),
|
|
91
96
|
}));
|
|
97
|
+
if (turboCreditDestinationAddress !== undefined) {
|
|
98
|
+
tx.add(new TransactionInstruction({
|
|
99
|
+
programId: new PublicKey(memoProgramId),
|
|
100
|
+
keys: [],
|
|
101
|
+
data: Buffer.from('turboCreditDestinationAddress=' +
|
|
102
|
+
turboCreditDestinationAddress),
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
92
105
|
const signedTx = await this.walletAdapter.signTransaction(tx);
|
|
93
106
|
const id = await connection.sendRawTransaction(signedTx.serialize());
|
|
94
107
|
return id;
|
|
@@ -103,6 +116,7 @@ export class TurboDataItemAbstractSigner {
|
|
|
103
116
|
const { hash } = await signer.sendTransaction({
|
|
104
117
|
to: target,
|
|
105
118
|
value: parseEther(amount.toFixed(18)),
|
|
119
|
+
data: ethDataFromTurboCreditDestinationAddress(turboCreditDestinationAddress),
|
|
106
120
|
});
|
|
107
121
|
return hash;
|
|
108
122
|
}
|
|
@@ -115,6 +129,7 @@ export class TurboDataItemAbstractSigner {
|
|
|
115
129
|
const tx = await ethWalletAndProvider.sendTransaction({
|
|
116
130
|
to: target,
|
|
117
131
|
value: parseEther(amount.toFixed(18)),
|
|
132
|
+
data: ethDataFromTurboCreditDestinationAddress(turboCreditDestinationAddress),
|
|
118
133
|
});
|
|
119
134
|
this.logger.debug('Sent transaction', { tx });
|
|
120
135
|
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
|
}
|
|
@@ -40,7 +40,7 @@ export class ArweaveToken {
|
|
|
40
40
|
this.mintU = mintU;
|
|
41
41
|
this.pollingOptions = pollingOptions;
|
|
42
42
|
}
|
|
43
|
-
async createAndSubmitTx({ feeMultiplier, target, tokenAmount, signer, }) {
|
|
43
|
+
async createAndSubmitTx({ feeMultiplier, target, tokenAmount, signer, turboCreditDestinationAddress, }) {
|
|
44
44
|
const tx = await this.arweave.createTransaction({
|
|
45
45
|
target,
|
|
46
46
|
quantity: tokenAmount.toString(),
|
|
@@ -57,6 +57,9 @@ export class ArweaveToken {
|
|
|
57
57
|
tx.addTag('Contract', 'KTzTXT_ANmF84fWEKHzWURD1LWd9QaFR9yfYUwH2Lxw'); // cspell:enable
|
|
58
58
|
tx.addTag('Input', JSON.stringify({ function: 'mint' }));
|
|
59
59
|
}
|
|
60
|
+
if (turboCreditDestinationAddress !== undefined) {
|
|
61
|
+
tx.addTag('Turbo-Credit-Destination-Address', turboCreditDestinationAddress);
|
|
62
|
+
}
|
|
60
63
|
const publicKeyB64Url = toB64Url(await signer.getPublicKey());
|
|
61
64
|
tx.setOwner(publicKeyB64Url);
|
|
62
65
|
const dataToSign = await tx.getSignatureData();
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { defaultProdGatewayUrls } from '../../utils/common.js';
|
|
2
2
|
import { EthereumToken } from './ethereum.js';
|
|
3
|
+
export const defaultBaseNetworkPollingOptions = {
|
|
4
|
+
initialBackoffMs: 2_500,
|
|
5
|
+
maxAttempts: 10,
|
|
6
|
+
pollingIntervalMs: 750,
|
|
7
|
+
};
|
|
3
8
|
export class BaseEthToken extends EthereumToken {
|
|
4
|
-
constructor({ logger, gatewayUrl = defaultProdGatewayUrls['base-eth'], pollingOptions = {
|
|
5
|
-
initialBackoffMs: 2_500,
|
|
6
|
-
maxAttempts: 10,
|
|
7
|
-
pollingIntervalMs: 2_500,
|
|
8
|
-
}, } = {}) {
|
|
9
|
+
constructor({ logger, gatewayUrl = defaultProdGatewayUrls['base-eth'], pollingOptions = defaultBaseNetworkPollingOptions, } = {}) {
|
|
9
10
|
super({
|
|
10
11
|
logger,
|
|
11
12
|
gatewayUrl,
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { EthereumSigner } from '@dha-team/arbundles';
|
|
17
|
+
import { Wallet as EthereumWallet, JsonRpcProvider, ethers } from 'ethers';
|
|
18
|
+
import { isEthereumWalletAdapter, } from '../../types.js';
|
|
19
|
+
import { defaultProdGatewayUrls } from '../../utils/common.js';
|
|
20
|
+
import { TurboWinstonLogger } from '../logger.js';
|
|
21
|
+
import { EthereumToken, ethDataFromTurboCreditDestinationAddress, } from './ethereum.js';
|
|
22
|
+
export class ERC20Token extends EthereumToken {
|
|
23
|
+
constructor({ tokenContractAddress, logger = TurboWinstonLogger.default, gatewayUrl = defaultProdGatewayUrls.ethereum, pollingOptions, }) {
|
|
24
|
+
super({ logger, gatewayUrl, pollingOptions });
|
|
25
|
+
this.tokenContract = new ethers.Contract(tokenContractAddress, [
|
|
26
|
+
'function decimals() view returns (uint8)',
|
|
27
|
+
'function balanceOf(address) view returns (uint256)',
|
|
28
|
+
'function transfer(address to, uint256 value) returns (bool)',
|
|
29
|
+
], this.rpcProvider);
|
|
30
|
+
}
|
|
31
|
+
async createAndSubmitTx({ target, tokenAmount, signer, turboCreditDestinationAddress, }) {
|
|
32
|
+
try {
|
|
33
|
+
let connected;
|
|
34
|
+
let walletOrSigner;
|
|
35
|
+
if (signer.signer instanceof EthereumSigner) {
|
|
36
|
+
const provider = new JsonRpcProvider(this.gatewayUrl);
|
|
37
|
+
// 🧩 CLI / Node path
|
|
38
|
+
const keyHex = Buffer.from(signer.signer.key).toString('hex');
|
|
39
|
+
walletOrSigner = new EthereumWallet(keyHex, provider);
|
|
40
|
+
connected = this.tokenContract.connect(walletOrSigner);
|
|
41
|
+
}
|
|
42
|
+
else if (signer.walletAdapter !== undefined &&
|
|
43
|
+
isEthereumWalletAdapter(signer.walletAdapter)) {
|
|
44
|
+
walletOrSigner = signer.walletAdapter.getSigner();
|
|
45
|
+
connected = this.tokenContract.connect(walletOrSigner);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
throw new Error('Unsupported signer -- must be EthereumSigner or have a walletAdapter implementing getSigner');
|
|
49
|
+
}
|
|
50
|
+
// Encode transfer data
|
|
51
|
+
const baseTransferData = connected.interface.encodeFunctionData('transfer', [target, tokenAmount.toString()]);
|
|
52
|
+
let finalData = baseTransferData;
|
|
53
|
+
// Append optional memo data with turbo credit destination address
|
|
54
|
+
const memoData = ethDataFromTurboCreditDestinationAddress(turboCreditDestinationAddress);
|
|
55
|
+
if (memoData !== undefined) {
|
|
56
|
+
// remove the "0x" prefix and append
|
|
57
|
+
finalData += memoData.slice(2);
|
|
58
|
+
}
|
|
59
|
+
const txRequest = {
|
|
60
|
+
to: await connected.getAddress(),
|
|
61
|
+
data: finalData,
|
|
62
|
+
};
|
|
63
|
+
this.logger.debug('Submitting ERC20 transfer', {
|
|
64
|
+
target,
|
|
65
|
+
tokenAmount: tokenAmount.toString(),
|
|
66
|
+
rpcEndpoint: this.gatewayUrl,
|
|
67
|
+
txRequest,
|
|
68
|
+
});
|
|
69
|
+
const tx = await walletOrSigner.sendTransaction(txRequest);
|
|
70
|
+
this.logger.debug('ERC20 transfer submitted', {
|
|
71
|
+
txHash: tx.hash,
|
|
72
|
+
target,
|
|
73
|
+
tx,
|
|
74
|
+
});
|
|
75
|
+
return { id: tx.hash, target };
|
|
76
|
+
}
|
|
77
|
+
catch (e) {
|
|
78
|
+
this.logger.error('Error creating/submitting ERC20 tx', {
|
|
79
|
+
error: e instanceof Error ? e.message : e,
|
|
80
|
+
target,
|
|
81
|
+
tokenAmount,
|
|
82
|
+
rpcEndpoint: this.gatewayUrl,
|
|
83
|
+
});
|
|
84
|
+
throw e;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -14,34 +14,47 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
import { BigNumber } from 'bignumber.js';
|
|
17
|
-
import { ethers } from 'ethers';
|
|
17
|
+
import { ethers, hexlify, toUtf8Bytes } from 'ethers';
|
|
18
18
|
import { defaultProdGatewayUrls } from '../../utils/common.js';
|
|
19
19
|
import { TurboWinstonLogger } from '../logger.js';
|
|
20
20
|
export const weiToTokenAmount = (wei) => wei;
|
|
21
21
|
export const ETHToTokenAmount = (eth) => new BigNumber(eth).times(1e18).valueOf();
|
|
22
|
+
export const defaultEthereumPollingOptions = {
|
|
23
|
+
initialBackoffMs: 25_000,
|
|
24
|
+
maxAttempts: 10,
|
|
25
|
+
pollingIntervalMs: 1_500,
|
|
26
|
+
};
|
|
22
27
|
export class EthereumToken {
|
|
23
|
-
constructor({ logger = TurboWinstonLogger.default, gatewayUrl = defaultProdGatewayUrls.ethereum, pollingOptions = {
|
|
24
|
-
maxAttempts: 10,
|
|
25
|
-
pollingIntervalMs: 4_000,
|
|
26
|
-
initialBackoffMs: 10_000,
|
|
27
|
-
}, } = {}) {
|
|
28
|
+
constructor({ logger = TurboWinstonLogger.default, gatewayUrl = defaultProdGatewayUrls.ethereum, pollingOptions = defaultEthereumPollingOptions, } = {}) {
|
|
28
29
|
this.logger = logger;
|
|
29
30
|
this.gatewayUrl = gatewayUrl;
|
|
30
31
|
this.pollingOptions = pollingOptions;
|
|
31
32
|
this.rpcProvider = new ethers.JsonRpcProvider(gatewayUrl);
|
|
32
33
|
}
|
|
33
|
-
async createAndSubmitTx({ target, tokenAmount, signer, }) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
34
|
+
async createAndSubmitTx({ target, tokenAmount, signer, turboCreditDestinationAddress, }) {
|
|
35
|
+
try {
|
|
36
|
+
// convert wei to eth
|
|
37
|
+
const eth = tokenAmount.shiftedBy(-18);
|
|
38
|
+
const txId = await signer.sendTransaction({
|
|
39
|
+
target,
|
|
40
|
+
amount: eth,
|
|
41
|
+
gatewayUrl: this.gatewayUrl,
|
|
42
|
+
turboCreditDestinationAddress,
|
|
43
|
+
});
|
|
44
|
+
return {
|
|
45
|
+
id: txId,
|
|
46
|
+
target,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
this.logger.error('Error creating and submitting Ethereum tx', {
|
|
51
|
+
error: e instanceof Error ? e.message : e,
|
|
52
|
+
target,
|
|
53
|
+
tokenAmount,
|
|
54
|
+
rpcEndpoint: this.gatewayUrl,
|
|
55
|
+
});
|
|
56
|
+
throw e;
|
|
57
|
+
}
|
|
45
58
|
}
|
|
46
59
|
async getTxAvailability(txId) {
|
|
47
60
|
const tx = await this.rpcProvider.getTransaction(txId);
|
|
@@ -71,3 +84,9 @@ export class EthereumToken {
|
|
|
71
84
|
throw new Error(`Transaction ${txId} not found after polling!`);
|
|
72
85
|
}
|
|
73
86
|
}
|
|
87
|
+
export function ethDataFromTurboCreditDestinationAddress(turboCreditDestinationAddress) {
|
|
88
|
+
if (turboCreditDestinationAddress !== undefined) {
|
|
89
|
+
return hexlify(toUtf8Bytes('turboCreditDestinationAddress=' + turboCreditDestinationAddress));
|
|
90
|
+
}
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
@@ -6,6 +6,7 @@ import { ETHToTokenAmount, EthereumToken } from './ethereum.js';
|
|
|
6
6
|
import { KYVEToTokenAmount, KyveToken } from './kyve.js';
|
|
7
7
|
import { POLToTokenAmount, PolygonToken } from './polygon.js';
|
|
8
8
|
import { SOLToTokenAmount, SolanaToken } from './solana.js';
|
|
9
|
+
import { USDCToTokenAmount, USDCToken } from './usdc.js';
|
|
9
10
|
export const defaultTokenMap = {
|
|
10
11
|
arweave: (config) => new ArweaveToken(config),
|
|
11
12
|
ario: (config) => new ARIOToken(config),
|
|
@@ -15,8 +16,12 @@ export const defaultTokenMap = {
|
|
|
15
16
|
kyve: (config) => new KyveToken(config),
|
|
16
17
|
matic: (config) => new PolygonToken(config),
|
|
17
18
|
pol: (config) => new PolygonToken(config),
|
|
19
|
+
usdc: (config) => new USDCToken({ network: 'ethereum', ...config }),
|
|
20
|
+
'base-usdc': (config) => new USDCToken({ network: 'base', ...config }),
|
|
21
|
+
'polygon-usdc': (config) => new USDCToken({ network: 'polygon', ...config }),
|
|
18
22
|
};
|
|
19
23
|
const ethExponent = 18;
|
|
24
|
+
const usdcExponent = 6;
|
|
20
25
|
export const exponentMap = {
|
|
21
26
|
arweave: 12,
|
|
22
27
|
ario: 6,
|
|
@@ -26,6 +31,9 @@ export const exponentMap = {
|
|
|
26
31
|
kyve: 6,
|
|
27
32
|
matic: ethExponent,
|
|
28
33
|
pol: ethExponent,
|
|
34
|
+
usdc: usdcExponent,
|
|
35
|
+
'base-usdc': usdcExponent,
|
|
36
|
+
'polygon-usdc': usdcExponent,
|
|
29
37
|
};
|
|
30
38
|
export const tokenToBaseMap = {
|
|
31
39
|
arweave: (a) => ARToTokenAmount(a),
|
|
@@ -36,6 +44,9 @@ export const tokenToBaseMap = {
|
|
|
36
44
|
kyve: (a) => KYVEToTokenAmount(a),
|
|
37
45
|
matic: (a) => POLToTokenAmount(a),
|
|
38
46
|
pol: (a) => POLToTokenAmount(a),
|
|
47
|
+
usdc: (a) => USDCToTokenAmount(a),
|
|
48
|
+
'base-usdc': (a) => USDCToTokenAmount(a),
|
|
49
|
+
'polygon-usdc': (a) => USDCToTokenAmount(a),
|
|
39
50
|
};
|
|
40
51
|
export function isTokenType(token) {
|
|
41
52
|
return tokenTypes.includes(token);
|
|
@@ -2,12 +2,13 @@ import { defaultProdGatewayUrls } from '../../utils/common.js';
|
|
|
2
2
|
import { TurboWinstonLogger } from '../logger.js';
|
|
3
3
|
import { ETHToTokenAmount, EthereumToken } from './ethereum.js';
|
|
4
4
|
export const POLToTokenAmount = ETHToTokenAmount;
|
|
5
|
+
export const defaultPolygonPollingOptions = {
|
|
6
|
+
maxAttempts: 10,
|
|
7
|
+
initialBackoffMs: 5_000,
|
|
8
|
+
pollingIntervalMs: 1_000,
|
|
9
|
+
};
|
|
5
10
|
export class PolygonToken extends EthereumToken {
|
|
6
|
-
constructor({ logger = TurboWinstonLogger.default, gatewayUrl = defaultProdGatewayUrls.pol, pollingOptions = {
|
|
7
|
-
maxAttempts: 10,
|
|
8
|
-
pollingIntervalMs: 4_000,
|
|
9
|
-
initialBackoffMs: 5_000,
|
|
10
|
-
}, } = {}) {
|
|
11
|
+
constructor({ logger = TurboWinstonLogger.default, gatewayUrl = defaultProdGatewayUrls.pol, pollingOptions = defaultPolygonPollingOptions, } = {}) {
|
|
11
12
|
super({ logger, gatewayUrl, pollingOptions });
|
|
12
13
|
}
|
|
13
14
|
}
|