@zkp2p/cash 0.1.4 → 0.1.6
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 +18 -5
- package/dist/{createCashClient-BbkfxILl.d.cts → createCashClient-jUA_GNdh.d.cts} +2 -0
- package/dist/{createCashClient-BbkfxILl.d.ts → createCashClient-jUA_GNdh.d.ts} +2 -0
- package/dist/index.cjs +119 -3
- package/dist/index.d.cts +177 -2
- package/dist/index.d.ts +177 -2
- package/dist/index.js +119 -3
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/tools.cjs +1 -1
- package/dist/tools.js +1 -1
- package/llms.txt +3 -4
- package/package.json +21 -14
package/README.md
CHANGED
|
@@ -5,12 +5,25 @@ on Venmo, Revolut, Wise, Zelle, and more at the live Chainlink market rate,
|
|
|
5
5
|
with zero spread and no centralized off-ramp provider.
|
|
6
6
|
|
|
7
7
|
Peer Cash is an **offramp-only** SDK for the [ZKP2P](https://peer.xyz)
|
|
8
|
-
protocol. The cashing-out user is the maker: their USDC becomes a
|
|
9
|
-
protocol
|
|
10
|
-
integrator a small set of typed verbs plus readable order state.
|
|
11
|
-
widget, no provider custody, no quote engine to maintain.
|
|
8
|
+
protocol. The cashing-out user is the maker: their USDC becomes a deposit in
|
|
9
|
+
the protocol contracts, a buyer pays them fiat and proves the payment, and the
|
|
10
|
+
SDK gives the integrator a small set of typed verbs plus readable order state.
|
|
11
|
+
No hosted widget, no provider custody, no quote engine to maintain.
|
|
12
12
|
|
|
13
|
-
**[
|
|
13
|
+
**[npm](https://www.npmjs.com/package/@zkp2p/cash)** · **[Lifecycle and recovery](docs/lifecycle-and-recovery.md)** · **[Agent integration manual](AGENTS.md)**
|
|
14
|
+
|
|
15
|
+
## Pick the right SDK
|
|
16
|
+
|
|
17
|
+
Peer Cash and the general ZKP2P SDK serve different integration depths:
|
|
18
|
+
|
|
19
|
+
| Package | Use it when | Boundary |
|
|
20
|
+
| ------------- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
21
|
+
| `@zkp2p/cash` | Cash-out is the product | Offramp only. The user is always the maker, the destination is Base USDC, pricing is the live Chainlink rate at fill with zero spread, and the SDK owns the resumable order lifecycle. |
|
|
22
|
+
| `@zkp2p/sdk` | You are composing directly with the Peer protocol | General maker and taker operations, deposits, intents, proofs, quotes, vaults, rate managers, referrals, hooks, and API helpers. Your application owns the workflow and protocol choices. |
|
|
23
|
+
|
|
24
|
+
Peer Cash is a narrow facade over `@zkp2p/sdk`, not a replacement for it. It
|
|
25
|
+
cannot express custom spreads, buyer-side proof flows, vaults, disputes, or
|
|
26
|
+
arbitrary protocol operations.
|
|
14
27
|
|
|
15
28
|
```ts
|
|
16
29
|
import { createCashClient, usdc } from '@zkp2p/cash';
|
|
@@ -272,6 +272,8 @@ interface RelayExecutionResult {
|
|
|
272
272
|
interface RelayTransaction {
|
|
273
273
|
hash: string;
|
|
274
274
|
chainId: number;
|
|
275
|
+
/** Relay batch-call identifiers are not transaction hashes. */
|
|
276
|
+
isBatchTx?: boolean | undefined;
|
|
275
277
|
}
|
|
276
278
|
interface RelayStatus {
|
|
277
279
|
requestId: string;
|
|
@@ -272,6 +272,8 @@ interface RelayExecutionResult {
|
|
|
272
272
|
interface RelayTransaction {
|
|
273
273
|
hash: string;
|
|
274
274
|
chainId: number;
|
|
275
|
+
/** Relay batch-call identifiers are not transaction hashes. */
|
|
276
|
+
isBatchTx?: boolean | undefined;
|
|
275
277
|
}
|
|
276
278
|
interface RelayStatus {
|
|
277
279
|
requestId: string;
|
package/dist/index.cjs
CHANGED
|
@@ -980,10 +980,18 @@ function collectRelayTransactions(steps, sourceChainId) {
|
|
|
980
980
|
for (const step of steps) {
|
|
981
981
|
for (const item of step.items) {
|
|
982
982
|
for (const tx of item.internalTxHashes ?? []) {
|
|
983
|
-
record({
|
|
983
|
+
record({
|
|
984
|
+
hash: tx.txHash,
|
|
985
|
+
chainId: tx.chainId,
|
|
986
|
+
...tx.isBatchTx ? { isBatchTx: true } : {}
|
|
987
|
+
});
|
|
984
988
|
}
|
|
985
989
|
for (const tx of item.txHashes ?? []) {
|
|
986
|
-
record({
|
|
990
|
+
record({
|
|
991
|
+
hash: tx.txHash,
|
|
992
|
+
chainId: tx.chainId,
|
|
993
|
+
...tx.isBatchTx ? { isBatchTx: true } : {}
|
|
994
|
+
});
|
|
987
995
|
}
|
|
988
996
|
}
|
|
989
997
|
}
|
|
@@ -1703,6 +1711,97 @@ function createCashClient(options) {
|
|
|
1703
1711
|
}
|
|
1704
1712
|
throw errors.allowanceNotVisible(amount, lastReadError);
|
|
1705
1713
|
}
|
|
1714
|
+
async function waitForBaseSignerAfterRelay(client, cashoutSigner, sourceSigner, owner, sourceChainId, executed) {
|
|
1715
|
+
if (sourceChainId !== BASE_CHAIN_ID) return;
|
|
1716
|
+
const baseTransactions = (executed.transactions?.origin ?? []).filter(
|
|
1717
|
+
(transaction) => transaction.chainId === BASE_CHAIN_ID
|
|
1718
|
+
);
|
|
1719
|
+
const batchIds = baseTransactions.filter((transaction) => transaction.isBatchTx === true).map((transaction) => transaction.hash);
|
|
1720
|
+
const batchExecutionFailed = (cause) => errors.sourceExecutionFailed(cause, {
|
|
1721
|
+
...executed.requestId ? { requestId: executed.requestId } : {},
|
|
1722
|
+
txHashes: executed.txHashes,
|
|
1723
|
+
...executed.transactions ? { transactions: executed.transactions } : {}
|
|
1724
|
+
});
|
|
1725
|
+
let batchTransactionHashes = [];
|
|
1726
|
+
if (batchIds.length > 0) {
|
|
1727
|
+
let batchError;
|
|
1728
|
+
let batchesComplete = false;
|
|
1729
|
+
let batchesSucceededWithoutReceipts = false;
|
|
1730
|
+
for (let attempt = 0; attempt < 20; attempt++) {
|
|
1731
|
+
try {
|
|
1732
|
+
const statuses = await Promise.all(
|
|
1733
|
+
batchIds.map((id) => sourceSigner.getCallsStatus({ id }))
|
|
1734
|
+
);
|
|
1735
|
+
if (statuses.some((status) => status.status === "failure")) {
|
|
1736
|
+
throw batchExecutionFailed(new Error("Relay wallet call bundle failed"));
|
|
1737
|
+
}
|
|
1738
|
+
if (statuses.every((status) => status.status === "success")) {
|
|
1739
|
+
const receiptGroups = statuses.map((status) => status.receipts ?? []);
|
|
1740
|
+
if (receiptGroups.some((receipts) => receipts.length === 0)) {
|
|
1741
|
+
batchError = new Error(
|
|
1742
|
+
"Relay wallet call bundle did not include transaction receipts"
|
|
1743
|
+
);
|
|
1744
|
+
batchesSucceededWithoutReceipts = true;
|
|
1745
|
+
break;
|
|
1746
|
+
} else {
|
|
1747
|
+
batchTransactionHashes = receiptGroups.flatMap(
|
|
1748
|
+
(receipts) => receipts.map((receipt) => receipt.transactionHash)
|
|
1749
|
+
);
|
|
1750
|
+
batchesComplete = true;
|
|
1751
|
+
break;
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
} catch (err) {
|
|
1755
|
+
if (isCashError(err)) throw err;
|
|
1756
|
+
batchError = err;
|
|
1757
|
+
}
|
|
1758
|
+
await sleep(250);
|
|
1759
|
+
}
|
|
1760
|
+
if (!batchesComplete) {
|
|
1761
|
+
if (batchesSucceededWithoutReceipts) throw batchError;
|
|
1762
|
+
throw batchExecutionFailed(
|
|
1763
|
+
batchError ?? new Error("Relay wallet call bundle did not complete")
|
|
1764
|
+
);
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
const hashes = [
|
|
1768
|
+
...baseTransactions.filter((transaction) => transaction.isBatchTx !== true).map((transaction) => transaction.hash),
|
|
1769
|
+
...batchTransactionHashes
|
|
1770
|
+
];
|
|
1771
|
+
if (hashes.length === 0) return;
|
|
1772
|
+
let transactions;
|
|
1773
|
+
let lastLookupError;
|
|
1774
|
+
for (let attempt = 0; attempt < 20; attempt++) {
|
|
1775
|
+
try {
|
|
1776
|
+
transactions = await Promise.all(
|
|
1777
|
+
hashes.map((hash) => client.publicClient.getTransaction({ hash }))
|
|
1778
|
+
);
|
|
1779
|
+
break;
|
|
1780
|
+
} catch (err) {
|
|
1781
|
+
lastLookupError = err;
|
|
1782
|
+
await sleep(250);
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
if (!transactions) throw lastLookupError;
|
|
1786
|
+
const ownerNonces = transactions.filter((transaction) => transaction.from.toLowerCase() === owner.toLowerCase()).map((transaction) => transaction.nonce);
|
|
1787
|
+
if (ownerNonces.length === 0) return;
|
|
1788
|
+
const afterRelay = Math.max(...ownerNonces) + 1;
|
|
1789
|
+
let lastNonceError;
|
|
1790
|
+
for (let attempt = 0; attempt < 20; attempt++) {
|
|
1791
|
+
try {
|
|
1792
|
+
const pendingHex = await cashoutSigner.transport.request({
|
|
1793
|
+
method: "eth_getTransactionCount",
|
|
1794
|
+
params: [owner, "pending"]
|
|
1795
|
+
});
|
|
1796
|
+
if (Number(BigInt(pendingHex)) >= afterRelay) return;
|
|
1797
|
+
} catch (err) {
|
|
1798
|
+
lastNonceError = err;
|
|
1799
|
+
}
|
|
1800
|
+
await sleep(250);
|
|
1801
|
+
}
|
|
1802
|
+
if (lastNonceError) throw lastNonceError;
|
|
1803
|
+
throw new Error(`Signer provider did not observe Relay nonce ${afterRelay - 1}`);
|
|
1804
|
+
}
|
|
1706
1805
|
return {
|
|
1707
1806
|
capabilities,
|
|
1708
1807
|
async sourceCapabilities() {
|
|
@@ -1774,6 +1873,22 @@ function createCashClient(options) {
|
|
|
1774
1873
|
...executed.transactions ? { transactions: executed.transactions } : {}
|
|
1775
1874
|
};
|
|
1776
1875
|
sourceResult = routedSource;
|
|
1876
|
+
try {
|
|
1877
|
+
await waitForBaseSignerAfterRelay(
|
|
1878
|
+
client,
|
|
1879
|
+
opts.signer,
|
|
1880
|
+
sourceSigner,
|
|
1881
|
+
owner,
|
|
1882
|
+
input.source.chainId,
|
|
1883
|
+
executed
|
|
1884
|
+
);
|
|
1885
|
+
} catch (err) {
|
|
1886
|
+
if (isCashError(err)) throw err;
|
|
1887
|
+
throw errors.sourceRouteCompletedCashoutFailed(
|
|
1888
|
+
routedSource,
|
|
1889
|
+
mapChainError("resolve same-chain Relay nonce", err)
|
|
1890
|
+
);
|
|
1891
|
+
}
|
|
1777
1892
|
const attributedParams2 = { ...params2, txOverrides: attribution };
|
|
1778
1893
|
const send2 = async () => {
|
|
1779
1894
|
try {
|
|
@@ -2146,7 +2261,8 @@ var bigintString = zod.z.string().regex(/^-?\d+$/, "expected a decimal bigint st
|
|
|
2146
2261
|
var nonNegativeBigintString = zod.z.string().regex(/^\d+$/, "expected a non-negative decimal bigint string");
|
|
2147
2262
|
var relayTransactionJsonSchema = zod.z.object({
|
|
2148
2263
|
hash: zod.z.string(),
|
|
2149
|
-
chainId: zod.z.number()
|
|
2264
|
+
chainId: zod.z.number(),
|
|
2265
|
+
isBatchTx: zod.z.boolean().optional()
|
|
2150
2266
|
});
|
|
2151
2267
|
var relayTransactionsJsonSchema = zod.z.object({
|
|
2152
2268
|
origin: zod.z.array(relayTransactionJsonSchema),
|