@atxp/polygon 0.8.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.
- package/README.md +497 -0
- package/dist/cache.d.ts +19 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +11 -0
- package/dist/cache.js.map +1 -0
- package/dist/directWalletPaymentMaker.d.ts +35 -0
- package/dist/directWalletPaymentMaker.d.ts.map +1 -0
- package/dist/directWalletPaymentMaker.js +143 -0
- package/dist/directWalletPaymentMaker.js.map +1 -0
- package/dist/index.cjs +458 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +145 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +446 -0
- package/dist/index.js.map +1 -0
- package/dist/polygonBrowserAccount.d.ts +40 -0
- package/dist/polygonBrowserAccount.d.ts.map +1 -0
- package/dist/polygonBrowserAccount.js +70 -0
- package/dist/polygonBrowserAccount.js.map +1 -0
- package/dist/polygonServerAccount.d.ts +35 -0
- package/dist/polygonServerAccount.d.ts.map +1 -0
- package/dist/polygonServerAccount.js +78 -0
- package/dist/polygonServerAccount.js.map +1 -0
- package/dist/serverPaymentMaker.d.ts +29 -0
- package/dist/serverPaymentMaker.d.ts.map +1 -0
- package/dist/serverPaymentMaker.js +173 -0
- package/dist/serverPaymentMaker.js.map +1 -0
- package/dist/smartWalletHelpers.d.ts +18 -0
- package/dist/smartWalletHelpers.d.ts.map +1 -0
- package/dist/smartWalletHelpers.js +93 -0
- package/dist/smartWalletHelpers.js.map +1 -0
- package/dist/smartWalletPaymentMaker.d.ts +29 -0
- package/dist/smartWalletPaymentMaker.d.ts.map +1 -0
- package/dist/smartWalletPaymentMaker.js +172 -0
- package/dist/smartWalletPaymentMaker.js.map +1 -0
- package/dist/spendPermissionShim.d.ts +19 -0
- package/dist/spendPermissionShim.d.ts.map +1 -0
- package/dist/spendPermissionShim.js +129 -0
- package/dist/spendPermissionShim.js.map +1 -0
- package/dist/testHelpers.d.ts +17 -0
- package/dist/testHelpers.d.ts.map +1 -0
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { getPolygonUSDCAddress } from '@atxp/client';
|
|
2
|
+
import { polygon } from 'viem/chains';
|
|
3
|
+
import { ConsoleLogger, constructEIP1271Message, createEIP1271AuthData, createEIP1271JWT } from '@atxp/common';
|
|
4
|
+
import { encodeFunctionData, parseEther } from 'viem';
|
|
5
|
+
import { prepareSpendCallData } from './spendPermissionShim.js';
|
|
6
|
+
|
|
7
|
+
const USDC_DECIMALS = 6;
|
|
8
|
+
// Minimal ERC20 ABI for transfer function
|
|
9
|
+
const ERC20_ABI = [
|
|
10
|
+
{
|
|
11
|
+
inputs: [
|
|
12
|
+
{ name: 'to', type: 'address' },
|
|
13
|
+
{ name: 'amount', type: 'uint256' }
|
|
14
|
+
],
|
|
15
|
+
name: 'transfer',
|
|
16
|
+
outputs: [{ name: '', type: 'bool' }],
|
|
17
|
+
stateMutability: 'nonpayable',
|
|
18
|
+
type: 'function'
|
|
19
|
+
}
|
|
20
|
+
];
|
|
21
|
+
async function waitForTransactionConfirmations(smartWallet, txHash, confirmations, logger) {
|
|
22
|
+
try {
|
|
23
|
+
const publicClient = smartWallet.client.account?.client;
|
|
24
|
+
if (publicClient && 'waitForTransactionReceipt' in publicClient) {
|
|
25
|
+
logger.info(`Waiting for ${confirmations} confirmations...`);
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
27
|
+
await publicClient.waitForTransactionReceipt({
|
|
28
|
+
hash: txHash,
|
|
29
|
+
confirmations: confirmations
|
|
30
|
+
});
|
|
31
|
+
logger.info(`Transaction confirmed with ${confirmations} confirmations`);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
logger.warn('Unable to wait for confirmations: client does not support waitForTransactionReceipt');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
logger.warn(`Could not wait for additional confirmations: ${error}`);
|
|
39
|
+
// Continue anyway - the transaction is already mined
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Browser-based payment maker using ephemeral smart wallets with account abstraction.
|
|
44
|
+
* Uses Coinbase CDP for gasless transactions and spend permissions.
|
|
45
|
+
*/
|
|
46
|
+
class SmartWalletPaymentMaker {
|
|
47
|
+
constructor(spendPermission, smartWallet, logger, chainId = polygon.id) {
|
|
48
|
+
if (!spendPermission) {
|
|
49
|
+
throw new Error('Spend permission is required');
|
|
50
|
+
}
|
|
51
|
+
if (!smartWallet) {
|
|
52
|
+
throw new Error('Smart wallet is required');
|
|
53
|
+
}
|
|
54
|
+
this.logger = logger ?? new ConsoleLogger();
|
|
55
|
+
this.spendPermission = spendPermission;
|
|
56
|
+
this.smartWallet = smartWallet;
|
|
57
|
+
this.chainId = chainId;
|
|
58
|
+
this.usdcAddress = getPolygonUSDCAddress(chainId);
|
|
59
|
+
}
|
|
60
|
+
getSourceAddress(_params) {
|
|
61
|
+
return this.smartWallet.account.address;
|
|
62
|
+
}
|
|
63
|
+
async generateJWT({ paymentRequestId, codeChallenge, accountId }) {
|
|
64
|
+
// Generate EIP-1271 auth data for smart wallet authentication
|
|
65
|
+
const timestamp = Math.floor(Date.now() / 1000);
|
|
66
|
+
const message = constructEIP1271Message({
|
|
67
|
+
walletAddress: this.smartWallet.account.address,
|
|
68
|
+
timestamp,
|
|
69
|
+
codeChallenge,
|
|
70
|
+
paymentRequestId,
|
|
71
|
+
...(accountId ? { accountId } : {}),
|
|
72
|
+
});
|
|
73
|
+
// Sign the message - this will return an ABI-encoded signature from the smart wallet
|
|
74
|
+
const signature = await this.smartWallet.account.signMessage({
|
|
75
|
+
message: message
|
|
76
|
+
});
|
|
77
|
+
const authData = createEIP1271AuthData({
|
|
78
|
+
walletAddress: this.smartWallet.account.address,
|
|
79
|
+
message,
|
|
80
|
+
signature,
|
|
81
|
+
timestamp,
|
|
82
|
+
codeChallenge,
|
|
83
|
+
paymentRequestId,
|
|
84
|
+
...(accountId ? { accountId } : {}),
|
|
85
|
+
});
|
|
86
|
+
const jwtToken = createEIP1271JWT(authData);
|
|
87
|
+
this.logger.info(`codeChallenge: ${codeChallenge}`);
|
|
88
|
+
this.logger.info(`paymentRequestId: ${paymentRequestId}`);
|
|
89
|
+
this.logger.info(`walletAddress: ${this.smartWallet.account.address}`);
|
|
90
|
+
this.logger.info(`Generated EIP-1271 JWT: ${jwtToken}`);
|
|
91
|
+
return jwtToken;
|
|
92
|
+
}
|
|
93
|
+
async makePayment(destinations, memo, _paymentRequestId) {
|
|
94
|
+
// Filter to polygon chain destinations
|
|
95
|
+
const polygonDestinations = destinations.filter(d => d.chain === 'polygon');
|
|
96
|
+
if (polygonDestinations.length === 0) {
|
|
97
|
+
this.logger.debug('PolygonPaymentMaker: No polygon destinations found, cannot handle payment');
|
|
98
|
+
return null; // Cannot handle these destinations
|
|
99
|
+
}
|
|
100
|
+
// Pick first polygon destination
|
|
101
|
+
const dest = polygonDestinations[0];
|
|
102
|
+
const amount = dest.amount;
|
|
103
|
+
const currency = dest.currency;
|
|
104
|
+
const receiver = dest.address;
|
|
105
|
+
if (currency !== 'USDC') {
|
|
106
|
+
throw new Error('Only usdc currency is supported; received ' + currency);
|
|
107
|
+
}
|
|
108
|
+
this.logger.info(`Making spendPermission payment of ${amount} ${currency} to ${receiver} on Polygon with memo: ${memo}`);
|
|
109
|
+
// Convert amount to USDC units (6 decimals) as BigInt for spendPermission
|
|
110
|
+
const amountInUSDCUnits = BigInt(amount.multipliedBy(10 ** USDC_DECIMALS).toFixed(0));
|
|
111
|
+
const spendCalls = await prepareSpendCallData({ permission: this.spendPermission, amount: amountInUSDCUnits });
|
|
112
|
+
// Add a second call to transfer USDC from the smart wallet to the receiver
|
|
113
|
+
let transferCallData = encodeFunctionData({
|
|
114
|
+
abi: ERC20_ABI,
|
|
115
|
+
functionName: "transfer",
|
|
116
|
+
args: [receiver, amountInUSDCUnits],
|
|
117
|
+
});
|
|
118
|
+
// Append memo to transfer call data if present
|
|
119
|
+
// This works because the EVM ignores extra calldata beyond what a function expects.
|
|
120
|
+
// The ERC20 transfer() function only reads the first 68 bytes (4-byte selector + 32-byte address + 32-byte amount).
|
|
121
|
+
// Any additional data appended after those 68 bytes is safely ignored by the USDC contract
|
|
122
|
+
// but remains accessible in the transaction data for payment verification.
|
|
123
|
+
// This is a well-established pattern used by OpenSea, Uniswap, and other major protocols.
|
|
124
|
+
if (memo && memo.trim()) {
|
|
125
|
+
const memoHex = Buffer.from(memo.trim(), 'utf8').toString('hex');
|
|
126
|
+
transferCallData = (transferCallData + memoHex);
|
|
127
|
+
this.logger.info(`Added memo "${memo.trim()}" to transfer call`);
|
|
128
|
+
}
|
|
129
|
+
const transferCall = {
|
|
130
|
+
to: this.usdcAddress,
|
|
131
|
+
data: transferCallData,
|
|
132
|
+
value: '0x0'
|
|
133
|
+
};
|
|
134
|
+
// Combine spend permission calls with the transfer call
|
|
135
|
+
const allCalls = [...spendCalls, transferCall];
|
|
136
|
+
this.logger.info(`Executing ${allCalls.length} calls (${spendCalls.length} spend permission + 1 transfer)`);
|
|
137
|
+
const hash = await this.smartWallet.client.sendUserOperation({
|
|
138
|
+
account: this.smartWallet.account,
|
|
139
|
+
calls: allCalls.map(call => {
|
|
140
|
+
return {
|
|
141
|
+
to: call.to,
|
|
142
|
+
data: call.data,
|
|
143
|
+
value: BigInt(call.value || '0x0')
|
|
144
|
+
};
|
|
145
|
+
}),
|
|
146
|
+
maxPriorityFeePerGas: parseEther('0.000000001')
|
|
147
|
+
});
|
|
148
|
+
const receipt = await this.smartWallet.client.waitForUserOperationReceipt({ hash });
|
|
149
|
+
if (!receipt) {
|
|
150
|
+
throw new Error('User operation failed');
|
|
151
|
+
}
|
|
152
|
+
// The receipt contains the actual transaction hash that was mined on chain
|
|
153
|
+
const txHash = receipt.receipt.transactionHash;
|
|
154
|
+
if (!txHash) {
|
|
155
|
+
throw new Error('User operation was executed but no transaction hash was returned. This should not happen.');
|
|
156
|
+
}
|
|
157
|
+
this.logger.info(`Spend permission executed successfully. UserOp: ${receipt.userOpHash}, TxHash: ${txHash}`);
|
|
158
|
+
// Wait for additional confirmations to ensure the transaction is well-propagated
|
|
159
|
+
// This helps avoid the "Transaction receipt could not be found" error
|
|
160
|
+
await waitForTransactionConfirmations(this.smartWallet, txHash, 2, this.logger);
|
|
161
|
+
// Return payment result with chain and currency
|
|
162
|
+
return {
|
|
163
|
+
transactionId: txHash,
|
|
164
|
+
transactionSubId: receipt.userOpHash,
|
|
165
|
+
chain: 'polygon',
|
|
166
|
+
currency: 'USDC'
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export { SmartWalletPaymentMaker };
|
|
172
|
+
//# sourceMappingURL=smartWalletPaymentMaker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smartWalletPaymentMaker.js","sources":["../src/smartWalletPaymentMaker.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Eip1193Provider, SpendPermission } from "./types.js";
|
|
2
|
+
export declare function requestSpendPermission(params: {
|
|
3
|
+
account: string;
|
|
4
|
+
spender: string;
|
|
5
|
+
token: string;
|
|
6
|
+
chainId: number;
|
|
7
|
+
allowance: bigint;
|
|
8
|
+
periodInDays: number;
|
|
9
|
+
provider: Eip1193Provider;
|
|
10
|
+
}): Promise<SpendPermission>;
|
|
11
|
+
export declare function prepareSpendCallData(params: {
|
|
12
|
+
permission: SpendPermission;
|
|
13
|
+
amount: bigint;
|
|
14
|
+
}): Promise<{
|
|
15
|
+
to: string;
|
|
16
|
+
data: string;
|
|
17
|
+
value: bigint;
|
|
18
|
+
}[]>;
|
|
19
|
+
//# sourceMappingURL=spendPermissionShim.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spendPermissionShim.d.ts","sourceRoot":"","sources":["../src/spendPermissionShim.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAqF9D,wBAAsB,sBAAsB,CAAC,MAAM,EAAE;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,eAAe,CAAC;CAC3B,GAAG,OAAO,CAAC,eAAe,CAAC,CAkC3B;AAED,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,UAAU,EAAE,eAAe,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,CAczD"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { createWalletClient, custom, encodeFunctionData } from 'viem';
|
|
2
|
+
import { polygon } from 'viem/chains';
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
This shim uses ERC20 approvals for spend permissions on Polygon.
|
|
6
|
+
|
|
7
|
+
The upside is that it will work with any Polygon wallet/connector (MetaMask, WalletConnect, etc.).
|
|
8
|
+
|
|
9
|
+
The downside is that it's a worse user experience (the user might not be familiar with ERC20 approvals,
|
|
10
|
+
and their wallet might not explain it to them), and worse security (rather than a bounded recurring limit,
|
|
11
|
+
the user needs to grant a single fixed amount up-front—renewing that amount requires a new approval).
|
|
12
|
+
*/
|
|
13
|
+
// TODO: this version of the shim will only work for the initial approval. we need logic that can conditionally
|
|
14
|
+
// require new approvals when the old one runs out. (this will require changing the API for this module, or at
|
|
15
|
+
// least introducting a new function)
|
|
16
|
+
// Minimal ERC20 ABI for approve and transferFrom functions
|
|
17
|
+
const ERC20_ABI = [
|
|
18
|
+
{
|
|
19
|
+
"constant": false,
|
|
20
|
+
"inputs": [
|
|
21
|
+
{
|
|
22
|
+
"name": "_spender",
|
|
23
|
+
"type": "address"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "_value",
|
|
27
|
+
"type": "uint256"
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"name": "approve",
|
|
31
|
+
"outputs": [
|
|
32
|
+
{
|
|
33
|
+
"name": "",
|
|
34
|
+
"type": "bool"
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"payable": false,
|
|
38
|
+
"stateMutability": "nonpayable",
|
|
39
|
+
"type": "function"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"constant": false,
|
|
43
|
+
"inputs": [
|
|
44
|
+
{
|
|
45
|
+
"name": "_from",
|
|
46
|
+
"type": "address"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "_to",
|
|
50
|
+
"type": "address"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "_value",
|
|
54
|
+
"type": "uint256"
|
|
55
|
+
}
|
|
56
|
+
],
|
|
57
|
+
"name": "transferFrom",
|
|
58
|
+
"outputs": [
|
|
59
|
+
{
|
|
60
|
+
"name": "",
|
|
61
|
+
"type": "bool"
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
"payable": false,
|
|
65
|
+
"stateMutability": "nonpayable",
|
|
66
|
+
"type": "function"
|
|
67
|
+
}
|
|
68
|
+
];
|
|
69
|
+
/**
|
|
70
|
+
* Get Polygon chain configuration by chain ID
|
|
71
|
+
*/
|
|
72
|
+
function getPolygonChainConfig(chainId) {
|
|
73
|
+
switch (chainId) {
|
|
74
|
+
case 137: // Polygon mainnet
|
|
75
|
+
return polygon;
|
|
76
|
+
default:
|
|
77
|
+
throw new Error(`Unsupported Polygon Chain ID: ${chainId}. Supported chains: 137 (mainnet)`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async function requestSpendPermission(params) {
|
|
81
|
+
// Validate chain ID and get chain config
|
|
82
|
+
const chainConfig = getPolygonChainConfig(params.chainId);
|
|
83
|
+
const client = createWalletClient({
|
|
84
|
+
chain: chainConfig,
|
|
85
|
+
transport: custom(params.provider)
|
|
86
|
+
});
|
|
87
|
+
// Use the client
|
|
88
|
+
const hash = await client.sendTransaction({
|
|
89
|
+
account: params.account,
|
|
90
|
+
to: params.token,
|
|
91
|
+
data: encodeFunctionData({
|
|
92
|
+
abi: ERC20_ABI,
|
|
93
|
+
functionName: "approve",
|
|
94
|
+
args: [params.spender, params.allowance]
|
|
95
|
+
})
|
|
96
|
+
});
|
|
97
|
+
return {
|
|
98
|
+
permission: {
|
|
99
|
+
account: params.account,
|
|
100
|
+
spender: params.spender,
|
|
101
|
+
token: params.token,
|
|
102
|
+
allowance: params.allowance.toString(),
|
|
103
|
+
period: params.periodInDays * 24 * 60 * 60,
|
|
104
|
+
start: Math.floor(Date.now() / 1000),
|
|
105
|
+
end: Math.floor(Date.now() / 1000) + params.periodInDays * 24 * 60 * 60,
|
|
106
|
+
salt: '0x0',
|
|
107
|
+
extraData: '0x0'
|
|
108
|
+
},
|
|
109
|
+
signature: hash
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
async function prepareSpendCallData(params) {
|
|
113
|
+
// this introduces an extra layer of indirection: user wallet -> ephemeral wallet -> receiver
|
|
114
|
+
// but the reason for this is that we can't sign JWTs from the user wallet directly
|
|
115
|
+
return [
|
|
116
|
+
{
|
|
117
|
+
to: params.permission.permission.token,
|
|
118
|
+
data: encodeFunctionData({
|
|
119
|
+
abi: ERC20_ABI,
|
|
120
|
+
functionName: 'transferFrom',
|
|
121
|
+
args: [params.permission.permission.account, params.permission.permission.spender, params.amount]
|
|
122
|
+
}),
|
|
123
|
+
value: BigInt(0)
|
|
124
|
+
}
|
|
125
|
+
];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export { prepareSpendCallData, requestSpendPermission };
|
|
129
|
+
//# sourceMappingURL=spendPermissionShim.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spendPermissionShim.js","sources":["../src/spendPermissionShim.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Mock } from 'vitest';
|
|
2
|
+
import type { Eip1193Provider } from './types.js';
|
|
3
|
+
import type { Hex } from 'viem';
|
|
4
|
+
export type MockEip1193Provider = Eip1193Provider & {
|
|
5
|
+
request: Mock;
|
|
6
|
+
};
|
|
7
|
+
export declare const TEST_WALLET_ADDRESS: Hex;
|
|
8
|
+
export declare const TEST_RECEIVER_ADDRESS: Hex;
|
|
9
|
+
export declare const TEST_PRIVATE_KEY: Hex;
|
|
10
|
+
export declare function mockLogger(): {
|
|
11
|
+
info: Mock<(...args: any[]) => any>;
|
|
12
|
+
warn: Mock<(...args: any[]) => any>;
|
|
13
|
+
error: Mock<(...args: any[]) => any>;
|
|
14
|
+
debug: Mock<(...args: any[]) => any>;
|
|
15
|
+
};
|
|
16
|
+
export declare function mockProvider(overrides?: Partial<MockEip1193Provider>): MockEip1193Provider;
|
|
17
|
+
//# sourceMappingURL=testHelpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testHelpers.d.ts","sourceRoot":"","sources":["../src/testHelpers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAGhC,MAAM,MAAM,mBAAmB,GAAG,eAAe,GAAG;IAClD,OAAO,EAAE,IAAI,CAAC;CACf,CAAC;AAGF,eAAO,MAAM,mBAAmB,EAAmD,GAAG,CAAC;AACvF,eAAO,MAAM,qBAAqB,EAAmD,GAAG,CAAC;AACzF,eAAO,MAAM,gBAAgB,EAA2E,GAAG,CAAC;AAG5G,wBAAgB,UAAU;;;;;EAOzB;AAGD,wBAAgB,YAAY,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAAG,mBAAmB,CAK1F"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EIP-1193 compliant Ethereum provider interface
|
|
3
|
+
* Used for browser wallet integrations
|
|
4
|
+
*/
|
|
5
|
+
export type Eip1193Provider = {
|
|
6
|
+
request: (params: {
|
|
7
|
+
method: string;
|
|
8
|
+
params?: unknown[];
|
|
9
|
+
}) => Promise<unknown>;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,CAAC,MAAM,EAAE;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;KACpB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACxB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@atxp/polygon",
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"description": "ATXP for Polygon",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/atxp-dev/sdk.git",
|
|
9
|
+
"directory": "packages/atxp-polygon"
|
|
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.8.0",
|
|
37
|
+
"@atxp/common": "0.8.0",
|
|
38
|
+
"bignumber.js": "^9.3.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"viem": "^2.34.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/node": "^22.13.0",
|
|
45
|
+
"@types/supertest": "^6.0.3",
|
|
46
|
+
"@typescript-eslint/eslint-plugin": "^8.38.0",
|
|
47
|
+
"@typescript-eslint/parser": "^8.38.0",
|
|
48
|
+
"eslint": "^9.32.0",
|
|
49
|
+
"fetch-mock": "^12.5.2",
|
|
50
|
+
"happy-dom": "^15.11.6",
|
|
51
|
+
"jsdom": "^25.0.1",
|
|
52
|
+
"supertest": "^7.1.4",
|
|
53
|
+
"typescript": "^5.7.3",
|
|
54
|
+
"viem": "^2.34.0",
|
|
55
|
+
"vitest": "^3.0.9"
|
|
56
|
+
}
|
|
57
|
+
}
|