@atxp/worldchain 0.6.4
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 +237 -0
- package/dist/cache.js +11 -0
- package/dist/cache.js.map +1 -0
- package/dist/index.cjs +882 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +235 -0
- package/dist/index.js +869 -0
- package/dist/index.js.map +1 -0
- package/dist/mainWalletPaymentMaker.js +136 -0
- package/dist/mainWalletPaymentMaker.js.map +1 -0
- package/dist/minikit.js +254 -0
- package/dist/minikit.js.map +1 -0
- package/dist/smartWalletHelpers.js +46 -0
- package/dist/smartWalletHelpers.js.map +1 -0
- package/dist/spendPermissionShim.js +119 -0
- package/dist/spendPermissionShim.js.map +1 -0
- package/dist/worldchainAccount.js +136 -0
- package/dist/worldchainAccount.js.map +1 -0
- package/dist/worldchainPaymentMaker.js +206 -0
- package/dist/worldchainPaymentMaker.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { WORLD_CHAIN_MAINNET, USDC_CONTRACT_ADDRESS_WORLD_MAINNET } from '@atxp/client';
|
|
2
|
+
import { WorldchainPaymentMaker } from './worldchainPaymentMaker.js';
|
|
3
|
+
import { MainWalletPaymentMaker } from './mainWalletPaymentMaker.js';
|
|
4
|
+
import { generatePrivateKey } from 'viem/accounts';
|
|
5
|
+
import { requestSpendPermission } from './spendPermissionShim.js';
|
|
6
|
+
import { IntermediaryCache } from './cache.js';
|
|
7
|
+
import { toEphemeralSmartWallet } from './smartWalletHelpers.js';
|
|
8
|
+
import { ConsoleLogger, BrowserCache } from '@atxp/common';
|
|
9
|
+
|
|
10
|
+
const DEFAULT_ALLOWANCE = 10n;
|
|
11
|
+
const DEFAULT_PERIOD_IN_DAYS = 7;
|
|
12
|
+
class WorldchainAccount {
|
|
13
|
+
static toCacheKey(userWalletAddress) {
|
|
14
|
+
return `atxp-world-permission-${userWalletAddress}`;
|
|
15
|
+
}
|
|
16
|
+
static async initialize(config) {
|
|
17
|
+
const logger = config.logger || new ConsoleLogger();
|
|
18
|
+
const useEphemeralWallet = config.useEphemeralWallet ?? true;
|
|
19
|
+
const chainId = config.chainId || WORLD_CHAIN_MAINNET.id;
|
|
20
|
+
// Use World Chain Mainnet USDC address
|
|
21
|
+
const usdcAddress = USDC_CONTRACT_ADDRESS_WORLD_MAINNET;
|
|
22
|
+
// Some wallets don't support wallet_connect, so
|
|
23
|
+
// will just continue if it fails
|
|
24
|
+
try {
|
|
25
|
+
await config.provider.request({ method: 'wallet_connect' });
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
logger.warn(`wallet_connect not supported, continuing with initialization. ${error}`);
|
|
29
|
+
}
|
|
30
|
+
// If using main wallet mode, return early with main wallet payment maker
|
|
31
|
+
if (!useEphemeralWallet) {
|
|
32
|
+
logger.info(`Using main wallet mode for address: ${config.walletAddress}`);
|
|
33
|
+
return new WorldchainAccount(null, // No spend permission in main wallet mode
|
|
34
|
+
null, // No ephemeral wallet in main wallet mode
|
|
35
|
+
logger, config.walletAddress, config.provider, chainId, config.customRpcUrl);
|
|
36
|
+
}
|
|
37
|
+
// Initialize cache
|
|
38
|
+
const baseCache = config?.cache || new BrowserCache();
|
|
39
|
+
const cache = new IntermediaryCache(baseCache);
|
|
40
|
+
const cacheKey = this.toCacheKey(config.walletAddress);
|
|
41
|
+
// Try to load existing permission
|
|
42
|
+
const existingData = this.loadSavedWalletAndPermission(cache, cacheKey);
|
|
43
|
+
if (existingData) {
|
|
44
|
+
const ephemeralSmartWallet = await toEphemeralSmartWallet(existingData.privateKey);
|
|
45
|
+
return new WorldchainAccount(existingData.permission, ephemeralSmartWallet, logger, undefined, undefined, chainId, config.customRpcUrl);
|
|
46
|
+
}
|
|
47
|
+
const privateKey = generatePrivateKey();
|
|
48
|
+
const smartWallet = await toEphemeralSmartWallet(privateKey);
|
|
49
|
+
logger.info(`Generated ephemeral wallet: ${smartWallet.address}`);
|
|
50
|
+
await this.deploySmartWallet(smartWallet);
|
|
51
|
+
logger.info(`Deployed smart wallet: ${smartWallet.address}`);
|
|
52
|
+
const permission = await requestSpendPermission({
|
|
53
|
+
account: config.walletAddress,
|
|
54
|
+
spender: smartWallet.address,
|
|
55
|
+
token: usdcAddress,
|
|
56
|
+
chainId: chainId,
|
|
57
|
+
allowance: config?.allowance ?? DEFAULT_ALLOWANCE,
|
|
58
|
+
periodInDays: config?.periodInDays ?? DEFAULT_PERIOD_IN_DAYS,
|
|
59
|
+
provider: config.provider,
|
|
60
|
+
});
|
|
61
|
+
// Save wallet and permission
|
|
62
|
+
cache.set(cacheKey, { privateKey, permission });
|
|
63
|
+
return new WorldchainAccount(permission, smartWallet, logger, undefined, undefined, chainId, config.customRpcUrl);
|
|
64
|
+
}
|
|
65
|
+
static loadSavedWalletAndPermission(permissionCache, cacheKey) {
|
|
66
|
+
const cachedData = permissionCache.get(cacheKey);
|
|
67
|
+
if (!cachedData)
|
|
68
|
+
return null;
|
|
69
|
+
// Check if permission is not expired
|
|
70
|
+
const now = Math.floor(Date.now() / 1000);
|
|
71
|
+
const permissionEnd = parseInt(cachedData.permission.permission.end.toString());
|
|
72
|
+
if (permissionEnd <= now) {
|
|
73
|
+
permissionCache.delete(cacheKey);
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
return cachedData;
|
|
77
|
+
}
|
|
78
|
+
static async deploySmartWallet(smartWallet) {
|
|
79
|
+
const deployTx = await smartWallet.client.sendUserOperation({
|
|
80
|
+
calls: [{
|
|
81
|
+
to: smartWallet.address,
|
|
82
|
+
value: 0n,
|
|
83
|
+
data: '0x'
|
|
84
|
+
}]
|
|
85
|
+
// Note: World Chain may not have paymaster support initially
|
|
86
|
+
// paymaster omitted
|
|
87
|
+
});
|
|
88
|
+
const receipt = await smartWallet.client.waitForUserOperationReceipt({
|
|
89
|
+
hash: deployTx
|
|
90
|
+
});
|
|
91
|
+
if (!receipt.success) {
|
|
92
|
+
throw new Error(`Smart wallet deployment failed. Receipt: ${JSON.stringify(receipt)}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
constructor(spendPermission, ephemeralSmartWallet, logger, mainWalletAddress, provider, chainId = WORLD_CHAIN_MAINNET.id, customRpcUrl) {
|
|
96
|
+
if (ephemeralSmartWallet) {
|
|
97
|
+
// Ephemeral wallet mode
|
|
98
|
+
if (!spendPermission) {
|
|
99
|
+
throw new Error('Spend permission is required for ephemeral wallet mode');
|
|
100
|
+
}
|
|
101
|
+
this.accountId = ephemeralSmartWallet.address;
|
|
102
|
+
this.paymentMakers = {
|
|
103
|
+
'world': new WorldchainPaymentMaker(spendPermission, ephemeralSmartWallet, {
|
|
104
|
+
logger,
|
|
105
|
+
chainId,
|
|
106
|
+
customRpcUrl
|
|
107
|
+
}),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
// Main wallet mode
|
|
112
|
+
if (!mainWalletAddress || !provider) {
|
|
113
|
+
throw new Error('Main wallet address and provider are required for main wallet mode');
|
|
114
|
+
}
|
|
115
|
+
this.accountId = mainWalletAddress;
|
|
116
|
+
this.paymentMakers = {
|
|
117
|
+
'world': new MainWalletPaymentMaker(mainWalletAddress, provider, logger, chainId, customRpcUrl),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
static clearAllCachedData(userWalletAddress, cache) {
|
|
122
|
+
// In non-browser environments, require an explicit cache parameter
|
|
123
|
+
if (!cache) {
|
|
124
|
+
const browserCache = new BrowserCache();
|
|
125
|
+
// Check if BrowserCache would work (i.e., we're in a browser)
|
|
126
|
+
if (typeof window === 'undefined') {
|
|
127
|
+
throw new Error('clearAllCachedData requires a cache to be provided outside of browser environments');
|
|
128
|
+
}
|
|
129
|
+
cache = browserCache;
|
|
130
|
+
}
|
|
131
|
+
cache.delete(this.toCacheKey(userWalletAddress));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export { WorldchainAccount };
|
|
136
|
+
//# sourceMappingURL=worldchainAccount.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worldchainAccount.js","sources":["../src/worldchainAccount.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { WORLD_CHAIN_MAINNET, USDC_CONTRACT_ADDRESS_WORLD_MAINNET } from '@atxp/client';
|
|
2
|
+
import { ConsoleLogger, constructEIP1271Message, createEIP1271AuthData, createEIP1271JWT } from '@atxp/common';
|
|
3
|
+
import { encodeFunctionData, parseEther } from 'viem';
|
|
4
|
+
import { prepareSpendCallData } from './spendPermissionShim.js';
|
|
5
|
+
|
|
6
|
+
const USDC_DECIMALS = 6;
|
|
7
|
+
// Minimal ERC20 ABI for transfer function
|
|
8
|
+
const ERC20_ABI = [
|
|
9
|
+
{
|
|
10
|
+
inputs: [
|
|
11
|
+
{ name: 'to', type: 'address' },
|
|
12
|
+
{ name: 'amount', type: 'uint256' }
|
|
13
|
+
],
|
|
14
|
+
name: 'transfer',
|
|
15
|
+
outputs: [{ name: '', type: 'bool' }],
|
|
16
|
+
stateMutability: 'nonpayable',
|
|
17
|
+
type: 'function'
|
|
18
|
+
}
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* Validates confirmation delays configuration
|
|
22
|
+
*/
|
|
23
|
+
function validateConfirmationDelays(delays) {
|
|
24
|
+
if (delays.networkPropagationMs < 0) {
|
|
25
|
+
throw new Error('networkPropagationMs must be non-negative');
|
|
26
|
+
}
|
|
27
|
+
if (delays.confirmationFailedMs < 0) {
|
|
28
|
+
throw new Error('confirmationFailedMs must be non-negative');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const DEFAULT_CONFIRMATION_DELAYS = {
|
|
32
|
+
networkPropagationMs: 5000, // 5 seconds for production
|
|
33
|
+
confirmationFailedMs: 15000 // 15 seconds for production
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Gets default confirmation delays based on environment
|
|
37
|
+
*/
|
|
38
|
+
const getDefaultConfirmationDelays = () => {
|
|
39
|
+
if (process.env.NODE_ENV === 'test') {
|
|
40
|
+
return { networkPropagationMs: 10, confirmationFailedMs: 20 };
|
|
41
|
+
}
|
|
42
|
+
return DEFAULT_CONFIRMATION_DELAYS;
|
|
43
|
+
};
|
|
44
|
+
async function waitForTransactionConfirmations(smartWallet, txHash, confirmations, logger, delays = DEFAULT_CONFIRMATION_DELAYS) {
|
|
45
|
+
try {
|
|
46
|
+
const publicClient = smartWallet.client.account?.client;
|
|
47
|
+
if (publicClient && 'waitForTransactionReceipt' in publicClient) {
|
|
48
|
+
logger.info(`Waiting for ${confirmations} confirmations...`);
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
50
|
+
await publicClient.waitForTransactionReceipt({
|
|
51
|
+
hash: txHash,
|
|
52
|
+
confirmations: 1, // Reduce confirmations to speed up
|
|
53
|
+
timeout: 60000 // 60 second timeout
|
|
54
|
+
});
|
|
55
|
+
logger.info(`Transaction confirmed with 1 confirmation`);
|
|
56
|
+
// Add extra delay for network propagation
|
|
57
|
+
logger.info(`Adding ${delays.networkPropagationMs}ms delay for network propagation...`);
|
|
58
|
+
await new Promise(resolve => setTimeout(resolve, delays.networkPropagationMs));
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
logger.warn('Unable to wait for confirmations: client does not support waitForTransactionReceipt');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
logger.warn(`Could not wait for additional confirmations: ${error}`);
|
|
66
|
+
// Add longer delay if confirmation failed
|
|
67
|
+
logger.info(`Confirmation failed, adding ${delays.confirmationFailedMs}ms delay for transaction to propagate...`);
|
|
68
|
+
await new Promise(resolve => setTimeout(resolve, delays.confirmationFailedMs));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Payment maker for World Chain transactions using smart wallets and spend permissions
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* // For production use (default delays)
|
|
77
|
+
* const paymentMaker = new WorldchainPaymentMaker(permission, wallet);
|
|
78
|
+
*
|
|
79
|
+
* // For testing (fast delays)
|
|
80
|
+
* const paymentMaker = new WorldchainPaymentMaker(permission, wallet, {
|
|
81
|
+
* confirmationDelays: { networkPropagationMs: 10, confirmationFailedMs: 20 }
|
|
82
|
+
* });
|
|
83
|
+
*
|
|
84
|
+
* // With custom configuration
|
|
85
|
+
* const paymentMaker = new WorldchainPaymentMaker(permission, wallet, {
|
|
86
|
+
* chainId: 480, // World Chain Mainnet
|
|
87
|
+
* customRpcUrl: 'https://my-rpc.com',
|
|
88
|
+
* logger: myLogger
|
|
89
|
+
* });
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
class WorldchainPaymentMaker {
|
|
93
|
+
/**
|
|
94
|
+
* Creates a new WorldchainPaymentMaker instance
|
|
95
|
+
*
|
|
96
|
+
* @param spendPermission - The spend permission for transactions
|
|
97
|
+
* @param smartWallet - The smart wallet instance to use
|
|
98
|
+
* @param options - Optional configuration
|
|
99
|
+
*/
|
|
100
|
+
constructor(spendPermission, smartWallet, options = {}) {
|
|
101
|
+
if (!spendPermission) {
|
|
102
|
+
throw new Error('Spend permission is required');
|
|
103
|
+
}
|
|
104
|
+
if (!smartWallet) {
|
|
105
|
+
throw new Error('Smart wallet is required');
|
|
106
|
+
}
|
|
107
|
+
// Extract and validate options
|
|
108
|
+
const { logger, chainId = WORLD_CHAIN_MAINNET.id, customRpcUrl, confirmationDelays } = options;
|
|
109
|
+
const finalDelays = confirmationDelays ?? getDefaultConfirmationDelays();
|
|
110
|
+
validateConfirmationDelays(finalDelays);
|
|
111
|
+
this.logger = logger ?? new ConsoleLogger();
|
|
112
|
+
this.spendPermission = spendPermission;
|
|
113
|
+
this.smartWallet = smartWallet;
|
|
114
|
+
this.chainId = chainId;
|
|
115
|
+
this.customRpcUrl = customRpcUrl;
|
|
116
|
+
this.confirmationDelays = finalDelays;
|
|
117
|
+
}
|
|
118
|
+
async generateJWT({ paymentRequestId, codeChallenge }) {
|
|
119
|
+
// Generate EIP-1271 auth data for smart wallet authentication
|
|
120
|
+
const timestamp = Math.floor(Date.now() / 1000);
|
|
121
|
+
const message = constructEIP1271Message({
|
|
122
|
+
walletAddress: this.smartWallet.account.address,
|
|
123
|
+
timestamp,
|
|
124
|
+
codeChallenge,
|
|
125
|
+
paymentRequestId
|
|
126
|
+
});
|
|
127
|
+
// Sign the message - this will return an ABI-encoded signature from the smart wallet
|
|
128
|
+
const signature = await this.smartWallet.account.signMessage({
|
|
129
|
+
message: message
|
|
130
|
+
});
|
|
131
|
+
const authData = createEIP1271AuthData({
|
|
132
|
+
walletAddress: this.smartWallet.account.address,
|
|
133
|
+
message,
|
|
134
|
+
signature,
|
|
135
|
+
timestamp,
|
|
136
|
+
codeChallenge,
|
|
137
|
+
paymentRequestId
|
|
138
|
+
});
|
|
139
|
+
return createEIP1271JWT(authData);
|
|
140
|
+
}
|
|
141
|
+
async makePayment(amount, currency, receiver, memo) {
|
|
142
|
+
if (currency !== 'USDC') {
|
|
143
|
+
throw new Error('Only usdc currency is supported; received ' + currency);
|
|
144
|
+
}
|
|
145
|
+
// Use World Chain Mainnet configuration
|
|
146
|
+
const usdcAddress = USDC_CONTRACT_ADDRESS_WORLD_MAINNET;
|
|
147
|
+
// Convert amount to USDC units (6 decimals) as BigInt for spendPermission
|
|
148
|
+
const amountInUSDCUnits = BigInt(amount.multipliedBy(10 ** USDC_DECIMALS).toFixed(0));
|
|
149
|
+
const spendCalls = await prepareSpendCallData({ permission: this.spendPermission, amount: amountInUSDCUnits });
|
|
150
|
+
// Add a second call to transfer USDC from the smart wallet to the receiver
|
|
151
|
+
let transferCallData = encodeFunctionData({
|
|
152
|
+
abi: ERC20_ABI,
|
|
153
|
+
functionName: "transfer",
|
|
154
|
+
args: [receiver, amountInUSDCUnits],
|
|
155
|
+
});
|
|
156
|
+
// Append memo to transfer call data if present
|
|
157
|
+
// This works because the EVM ignores extra calldata beyond what a function expects.
|
|
158
|
+
// The ERC20 transfer() function only reads the first 68 bytes (4-byte selector + 32-byte address + 32-byte amount).
|
|
159
|
+
// Any additional data appended after those 68 bytes is safely ignored by the USDC contract
|
|
160
|
+
// but remains accessible in the transaction data for payment verification.
|
|
161
|
+
// This is a well-established pattern used by OpenSea, Uniswap, and other major protocols.
|
|
162
|
+
if (memo && memo.trim()) {
|
|
163
|
+
const memoHex = Buffer.from(memo.trim(), 'utf8').toString('hex');
|
|
164
|
+
transferCallData = (transferCallData + memoHex);
|
|
165
|
+
this.logger.info(`Added memo "${memo.trim()}" to transfer call`);
|
|
166
|
+
}
|
|
167
|
+
const transferCall = {
|
|
168
|
+
to: usdcAddress,
|
|
169
|
+
data: transferCallData,
|
|
170
|
+
value: '0x0'
|
|
171
|
+
};
|
|
172
|
+
// Combine spend permission calls with the transfer call
|
|
173
|
+
const allCalls = [...spendCalls, transferCall];
|
|
174
|
+
this.logger.info(`Executing ${allCalls.length} calls (${spendCalls.length} spend permission + 1 transfer)`);
|
|
175
|
+
const hash = await this.smartWallet.client.sendUserOperation({
|
|
176
|
+
account: this.smartWallet.account,
|
|
177
|
+
calls: allCalls.map(call => {
|
|
178
|
+
return {
|
|
179
|
+
to: call.to,
|
|
180
|
+
data: call.data,
|
|
181
|
+
value: BigInt(call.value || '0x0')
|
|
182
|
+
};
|
|
183
|
+
}),
|
|
184
|
+
maxPriorityFeePerGas: parseEther('0.000000001')
|
|
185
|
+
});
|
|
186
|
+
const receipt = await this.smartWallet.client.waitForUserOperationReceipt({ hash });
|
|
187
|
+
if (!receipt) {
|
|
188
|
+
throw new Error('User operation failed');
|
|
189
|
+
}
|
|
190
|
+
// The receipt contains the actual transaction hash that was mined on chain
|
|
191
|
+
const txHash = receipt.receipt.transactionHash;
|
|
192
|
+
if (!txHash) {
|
|
193
|
+
throw new Error('User operation was executed but no transaction hash was returned. This should not happen.');
|
|
194
|
+
}
|
|
195
|
+
this.logger.info(`Spend permission executed successfully. UserOp: ${receipt.userOpHash}, TxHash: ${txHash}`);
|
|
196
|
+
// Wait for additional confirmations to ensure the transaction is well-propagated
|
|
197
|
+
// This helps avoid the "Transaction receipt could not be found" error
|
|
198
|
+
await waitForTransactionConfirmations(this.smartWallet, txHash, 2, this.logger, this.confirmationDelays);
|
|
199
|
+
// Return the actual transaction hash, not the user operation hash
|
|
200
|
+
// The payment verification system needs the on-chain transaction hash
|
|
201
|
+
return txHash;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export { WorldchainPaymentMaker, getDefaultConfirmationDelays };
|
|
206
|
+
//# sourceMappingURL=worldchainPaymentMaker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worldchainPaymentMaker.js","sources":["../src/worldchainPaymentMaker.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@atxp/worldchain",
|
|
3
|
+
"version": "0.6.4",
|
|
4
|
+
"description": "ATXP for World Chain Mini Apps",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/atxp-dev/sdk.git",
|
|
9
|
+
"directory": "packages/atxp-worldchain"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"sideEffects": false,
|
|
13
|
+
"main": "./dist/index.cjs",
|
|
14
|
+
"module": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js",
|
|
20
|
+
"require": "./dist/index.cjs"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "rollup -c",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"lint": "eslint . --ext .ts",
|
|
30
|
+
"lint:fix": "eslint . --ext .ts --fix",
|
|
31
|
+
"test": "vitest run",
|
|
32
|
+
"prepack": "npm run build && npm run typecheck",
|
|
33
|
+
"pack:dry": "npm pack --dry-run"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@atxp/client": "0.6.4",
|
|
37
|
+
"@atxp/common": "0.6.4",
|
|
38
|
+
"bignumber.js": "^9.3.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"viem": "^2.34.0",
|
|
42
|
+
"@worldcoin/minikit-js": "^1.9.6"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/node": "^22.13.0",
|
|
46
|
+
"@types/supertest": "^6.0.3",
|
|
47
|
+
"@typescript-eslint/eslint-plugin": "^8.38.0",
|
|
48
|
+
"@typescript-eslint/parser": "^8.38.0",
|
|
49
|
+
"eslint": "^9.32.0",
|
|
50
|
+
"fetch-mock": "^12.5.2",
|
|
51
|
+
"happy-dom": "^15.11.6",
|
|
52
|
+
"jsdom": "^25.0.1",
|
|
53
|
+
"supertest": "^7.1.4",
|
|
54
|
+
"typescript": "^5.7.3",
|
|
55
|
+
"viem": "^2.34.0",
|
|
56
|
+
"vitest": "^3.0.9"
|
|
57
|
+
}
|
|
58
|
+
}
|