@azeth/sdk 0.2.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/LICENSE +21 -0
- package/README.md +139 -0
- package/dist/account/balance.d.ts +41 -0
- package/dist/account/balance.d.ts.map +1 -0
- package/dist/account/balance.js +264 -0
- package/dist/account/balance.js.map +1 -0
- package/dist/account/create.d.ts +27 -0
- package/dist/account/create.d.ts.map +1 -0
- package/dist/account/create.js +116 -0
- package/dist/account/create.js.map +1 -0
- package/dist/account/deposit.d.ts +34 -0
- package/dist/account/deposit.d.ts.map +1 -0
- package/dist/account/deposit.js +88 -0
- package/dist/account/deposit.js.map +1 -0
- package/dist/account/guardian-approval.d.ts +111 -0
- package/dist/account/guardian-approval.d.ts.map +1 -0
- package/dist/account/guardian-approval.js +223 -0
- package/dist/account/guardian-approval.js.map +1 -0
- package/dist/account/guardian.d.ts +27 -0
- package/dist/account/guardian.d.ts.map +1 -0
- package/dist/account/guardian.js +67 -0
- package/dist/account/guardian.js.map +1 -0
- package/dist/account/history.d.ts +22 -0
- package/dist/account/history.d.ts.map +1 -0
- package/dist/account/history.js +144 -0
- package/dist/account/history.js.map +1 -0
- package/dist/account/transfer.d.ts +28 -0
- package/dist/account/transfer.d.ts.map +1 -0
- package/dist/account/transfer.js +137 -0
- package/dist/account/transfer.js.map +1 -0
- package/dist/auth/erc8128.d.ts +14 -0
- package/dist/auth/erc8128.d.ts.map +1 -0
- package/dist/auth/erc8128.js +92 -0
- package/dist/auth/erc8128.js.map +1 -0
- package/dist/client.d.ts +394 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +970 -0
- package/dist/client.js.map +1 -0
- package/dist/events/emitter.d.ts +96 -0
- package/dist/events/emitter.d.ts.map +1 -0
- package/dist/events/emitter.js +90 -0
- package/dist/events/emitter.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/messaging/message-router.d.ts +69 -0
- package/dist/messaging/message-router.d.ts.map +1 -0
- package/dist/messaging/message-router.js +307 -0
- package/dist/messaging/message-router.js.map +1 -0
- package/dist/messaging/rate-limiter.d.ts +31 -0
- package/dist/messaging/rate-limiter.d.ts.map +1 -0
- package/dist/messaging/rate-limiter.js +74 -0
- package/dist/messaging/rate-limiter.js.map +1 -0
- package/dist/messaging/xmtp.d.ts +144 -0
- package/dist/messaging/xmtp.d.ts.map +1 -0
- package/dist/messaging/xmtp.js +473 -0
- package/dist/messaging/xmtp.js.map +1 -0
- package/dist/payments/agreements.d.ts +87 -0
- package/dist/payments/agreements.d.ts.map +1 -0
- package/dist/payments/agreements.js +337 -0
- package/dist/payments/agreements.js.map +1 -0
- package/dist/payments/budget.d.ts +118 -0
- package/dist/payments/budget.d.ts.map +1 -0
- package/dist/payments/budget.js +176 -0
- package/dist/payments/budget.js.map +1 -0
- package/dist/payments/smart-fetch.d.ts +65 -0
- package/dist/payments/smart-fetch.d.ts.map +1 -0
- package/dist/payments/smart-fetch.js +115 -0
- package/dist/payments/smart-fetch.js.map +1 -0
- package/dist/payments/x402.d.ts +89 -0
- package/dist/payments/x402.d.ts.map +1 -0
- package/dist/payments/x402.js +620 -0
- package/dist/payments/x402.js.map +1 -0
- package/dist/registry/discover.d.ts +43 -0
- package/dist/registry/discover.d.ts.map +1 -0
- package/dist/registry/discover.js +272 -0
- package/dist/registry/discover.js.map +1 -0
- package/dist/registry/register.d.ts +44 -0
- package/dist/registry/register.d.ts.map +1 -0
- package/dist/registry/register.js +126 -0
- package/dist/registry/register.js.map +1 -0
- package/dist/reputation/opinion.d.ts +52 -0
- package/dist/reputation/opinion.d.ts.map +1 -0
- package/dist/reputation/opinion.js +198 -0
- package/dist/reputation/opinion.js.map +1 -0
- package/dist/utils/addresses.d.ts +6 -0
- package/dist/utils/addresses.d.ts.map +1 -0
- package/dist/utils/addresses.js +53 -0
- package/dist/utils/addresses.js.map +1 -0
- package/dist/utils/errors.d.ts +23 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +188 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/execution.d.ts +20 -0
- package/dist/utils/execution.d.ts.map +1 -0
- package/dist/utils/execution.js +28 -0
- package/dist/utils/execution.js.map +1 -0
- package/dist/utils/paymaster.d.ts +35 -0
- package/dist/utils/paymaster.d.ts.map +1 -0
- package/dist/utils/paymaster.js +115 -0
- package/dist/utils/paymaster.js.map +1 -0
- package/dist/utils/retry.d.ts +19 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +68 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/userop.d.ts +55 -0
- package/dist/utils/userop.d.ts.map +1 -0
- package/dist/utils/userop.js +201 -0
- package/dist/utils/userop.js.map +1 -0
- package/dist/utils/validation.d.ts +8 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +35 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { withRetry } from '../utils/retry.js';
|
|
2
|
+
/** Pad an address to a 32-byte hex topic for event log filtering */
|
|
3
|
+
function addressToTopic(addr) {
|
|
4
|
+
return `0x000000000000000000000000${addr.slice(2).toLowerCase()}`;
|
|
5
|
+
}
|
|
6
|
+
/** Get transaction history for an account
|
|
7
|
+
* Tries server API first, then falls back to on-chain TransferRecorded events
|
|
8
|
+
* from the ReputationModule (last ~1000 blocks).
|
|
9
|
+
*/
|
|
10
|
+
export async function getHistory(publicClient, account, serverUrl, params, reputationModuleAddress, tokenAddresses) {
|
|
11
|
+
// If server URL is provided, try the indexed API (fall back gracefully if unreachable)
|
|
12
|
+
if (serverUrl) {
|
|
13
|
+
const queryParams = new URLSearchParams();
|
|
14
|
+
queryParams.set('address', account);
|
|
15
|
+
if (params?.limit)
|
|
16
|
+
queryParams.set('limit', params.limit.toString());
|
|
17
|
+
if (params?.offset)
|
|
18
|
+
queryParams.set('offset', params.offset.toString());
|
|
19
|
+
try {
|
|
20
|
+
const response = await withRetry(() => fetch(`${serverUrl}/api/v1/history?${queryParams}`));
|
|
21
|
+
if (response.ok) {
|
|
22
|
+
return await response.json();
|
|
23
|
+
}
|
|
24
|
+
// Non-OK response — fall through to on-chain fallback
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// Server unreachable — fall through to on-chain fallback
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// On-chain fallback: query TransferRecorded events from ReputationModule
|
|
31
|
+
if (!reputationModuleAddress)
|
|
32
|
+
return [];
|
|
33
|
+
try {
|
|
34
|
+
const currentBlock = await publicClient.getBlockNumber();
|
|
35
|
+
const fromBlock = params?.fromBlock ?? (currentBlock > 50000n ? currentBlock - 50000n : 0n);
|
|
36
|
+
const limit = params?.limit ?? 50;
|
|
37
|
+
const accountTopic = addressToTopic(account);
|
|
38
|
+
// Query outgoing (from=account) and incoming (to=account) in parallel
|
|
39
|
+
const [outgoing, incoming] = await Promise.all([
|
|
40
|
+
publicClient.getLogs({
|
|
41
|
+
address: reputationModuleAddress,
|
|
42
|
+
event: {
|
|
43
|
+
type: 'event',
|
|
44
|
+
name: 'TransferRecorded',
|
|
45
|
+
inputs: [
|
|
46
|
+
{ name: 'from', type: 'address', indexed: true },
|
|
47
|
+
{ name: 'to', type: 'address', indexed: true },
|
|
48
|
+
{ name: 'token', type: 'address', indexed: true },
|
|
49
|
+
{ name: 'amount', type: 'uint256', indexed: false },
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
args: { from: account },
|
|
53
|
+
fromBlock,
|
|
54
|
+
toBlock: currentBlock,
|
|
55
|
+
}).catch(() => []),
|
|
56
|
+
publicClient.getLogs({
|
|
57
|
+
address: reputationModuleAddress,
|
|
58
|
+
event: {
|
|
59
|
+
type: 'event',
|
|
60
|
+
name: 'TransferRecorded',
|
|
61
|
+
inputs: [
|
|
62
|
+
{ name: 'from', type: 'address', indexed: true },
|
|
63
|
+
{ name: 'to', type: 'address', indexed: true },
|
|
64
|
+
{ name: 'token', type: 'address', indexed: true },
|
|
65
|
+
{ name: 'amount', type: 'uint256', indexed: false },
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
args: { to: account },
|
|
69
|
+
fromBlock,
|
|
70
|
+
toBlock: currentBlock,
|
|
71
|
+
}).catch(() => []),
|
|
72
|
+
]);
|
|
73
|
+
// Query standard ERC-20 Transfer events for incoming deposits.
|
|
74
|
+
// Deposits via direct ERC-20 transfer() bypass the ReputationModule Hook,
|
|
75
|
+
// so no TransferRecorded events are emitted — we must query Transfer events directly.
|
|
76
|
+
const validTokens = (tokenAddresses ?? []).filter(Boolean);
|
|
77
|
+
const depositLogs = validTokens.length > 0
|
|
78
|
+
? (await Promise.all(validTokens.map(tokenAddr => publicClient.getLogs({
|
|
79
|
+
address: tokenAddr,
|
|
80
|
+
event: {
|
|
81
|
+
type: 'event',
|
|
82
|
+
name: 'Transfer',
|
|
83
|
+
inputs: [
|
|
84
|
+
{ name: 'from', type: 'address', indexed: true },
|
|
85
|
+
{ name: 'to', type: 'address', indexed: true },
|
|
86
|
+
{ name: 'value', type: 'uint256', indexed: false },
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
args: { to: account },
|
|
90
|
+
fromBlock,
|
|
91
|
+
toBlock: currentBlock,
|
|
92
|
+
}).catch(() => [])))).flat()
|
|
93
|
+
: [];
|
|
94
|
+
// Merge, deduplicate by txHash, and sort by blockNumber descending
|
|
95
|
+
const allLogs = [...outgoing, ...incoming, ...depositLogs];
|
|
96
|
+
const seen = new Set();
|
|
97
|
+
const records = [];
|
|
98
|
+
for (const log of allLogs) {
|
|
99
|
+
const txHash = log.transactionHash;
|
|
100
|
+
if (!txHash || seen.has(txHash))
|
|
101
|
+
continue;
|
|
102
|
+
seen.add(txHash);
|
|
103
|
+
const args = log.args;
|
|
104
|
+
const from = args.from;
|
|
105
|
+
const to = args.to;
|
|
106
|
+
// TransferRecorded has 'token' and 'amount'; ERC-20 Transfer has 'value' and log.address is the token
|
|
107
|
+
const token = args.token ?? (args.value !== undefined ? log.address : null);
|
|
108
|
+
const amount = args.amount ?? args.value;
|
|
109
|
+
records.push({
|
|
110
|
+
hash: txHash,
|
|
111
|
+
from: from ?? account,
|
|
112
|
+
to: to ?? null,
|
|
113
|
+
value: amount ?? 0n,
|
|
114
|
+
token: token ?? null,
|
|
115
|
+
blockNumber: log.blockNumber ?? 0n,
|
|
116
|
+
timestamp: 0,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
// Sort by blockNumber descending (most recent first) and apply limit
|
|
120
|
+
records.sort((a, b) => Number(b.blockNumber - a.blockNumber));
|
|
121
|
+
const sliced = records.slice(0, limit);
|
|
122
|
+
// Batch-fetch block timestamps for unique block numbers
|
|
123
|
+
const uniqueBlocks = [...new Set(sliced.map(r => r.blockNumber))];
|
|
124
|
+
const blockTimestamps = new Map();
|
|
125
|
+
await Promise.all(uniqueBlocks.map(async (blockNumber) => {
|
|
126
|
+
try {
|
|
127
|
+
const block = await publicClient.getBlock({ blockNumber });
|
|
128
|
+
blockTimestamps.set(blockNumber, Number(block.timestamp));
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// If block fetch fails, leave timestamp as 0
|
|
132
|
+
}
|
|
133
|
+
}));
|
|
134
|
+
for (const record of sliced) {
|
|
135
|
+
record.timestamp = blockTimestamps.get(record.blockNumber) ?? 0;
|
|
136
|
+
}
|
|
137
|
+
return sliced;
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// On-chain fallback failed — return empty
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/account/history.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type PublicClient, type Chain, type Transport } from 'viem';
|
|
2
|
+
import { type AzethContractAddresses } from '@azeth/common';
|
|
3
|
+
import type { AzethSmartAccountClient } from '../utils/userop.js';
|
|
4
|
+
export interface TransferParams {
|
|
5
|
+
to: `0x${string}`;
|
|
6
|
+
token?: `0x${string}`;
|
|
7
|
+
amount: bigint;
|
|
8
|
+
}
|
|
9
|
+
export interface TransferResult {
|
|
10
|
+
txHash: `0x${string}`;
|
|
11
|
+
from: `0x${string}`;
|
|
12
|
+
to: `0x${string}`;
|
|
13
|
+
amount: bigint;
|
|
14
|
+
token: `0x${string}` | 'ETH';
|
|
15
|
+
}
|
|
16
|
+
/** Transfer ETH or ERC-20 tokens via the smart account using ERC-4337 UserOperations.
|
|
17
|
+
*
|
|
18
|
+
* The SmartAccountClient builds a UserOperation that routes through EntryPoint v0.7,
|
|
19
|
+
* which calls executeUserOp() on the AzethAccount. This is the ONLY authorized path
|
|
20
|
+
* for state-changing operations on AzethAccount v12.
|
|
21
|
+
*
|
|
22
|
+
* When publicClient and addresses are provided, a pre-flight guardrail check is
|
|
23
|
+
* performed via GuardianModule.checkOperation() to catch spending limit / whitelist
|
|
24
|
+
* failures with descriptive errors instead of the opaque "AA24 signature error".
|
|
25
|
+
*
|
|
26
|
+
* M-12 fix (Audit #8): Rejects zero-amount transfers at the raw function level. */
|
|
27
|
+
export declare function transfer(smartAccountClient: AzethSmartAccountClient, smartAccount: `0x${string}`, params: TransferParams, publicClient?: PublicClient<Transport, Chain>, addresses?: AzethContractAddresses): Promise<TransferResult>;
|
|
28
|
+
//# sourceMappingURL=transfer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transfer.d.ts","sourceRoot":"","sources":["../../src/account/transfer.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,KAAK,SAAS,EACf,MAAM,MAAM,CAAC;AACd,OAAO,EAAiC,KAAK,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAG3F,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAElE,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,KAAK,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,KAAK,MAAM,EAAE,CAAC;IACtB,IAAI,EAAE,KAAK,MAAM,EAAE,CAAC;IACpB,EAAE,EAAE,KAAK,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,MAAM,EAAE,GAAG,KAAK,CAAC;CAC9B;AAoFD;;;;;;;;;;oFAUoF;AACpF,wBAAsB,QAAQ,CAC5B,kBAAkB,EAAE,uBAAuB,EAC3C,YAAY,EAAE,KAAK,MAAM,EAAE,EAC3B,MAAM,EAAE,cAAc,EACtB,YAAY,CAAC,EAAE,YAAY,CAAC,SAAS,EAAE,KAAK,CAAC,EAC7C,SAAS,CAAC,EAAE,sBAAsB,GACjC,OAAO,CAAC,cAAc,CAAC,CAyDzB"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { erc20Abi, encodeFunctionData, } from 'viem';
|
|
2
|
+
import { AzethError, formatTokenAmount } from '@azeth/common';
|
|
3
|
+
import { GuardianModuleAbi } from '@azeth/common/abis';
|
|
4
|
+
import { requireAddress } from '../utils/addresses.js';
|
|
5
|
+
/** Reason enum values matching GuardianModule.ValidationReason order */
|
|
6
|
+
const VALIDATION_REASON_NAMES = [
|
|
7
|
+
'OK',
|
|
8
|
+
'ACCOUNT_NOT_INITIALIZED',
|
|
9
|
+
'EXCEEDS_TX_LIMIT',
|
|
10
|
+
'EXCEEDS_DAILY_LIMIT',
|
|
11
|
+
'TARGET_NOT_WHITELISTED',
|
|
12
|
+
'ORACLE_STALE',
|
|
13
|
+
'GUARDIAN_REQUIRED',
|
|
14
|
+
];
|
|
15
|
+
/** Format a bigint 18-decimal USD value for human-readable error messages */
|
|
16
|
+
function formatUSD(value18) {
|
|
17
|
+
return `$${formatTokenAmount(value18, 18, 2)}`;
|
|
18
|
+
}
|
|
19
|
+
/** Pre-flight guardrail check via GuardianModule.checkOperation().
|
|
20
|
+
*
|
|
21
|
+
* Calls the on-chain view function to get structured validation reasons before
|
|
22
|
+
* submitting a UserOp that would fail with the opaque "AA24 signature error".
|
|
23
|
+
*
|
|
24
|
+
* @throws AzethError('GUARDIAN_REJECTED') with human-readable details */
|
|
25
|
+
async function preflightCheck(publicClient, addresses, smartAccount, params) {
|
|
26
|
+
const guardianAddress = requireAddress(addresses, 'guardianModule');
|
|
27
|
+
// Map transfer params to checkOperation params:
|
|
28
|
+
// ETH: target=recipient, value=amount, token=address(0)
|
|
29
|
+
// ERC-20: target=tokenContract, value=amount, token=tokenContract
|
|
30
|
+
const target = params.token ?? params.to;
|
|
31
|
+
const token = params.token ?? '0x0000000000000000000000000000000000000000';
|
|
32
|
+
const [reason, details] = await publicClient.readContract({
|
|
33
|
+
address: guardianAddress,
|
|
34
|
+
abi: GuardianModuleAbi,
|
|
35
|
+
functionName: 'checkOperation',
|
|
36
|
+
args: [smartAccount, target, params.amount, token],
|
|
37
|
+
});
|
|
38
|
+
if (reason === 0)
|
|
39
|
+
return; // OK
|
|
40
|
+
const reasonName = VALIDATION_REASON_NAMES[reason] ?? 'UNKNOWN';
|
|
41
|
+
const errorDetails = {
|
|
42
|
+
reason: reasonName,
|
|
43
|
+
usdValue: formatUSD(details.usdValue),
|
|
44
|
+
dailySpentUSD: formatUSD(details.dailySpentUSD),
|
|
45
|
+
maxTxAmountUSD: formatUSD(details.maxTxAmountUSD),
|
|
46
|
+
dailySpendLimitUSD: formatUSD(details.dailySpendLimitUSD),
|
|
47
|
+
};
|
|
48
|
+
let message;
|
|
49
|
+
switch (reason) {
|
|
50
|
+
case 1: // ACCOUNT_NOT_INITIALIZED
|
|
51
|
+
message = `Smart account ${smartAccount} is not initialized with GuardianModule`;
|
|
52
|
+
break;
|
|
53
|
+
case 2: // EXCEEDS_TX_LIMIT
|
|
54
|
+
message = `Transfer of ${formatUSD(details.usdValue)} exceeds per-transaction limit of ${formatUSD(details.maxTxAmountUSD)}`;
|
|
55
|
+
break;
|
|
56
|
+
case 3: // EXCEEDS_DAILY_LIMIT
|
|
57
|
+
message = `Transfer of ${formatUSD(details.usdValue)} would push daily spend to ${formatUSD(details.dailySpentUSD + details.usdValue)}, exceeding daily limit of ${formatUSD(details.dailySpendLimitUSD)}`;
|
|
58
|
+
break;
|
|
59
|
+
case 4: // TARGET_NOT_WHITELISTED
|
|
60
|
+
message = `Target ${target} is not in the token or protocol whitelist. Add it via setTokenWhitelist() or setProtocolWhitelist()`;
|
|
61
|
+
break;
|
|
62
|
+
case 5: // ORACLE_STALE
|
|
63
|
+
message = `Price oracle is stale — guardian co-signature required for this transfer`;
|
|
64
|
+
break;
|
|
65
|
+
case 6: // GUARDIAN_REQUIRED
|
|
66
|
+
message = `Guardian co-signature required for this operation`;
|
|
67
|
+
break;
|
|
68
|
+
default:
|
|
69
|
+
message = `Guardian validation failed (reason ${reason})`;
|
|
70
|
+
}
|
|
71
|
+
throw new AzethError(message, 'GUARDIAN_REJECTED', errorDetails);
|
|
72
|
+
}
|
|
73
|
+
/** Transfer ETH or ERC-20 tokens via the smart account using ERC-4337 UserOperations.
|
|
74
|
+
*
|
|
75
|
+
* The SmartAccountClient builds a UserOperation that routes through EntryPoint v0.7,
|
|
76
|
+
* which calls executeUserOp() on the AzethAccount. This is the ONLY authorized path
|
|
77
|
+
* for state-changing operations on AzethAccount v12.
|
|
78
|
+
*
|
|
79
|
+
* When publicClient and addresses are provided, a pre-flight guardrail check is
|
|
80
|
+
* performed via GuardianModule.checkOperation() to catch spending limit / whitelist
|
|
81
|
+
* failures with descriptive errors instead of the opaque "AA24 signature error".
|
|
82
|
+
*
|
|
83
|
+
* M-12 fix (Audit #8): Rejects zero-amount transfers at the raw function level. */
|
|
84
|
+
export async function transfer(smartAccountClient, smartAccount, params, publicClient, addresses) {
|
|
85
|
+
// M-12 fix (Audit #8): Block negative amounts (bigint can go negative).
|
|
86
|
+
// AUDIT-FIX: Also reject zero-amount transfers — they waste gas on a no-op.
|
|
87
|
+
if (params.amount <= 0n) {
|
|
88
|
+
throw new AzethError('Transfer amount must be greater than zero', 'INVALID_INPUT', { field: 'amount' });
|
|
89
|
+
}
|
|
90
|
+
// Pre-flight guardrail check: catch spending limit / whitelist failures early
|
|
91
|
+
// with descriptive errors instead of opaque "AA24 signature error".
|
|
92
|
+
if (publicClient && addresses) {
|
|
93
|
+
await preflightCheck(publicClient, addresses, smartAccount, params);
|
|
94
|
+
}
|
|
95
|
+
let txHash;
|
|
96
|
+
try {
|
|
97
|
+
if (!params.token) {
|
|
98
|
+
// ETH transfer via UserOp: sendTransaction encodes via account.encodeCalls()
|
|
99
|
+
// which wraps in AzethAccount.execute(mode, encodeSingle(to, amount, "0x"))
|
|
100
|
+
txHash = await smartAccountClient.sendTransaction({
|
|
101
|
+
to: params.to,
|
|
102
|
+
value: params.amount,
|
|
103
|
+
data: '0x',
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
// ERC-20 transfer via UserOp: encode the ERC-20 transfer call,
|
|
108
|
+
// then sendTransaction wraps in execute(mode, encodeSingle(token, 0, data))
|
|
109
|
+
const data = encodeFunctionData({
|
|
110
|
+
abi: erc20Abi,
|
|
111
|
+
functionName: 'transfer',
|
|
112
|
+
args: [params.to, params.amount],
|
|
113
|
+
});
|
|
114
|
+
txHash = await smartAccountClient.sendTransaction({
|
|
115
|
+
to: params.token,
|
|
116
|
+
value: 0n,
|
|
117
|
+
data,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
if (err instanceof AzethError)
|
|
123
|
+
throw err;
|
|
124
|
+
const message = err instanceof Error ? err.message : 'Transfer failed';
|
|
125
|
+
const isInsufficientFunds = message.toLowerCase().includes('insufficient') ||
|
|
126
|
+
message.toLowerCase().includes('exceeds balance');
|
|
127
|
+
throw new AzethError(message, isInsufficientFunds ? 'INSUFFICIENT_BALANCE' : 'CONTRACT_ERROR', { originalError: err instanceof Error ? err.name : undefined });
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
txHash,
|
|
131
|
+
from: smartAccount,
|
|
132
|
+
to: params.to,
|
|
133
|
+
amount: params.amount,
|
|
134
|
+
token: params.token ?? 'ETH',
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=transfer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transfer.js","sourceRoot":"","sources":["../../src/account/transfer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type WalletClient, type Chain, type Transport, type Account } from 'viem';
|
|
2
|
+
export interface SignedRequest {
|
|
3
|
+
signature: `0x${string}`;
|
|
4
|
+
nonce: string;
|
|
5
|
+
created: number;
|
|
6
|
+
keyid: `0x${string}`;
|
|
7
|
+
}
|
|
8
|
+
/** Sign an HTTP request with ERC-8128 */
|
|
9
|
+
export declare function signRequest(walletClient: WalletClient<Transport, Chain, Account>, signer: `0x${string}`, method: string, url: string, body?: string): Promise<SignedRequest>;
|
|
10
|
+
/** Build the ERC-8128 Authorization header value */
|
|
11
|
+
export declare function buildAuthHeader(signed: SignedRequest): string;
|
|
12
|
+
/** Create a fetch wrapper that automatically adds ERC-8128 auth headers */
|
|
13
|
+
export declare function createSignedFetch(walletClient: WalletClient<Transport, Chain, Account>, signer: `0x${string}`): typeof fetch;
|
|
14
|
+
//# sourceMappingURL=erc8128.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"erc8128.d.ts","sourceRoot":"","sources":["../../src/auth/erc8128.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,KAAK,SAAS,EACd,KAAK,OAAO,EACb,MAAM,MAAM,CAAC;AAGd,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,KAAK,MAAM,EAAE,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,KAAK,MAAM,EAAE,CAAC;CACtB;AAyDD,yCAAyC;AACzC,wBAAsB,WAAW,CAC/B,YAAY,EAAE,YAAY,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,EACrD,MAAM,EAAE,KAAK,MAAM,EAAE,EACrB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,aAAa,CAAC,CAWxB;AAED,oDAAoD;AACpD,wBAAgB,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAgB7D;AAED,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,YAAY,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,EACrD,MAAM,EAAE,KAAK,MAAM,EAAE,GACpB,OAAO,KAAK,CAwBd"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { AzethError } from '@azeth/common';
|
|
2
|
+
/** Generate a nonce for ERC-8128 authentication */
|
|
3
|
+
function generateNonce() {
|
|
4
|
+
const bytes = new Uint8Array(32); // H-8 fix: Increased from 16 to 32 bytes (256-bit entropy)
|
|
5
|
+
crypto.getRandomValues(bytes);
|
|
6
|
+
return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
7
|
+
}
|
|
8
|
+
/** Compute SHA-256 content digest for HTTP message signatures */
|
|
9
|
+
async function computeContentDigest(body) {
|
|
10
|
+
const encoder = new TextEncoder();
|
|
11
|
+
const data = encoder.encode(body);
|
|
12
|
+
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
|
13
|
+
const hashArray = new Uint8Array(hashBuffer);
|
|
14
|
+
let binary = '';
|
|
15
|
+
for (const byte of hashArray) {
|
|
16
|
+
binary += String.fromCharCode(byte);
|
|
17
|
+
}
|
|
18
|
+
const hashBase64 = btoa(binary);
|
|
19
|
+
return `sha-256=:${hashBase64}:`;
|
|
20
|
+
}
|
|
21
|
+
/** Build the ERC-8128 signature base string
|
|
22
|
+
* Follows the HTTP Signature specification for machine-native auth
|
|
23
|
+
*/
|
|
24
|
+
async function buildSignatureBase(method, url, body, nonce, created) {
|
|
25
|
+
const parsed = new URL(url);
|
|
26
|
+
const parts = [
|
|
27
|
+
`"@method": ${method.toUpperCase()}`,
|
|
28
|
+
`"@path": ${parsed.pathname}`,
|
|
29
|
+
`"@query": ${parsed.search || '?'}`,
|
|
30
|
+
`"@authority": ${parsed.host}`,
|
|
31
|
+
];
|
|
32
|
+
if (nonce) {
|
|
33
|
+
parts.push(`"nonce": "${nonce}"`);
|
|
34
|
+
}
|
|
35
|
+
if (created !== undefined) {
|
|
36
|
+
parts.push(`"created": ${created}`);
|
|
37
|
+
}
|
|
38
|
+
if (body) {
|
|
39
|
+
const digest = await computeContentDigest(body);
|
|
40
|
+
parts.push(`"content-digest": ${digest}`);
|
|
41
|
+
}
|
|
42
|
+
return parts.join('\n');
|
|
43
|
+
}
|
|
44
|
+
/** Sign an HTTP request with ERC-8128 */
|
|
45
|
+
export async function signRequest(walletClient, signer, method, url, body) {
|
|
46
|
+
const nonce = generateNonce();
|
|
47
|
+
const created = Math.floor(Date.now() / 1000);
|
|
48
|
+
const signatureBase = await buildSignatureBase(method, url, body, nonce, created);
|
|
49
|
+
const signature = await walletClient.signMessage({
|
|
50
|
+
account: signer,
|
|
51
|
+
message: signatureBase,
|
|
52
|
+
});
|
|
53
|
+
return { signature, nonce, created, keyid: signer };
|
|
54
|
+
}
|
|
55
|
+
/** Build the ERC-8128 Authorization header value */
|
|
56
|
+
export function buildAuthHeader(signed) {
|
|
57
|
+
// CRITICAL-2 fix: Use cross-platform Uint8Array instead of Node.js Buffer
|
|
58
|
+
// so this works in browsers, Deno, Cloudflare Workers, and edge runtimes.
|
|
59
|
+
const hexStr = signed.signature.slice(2); // remove 0x prefix
|
|
60
|
+
// H-3 fix: Validate signature is exactly 65 bytes (130 hex chars)
|
|
61
|
+
if (hexStr.length !== 130) {
|
|
62
|
+
throw new AzethError('Invalid signature length — expected 65 bytes', 'INVALID_INPUT', { field: 'signature' });
|
|
63
|
+
}
|
|
64
|
+
const sigBytes = new Uint8Array(hexStr.match(/.{2}/g).map(b => parseInt(b, 16)));
|
|
65
|
+
// H-3 fix: Validate v value is canonical (27, 28, 0, or 1)
|
|
66
|
+
const v = sigBytes[64];
|
|
67
|
+
if (v !== 27 && v !== 28 && v !== 0 && v !== 1) {
|
|
68
|
+
throw new AzethError('Invalid signature recovery value', 'INVALID_INPUT', { field: 'signature' });
|
|
69
|
+
}
|
|
70
|
+
const sigBase64 = btoa(String.fromCharCode(...sigBytes));
|
|
71
|
+
return `ERC8128 sig=:${sigBase64}:; keyid="${signed.keyid}"; nonce="${signed.nonce}"; created=${signed.created}`;
|
|
72
|
+
}
|
|
73
|
+
/** Create a fetch wrapper that automatically adds ERC-8128 auth headers */
|
|
74
|
+
export function createSignedFetch(walletClient, signer) {
|
|
75
|
+
return async (input, init) => {
|
|
76
|
+
const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;
|
|
77
|
+
const method = init?.method ?? 'GET';
|
|
78
|
+
// MEDIUM-12 fix: Reject non-string body types that can't be included in the signature base.
|
|
79
|
+
// Without this, a ReadableStream/Blob/ArrayBuffer body would be silently excluded from the
|
|
80
|
+
// content digest, causing the server to verify a signature over a request without the body hash.
|
|
81
|
+
if (init?.body !== undefined && init.body !== null && typeof init.body !== 'string') {
|
|
82
|
+
throw new AzethError('ERC-8128 signed fetch requires a string body for content digest. Convert your body to a string first.', 'INVALID_INPUT', { field: 'body', bodyType: typeof init.body });
|
|
83
|
+
}
|
|
84
|
+
const body = typeof init?.body === 'string' ? init.body : undefined;
|
|
85
|
+
const signed = await signRequest(walletClient, signer, method, url, body);
|
|
86
|
+
const authHeader = buildAuthHeader(signed);
|
|
87
|
+
const headers = new Headers(init?.headers);
|
|
88
|
+
headers.set('Authorization', authHeader);
|
|
89
|
+
return fetch(input, { ...init, headers });
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=erc8128.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"erc8128.js","sourceRoot":"","sources":["../../src/auth/erc8128.ts"],"names":[],"mappings":"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"}
|