@phala/pay 0.2.0 → 0.4.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/CHANGELOG.md +53 -1
- package/README.md +46 -33
- package/dist/checkout.d.ts +2 -2
- package/dist/format.d.ts +6 -0
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +17 -2
- package/dist/format.js.map +1 -1
- package/dist/quote.d.ts +15 -4
- package/dist/quote.d.ts.map +1 -1
- package/dist/quote.js +7 -2
- package/dist/quote.js.map +1 -1
- package/dist/react/Checkout.d.ts +10 -2
- package/dist/react/Checkout.d.ts.map +1 -1
- package/dist/react/Checkout.js +34 -10
- package/dist/react/Checkout.js.map +1 -1
- package/dist/server/webhook.d.ts +6 -5
- package/dist/server/webhook.d.ts.map +1 -1
- package/dist/server/webhook.js +18 -16
- package/dist/server/webhook.js.map +1 -1
- package/dist/wallet.d.ts +5 -3
- package/dist/wallet.d.ts.map +1 -1
- package/dist/wallet.js +26 -2
- package/dist/wallet.js.map +1 -1
- package/package.json +1 -1
- package/src/checkout.ts +2 -2
- package/src/format.ts +20 -2
- package/src/quote.ts +22 -6
- package/src/react/Checkout.tsx +53 -6
- package/src/server/webhook.ts +21 -19
- package/src/wallet.ts +36 -3
package/src/wallet.ts
CHANGED
|
@@ -6,10 +6,13 @@ import {
|
|
|
6
6
|
erc20Abi,
|
|
7
7
|
SwitchChainError,
|
|
8
8
|
UserRejectedRequestError,
|
|
9
|
+
type Address,
|
|
9
10
|
type Hash,
|
|
10
11
|
type WalletClient,
|
|
11
12
|
} from "viem";
|
|
13
|
+
import { readContract } from "viem/actions";
|
|
12
14
|
import { knownChain, networkName } from "./chains.js";
|
|
15
|
+
import { formatTokenAmount, formatUnitsGrouped } from "./format.js";
|
|
13
16
|
import { quoteTransfer } from "./payment.js";
|
|
14
17
|
import type { ClientQuote } from "./quote.js";
|
|
15
18
|
|
|
@@ -83,7 +86,12 @@ function isProvider(value: unknown): value is EthereumProvider {
|
|
|
83
86
|
return typeof (value as Partial<EthereumProvider> | null | undefined)?.request === "function";
|
|
84
87
|
}
|
|
85
88
|
|
|
86
|
-
export type WalletErrorCode =
|
|
89
|
+
export type WalletErrorCode =
|
|
90
|
+
| "rejected"
|
|
91
|
+
| "no_account"
|
|
92
|
+
| "wrong_chain"
|
|
93
|
+
| "insufficient_balance"
|
|
94
|
+
| "failed";
|
|
87
95
|
|
|
88
96
|
export class WalletError extends Error {
|
|
89
97
|
override readonly name = "WalletError";
|
|
@@ -101,8 +109,10 @@ export class WalletError extends Error {
|
|
|
101
109
|
* Pays a quote from a wallet: a viem `WalletClient` (for example wagmi's `useWalletClient()`), or an
|
|
102
110
|
* EIP-1193 provider such as a discovered `Wallet`'s. Connects (unless the client already has an
|
|
103
111
|
* account), switches to (or adds) the quote's chain, and sends the ERC-20 `transfer` that the quote's
|
|
104
|
-
* `payment_uri` states.
|
|
105
|
-
*
|
|
112
|
+
* `payment_uri` states. Before sending, it reads the account's token balance and, when it is below
|
|
113
|
+
* the quote's amount, sends nothing and throws `WalletError` `insufficient_balance`: the transfer
|
|
114
|
+
* would revert and still cost gas. Resolves with the transaction hash once the wallet has broadcast
|
|
115
|
+
* it; the checkout's status follows the payment from there.
|
|
106
116
|
*/
|
|
107
117
|
export async function payWithWallet(
|
|
108
118
|
wallet: WalletClient | EthereumProvider,
|
|
@@ -116,6 +126,16 @@ export async function payWithWallet(
|
|
|
116
126
|
throw new WalletError("no_account", "The wallet shared no account");
|
|
117
127
|
}
|
|
118
128
|
await ensureChain(client, transfer.chainId);
|
|
129
|
+
const address = typeof account === "string" ? account : account.address;
|
|
130
|
+
const balance = await tokenBalance(client, transfer.token, address);
|
|
131
|
+
if (balance !== undefined && balance < transfer.amount) {
|
|
132
|
+
const symbol = quote.asset.toUpperCase();
|
|
133
|
+
throw new WalletError(
|
|
134
|
+
"insufficient_balance",
|
|
135
|
+
`Your wallet holds ${formatUnitsGrouped(balance, quote.decimals)} ${symbol}, less than the ` +
|
|
136
|
+
`${formatTokenAmount(quote)} ${symbol} to pay. Nothing was sent.`,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
119
139
|
return await client.writeContract({
|
|
120
140
|
account,
|
|
121
141
|
chain: knownChain(transfer.chainId) ?? null,
|
|
@@ -135,6 +155,19 @@ export async function payWithWallet(
|
|
|
135
155
|
}
|
|
136
156
|
}
|
|
137
157
|
|
|
158
|
+
/**
|
|
159
|
+
* The account's balance of `token`, read through the wallet on its current chain; `undefined` when
|
|
160
|
+
* the wallet cannot read it. The check only spares the payer a reverting transfer, so a wallet that
|
|
161
|
+
* does not serve `eth_call` still pays.
|
|
162
|
+
*/
|
|
163
|
+
async function tokenBalance(wallet: WalletClient, token: Address, owner: Address): Promise<bigint | undefined> {
|
|
164
|
+
try {
|
|
165
|
+
return await readContract(wallet, { address: token, abi: erc20Abi, functionName: "balanceOf", args: [owner] });
|
|
166
|
+
} catch {
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
138
171
|
function isWalletClient(wallet: WalletClient | EthereumProvider): wallet is WalletClient {
|
|
139
172
|
return "writeContract" in wallet;
|
|
140
173
|
}
|