remesso-sdk 0.0.0-stage → 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.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +379 -2
  3. package/dist/abi.d.ts +63 -0
  4. package/dist/abi.js +14 -0
  5. package/dist/agents.d.ts +15 -0
  6. package/dist/agents.js +39 -0
  7. package/dist/attribution.d.ts +3 -0
  8. package/dist/attribution.js +29 -0
  9. package/dist/client.d.ts +75 -0
  10. package/dist/client.js +60 -0
  11. package/dist/context.d.ts +42 -0
  12. package/dist/context.js +157 -0
  13. package/dist/deployment.d.ts +19 -0
  14. package/dist/deployment.js +80 -0
  15. package/dist/errors.d.ts +30 -0
  16. package/dist/errors.js +10 -0
  17. package/dist/events.d.ts +59 -0
  18. package/dist/events.js +53 -0
  19. package/dist/generated/abi.d.ts +1543 -0
  20. package/dist/generated/abi.js +1979 -0
  21. package/dist/generated/deployment.d.ts +114 -0
  22. package/dist/generated/deployment.js +127 -0
  23. package/dist/index.d.ts +16 -0
  24. package/dist/index.js +15 -0
  25. package/dist/links.d.ts +25 -0
  26. package/dist/links.js +60 -0
  27. package/dist/node.d.ts +5 -0
  28. package/dist/node.js +60 -0
  29. package/dist/payments.d.ts +39 -0
  30. package/dist/payments.js +232 -0
  31. package/dist/policies.d.ts +54 -0
  32. package/dist/policies.js +148 -0
  33. package/dist/requests.d.ts +103 -0
  34. package/dist/requests.js +145 -0
  35. package/dist/schedules.d.ts +111 -0
  36. package/dist/schedules.js +232 -0
  37. package/dist/store.d.ts +24 -0
  38. package/dist/store.js +19 -0
  39. package/dist/vendor/erc8021.d.ts +9 -0
  40. package/dist/vendor/erc8021.js +82 -0
  41. package/dist/vendor/fee.d.ts +3 -0
  42. package/dist/vendor/fee.js +40 -0
  43. package/dist/vendor/payee-profile.d.ts +27 -0
  44. package/dist/vendor/payee-profile.js +73 -0
  45. package/dist/vendor/request-decline.d.ts +55 -0
  46. package/dist/vendor/request-decline.js +60 -0
  47. package/dist/vendor/safe-fetch.d.ts +2 -0
  48. package/dist/vendor/safe-fetch.js +154 -0
  49. package/dist/vendor/standard-webhooks.d.ts +8 -0
  50. package/dist/vendor/standard-webhooks.js +83 -0
  51. package/dist/vendor/webhook-actions.d.ts +84 -0
  52. package/dist/vendor/webhook-actions.js +123 -0
  53. package/dist/webhooks.d.ts +88 -0
  54. package/dist/webhooks.js +113 -0
  55. package/package.json +58 -4
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Cracked Studio
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,380 @@
1
- # Temporary Holding Version
1
+ # remesso-sdk
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Recurring and agent payments on Celo, in TypeScript, on viem.
4
+
5
+ - **Schedules:** a payer signs once, on-chain, a fixed amount to a fixed
6
+ address on a fixed cadence. Remesso's executor sends each payment when it is
7
+ due. Nothing can change the recipient, the amount or the cadence afterwards;
8
+ cancelling is the only edit.
9
+ - **Agent payments:** a person grants their agent's key a policy (one token, a
10
+ cap per payment and per day, an expiry). The agent pays from the person's
11
+ wallet, and the contract refuses anything outside the policy. A payment
12
+ retried with its idempotency key pays once.
13
+ - **Payment requests:** ask a wallet to pay you, and see when it's paid.
14
+ - **Payment events and signed webhooks:** know you were paid without running a
15
+ node.
16
+
17
+ > **The contracts are live and have not been audited by a person.** There is no
18
+ > testnet: everything here moves real money on Celo mainnet. Start with small
19
+ > amounts, a dollar rather than a payroll.
20
+
21
+ The developer page walks through the same steps: https://remesso.xyz/developers
22
+
23
+ ## Quickstart: a payment and its webhook
24
+
25
+ About 15 minutes, most of it waiting. Node 20 or later.
26
+
27
+ **1. Install**, in a new folder. The SDK is ES modules only, and `tsx` runs
28
+ TypeScript without a build.
29
+
30
+ ```bash
31
+ npm init -y && npm pkg set type=module
32
+ npm install remesso-sdk viem
33
+ npm install -D tsx
34
+ ```
35
+
36
+ **2. Make two keys**, one that pays and one that receives. Save this as
37
+ `keys.ts` and run `npx tsx keys.ts` twice:
38
+
39
+ ```ts
40
+ import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
41
+
42
+ const key = generatePrivateKey();
43
+ console.log(key, privateKeyToAccount(key).address);
44
+ ```
45
+
46
+ Send the payer about **1.05 USDT** on Celo, and a little CELO for gas if you
47
+ have it. With no CELO, the SDK pays gas in USDT. The receiver needs nothing.
48
+ Use keys that hold only what you are willing to lose.
49
+
50
+ ```bash
51
+ export PAYER_KEY=0x… RECEIVER_KEY=0x… RECEIVER=0x…
52
+ ```
53
+
54
+ **3. Run a receiver.** In one terminal, open a public HTTPS address to port
55
+ 8787. A Cloudflare quick tunnel needs no account, and prints a
56
+ `trycloudflare.com` URL:
57
+
58
+ ```bash
59
+ cloudflared tunnel --url http://localhost:8787
60
+ ```
61
+
62
+ Then save this as `receiver.ts`:
63
+
64
+ ```ts
65
+ import { createServer } from "node:http";
66
+ import { createRemesso, formatAmount, webhookHandler } from "remesso-sdk";
67
+ import { toNodeListener } from "remesso-sdk/node";
68
+ import { privateKeyToAccount } from "viem/accounts";
69
+
70
+ const receiver = createRemesso({
71
+ account: privateKeyToAccount(process.env.RECEIVER_KEY as `0x${string}`),
72
+ });
73
+
74
+ // Remesso returns the signing secret once your endpoint has answered its
75
+ // challenge, so the handler reads it when each delivery arrives.
76
+ let secret: string | undefined;
77
+
78
+ const handler = webhookHandler({
79
+ recipient: receiver.address,
80
+ secret: () => secret,
81
+ onPayment: (m) =>
82
+ console.log(m.type, formatAmount(m.data.amount, m.data.token), m.data.final ? "final" : "not final yet", m.data.txHash),
83
+ });
84
+
85
+ const server = createServer(toNodeListener(handler));
86
+ await new Promise<void>((done) => server.listen(8787, done));
87
+ ```
88
+
89
+ **4. Subscribe.** Add these lines to the end of `receiver.ts`, and run it with
90
+ the tunnel's URL: `PUBLIC_URL=https://….trycloudflare.com npx tsx receiver.ts`.
91
+ Remesso first posts a challenge to the URL; the handler answers it for your
92
+ address only, and the signing secret comes back once.
93
+
94
+ ```ts
95
+ const sub = await receiver.webhooks.subscribe({ url: process.env.PUBLIC_URL! });
96
+ secret = sub.secret; // shown once: save it if the receiver must survive a restart
97
+ console.log("subscribed", sub.subscription, "for", receiver.address);
98
+ ```
99
+
100
+ A quick tunnel gets a new URL each time it starts, and an address can hold 5
101
+ subscriptions: drop old ones with `webhooks.revoke({ url })`.
102
+
103
+ **5. Pay.** In a second terminal, save this as `pay.ts` and run
104
+ `npx tsx pay.ts`. One payment of 1 USDT, the smallest the executor sends: it
105
+ approves the contract and creates the schedule, two transactions.
106
+
107
+ ```ts
108
+ import { createRemesso, formatAmount } from "remesso-sdk";
109
+ import { privateKeyToAccount } from "viem/accounts";
110
+
111
+ const payer = createRemesso({
112
+ account: privateKeyToAccount(process.env.PAYER_KEY as `0x${string}`),
113
+ });
114
+
115
+ const s = await payer.schedules.create({
116
+ to: process.env.RECEIVER!,
117
+ amount: "1",
118
+ token: "USDT",
119
+ every: "week",
120
+ runs: 1,
121
+ });
122
+ console.log("schedule", s.id, "pays", formatAmount(s.recipientGets, "USDT"), s.hash);
123
+ ```
124
+
125
+ **6. Watch the receiver's terminal.** Remesso finds a schedule created outside
126
+ its app within about 5 minutes, and sends the payment within a minute of that.
127
+ A minute or two later the handler gets `payment.received` with `final: false`:
128
+ 0.9975 USDT, after the 0.25% fee. `payment.finalized` follows when Celo
129
+ finalizes the block, 25 to 30 minutes later. A failed delivery is retried with
130
+ the same `webhook-id` for 24 hours, so dedupe on it.
131
+
132
+ No public endpoint? Pull the same events instead:
133
+
134
+ ```ts
135
+ for await (const e of receiver.events.watch({ role: "to", kinds: ["run", "policy_payment"] })) {
136
+ console.log(e.kind, formatAmount(e.amount!, e.token!), e.final ? "final" : "not final yet", e.txHash);
137
+ }
138
+ ```
139
+
140
+ ## Getting paid by request
141
+
142
+ The request lands in the payer's Remesso inbox, and the link opens their form
143
+ filled in. It carries `req=`, so the schedule they sign is reported against
144
+ your request. Nothing is authorised until they sign in their own wallet.
145
+
146
+ ```ts
147
+ const req = await receiver.requests.create({
148
+ payer: "0xTheirWallet",
149
+ amount: "50",
150
+ token: "USDT",
151
+ every: "month",
152
+ runs: 12,
153
+ from: "Ada's studio",
154
+ note: "monthly retainer",
155
+ });
156
+ console.log(req.link); // send it however you already reach them
157
+
158
+ const paid = await receiver.requests.waitUntilPaid(req.id);
159
+ console.log(paid.payment?.txs);
160
+ ```
161
+
162
+ A requested amount is what you receive: the payer's form signs `grossFor` of
163
+ it, the amount plus the fee. Accepted is not paid; `waitUntilPaid` waits for a
164
+ payment with a transaction. A payer using the SDK answers with the request's
165
+ own terms:
166
+
167
+ ```ts
168
+ await payer.schedules.create({ request: req.id }); // the request's terms, the fee on top
169
+ ```
170
+
171
+ ## Agent payments
172
+
173
+ A person gives their agent's key a policy at https://remesso.xyz/agent-wallet,
174
+ or in code. No fee: what the agent pays is what arrives.
175
+
176
+ ```ts
177
+ // The person, once:
178
+ await person.policies.create({
179
+ agent: "0xAgentAddress",
180
+ token: "USDT",
181
+ perPayment: "5",
182
+ perDay: "20",
183
+ days: 30,
184
+ budget: "100", // added to their USDT approval for the allowance contract
185
+ });
186
+ ```
187
+
188
+ ```ts
189
+ import { createRemesso } from "remesso-sdk";
190
+ import { fileStore } from "remesso-sdk/node";
191
+ import { privateKeyToAccount } from "viem/accounts";
192
+
193
+ const agent = createRemesso({
194
+ account: privateKeyToAccount(process.env.AGENT_KEY as `0x${string}`),
195
+ store: fileStore(), // the record that makes a retry pay once, kept on disk
196
+ });
197
+
198
+ const r = await agent.pay({ to: "0xShop", amount: "2.50", token: "USDT", idempotencyKey: "order-1042" });
199
+ // r.status is "confirmed", or "pending": call again with the same key, never a new one
200
+
201
+ await agent.requests.pay(requestId); // its reference goes in the memo, so the requester sees it
202
+ ```
203
+
204
+ - A retry with the same idempotency key pays once, even when the first attempt
205
+ timed out after it was sent. `fileStore` keeps that record on disk, so it
206
+ holds across restarts; the default store is memory.
207
+ - Without a key, an identical payment within ten minutes is refused unless you
208
+ pass `allowRepeat`.
209
+ - Agent payments are signed before they are sent, so they need a local account
210
+ (`privateKeyToAccount`). The agent's key pays its own gas: send it a little
211
+ CELO.
212
+
213
+ ## Builder codes
214
+
215
+ Celo credits on-chain volume to the app that caused it, by the ERC-8021 code
216
+ on each transaction
217
+ ([attribution tags](https://github.com/celo-org/attribution-tags)). Pass your
218
+ own, and every transaction this client sends carries it after Remesso's.
219
+ Remesso's executor then carries it on every run of each schedule the client
220
+ creates, so the volume a schedule moves counts for you too, for as long as it
221
+ runs.
222
+
223
+ ```ts
224
+ import { createRemesso } from "remesso-sdk";
225
+ import { privateKeyToAccount } from "viem/accounts";
226
+
227
+ const payer = createRemesso({
228
+ account: privateKeyToAccount(process.env.PAYER_KEY as `0x${string}`),
229
+ builderCodes: ["your_code"],
230
+ });
231
+ const s = await payer.schedules.create({ to: "0xRecipient", amount: "5", token: "USDT", every: "month", runs: 12 });
232
+
233
+ // A minute later, what Remesso read from the transaction that created it:
234
+ const { events } = await payer.events.list({ role: "from", kinds: ["schedule_created"] });
235
+ console.log(events.find((e) => e.refId === s.id.toString())?.details?.builderCodes); // ["your_code"]
236
+ ```
237
+
238
+ - Up to three codes, each 1 to 32 of `a-z`, `0-9` and `_`. The SDK refuses
239
+ anything else before signing, by the rules the executor applies.
240
+ - Without the SDK, tag your own `createSchedule` transaction with
241
+ `toDataSuffix` or `withAttribution` from `@celo/attribution-tags`. Only
242
+ Schema 0, the one they write, is read. A schedule created through an
243
+ ERC-4337 bundle isn't read yet: the codes come from the outer transaction.
244
+ - The suffix sits after the calldata, where the EVM ignores it. A code
245
+ changes who is credited, never what a payment does.
246
+
247
+ ## What it costs
248
+
249
+ - **0.25% of each scheduled payment**, taken from it and rounded up: a 1 USDT
250
+ payment delivers 0.9975. Policy payments carry no fee. Receiving costs
251
+ nothing.
252
+ - **Per payment, the executor sends the equivalent of 1 to 1,000 dollars**: 1
253
+ to 1,000 USDT, USDC, USA₮ or USDm; 1,500 to 1.5 million wARS; 5 to 5,000
254
+ wBRL; 3,200 to 3.2 million wCOP; 1,300 to 1.3 million cNGN. Outside that a
255
+ schedule is valid on-chain and never runs, so the SDK refuses it before
256
+ signing. `runLimits(token)` gives the bounds, and
257
+ `https://engaboljiqudghvzmebq.supabase.co/functions/v1/agent` publishes them
258
+ under `limits.perRun`.
259
+ - **Gas, measured on our own transactions, not promised:** creating a schedule
260
+ is about 224,000 gas (about $0.004) plus an approval, paid by the payer. Each
261
+ payment is about 91,875 gas, paid by Remesso's executor. A policy payment is
262
+ about 85,000 gas, paid by the agent. That was at 202 gwei with CELO near
263
+ $0.09; the numbers move with both.
264
+
265
+ ## API
266
+
267
+ ### `createRemesso(options)`
268
+
269
+ | Option | |
270
+ |---|---|
271
+ | `account` | The key that pays, signs and receives: `privateKeyToAccount(...)`. Leave it out to read only. |
272
+ | `walletClient` | A wallet client instead of an account, such as a browser wallet on Celo. |
273
+ | `rpcUrl` | Default `https://forno.celo.org`. |
274
+ | `publicClient` | Your own viem public client. |
275
+ | `store` | Where agent payments are recorded so a retry pays once. Default: memory. |
276
+ | `fetchText` | Reads an https or ipfs registration file for `agents.find`: pass `safeFetchText`. |
277
+ | `attribution` | Tag transactions for ERC-8021 attribution. Default true. |
278
+ | `builderCodes` | Your own ERC-8021 codes, up to three: on every transaction this client sends, and on every run of the schedules it creates (see Builder codes). |
279
+
280
+ The client:
281
+
282
+ | | |
283
+ |---|---|
284
+ | `address` | This client's address, or null when it only reads. |
285
+ | `schedules.create(input)` | Approve and create a schedule. `amount` (what you send each time) or `receive` (what arrives); `token`, `every`, `runs`, `firstRunAt`, `trigger`, `request`. Refuses amounts outside the per-run limits before signing. |
286
+ | `schedules.get(id)` | The schedule as the contract stores it, with what the recipient gets per payment. |
287
+ | `schedules.status(id)` | Whether a payment is due, funded and approved. |
288
+ | `schedules.list(sender?)` | The ids of every schedule a sender created. |
289
+ | `schedules.cancel(id)` | Stop it for good. |
290
+ | `schedules.setActive(id, active)` | Pause or resume. |
291
+ | `schedules.setTrigger(id, trigger, count)` | Change who may bring a payment forward; `"remesso"` grants Remesso's executor. |
292
+ | `pay(input)` | As an agent, pay through a policy: `to`, `amount`, `token`, `policyId`, `memo`, `idempotencyKey`, `allowRepeat`, `waitMs`. |
293
+ | `policies.find(agent?)` | Every policy naming an agent, newest first. |
294
+ | `policies.get(id)` | A policy, with what it can spend right now. |
295
+ | `policies.create(input)` | As the owner: approve a budget and create a policy. |
296
+ | `policies.revoke(id)` | As the owner: end it now. |
297
+ | `policies.setRecipient(id, recipient, allowed)` | As the owner: edit the allowlist. |
298
+ | `requests.create(input)` | Ask a wallet to pay you. Returns the id, the reference and the link. |
299
+ | `requests.get(id)` | Where a request stands, its payer, and its payments. |
300
+ | `requests.list(payer?)` | The live requests addressed to a wallet. |
301
+ | `requests.answer(id, scheduleId)` | Report the schedule that answers a request. |
302
+ | `requests.decline(id, { policyId? })` | Turn a request down, signed by the payer or by its agent under a policy. |
303
+ | `requests.pay(id)` | As an agent, pay a request through a policy, its reference as the memo. |
304
+ | `requests.waitUntilPaid(id)` | Wait for a payment with a transaction. |
305
+ | `events.list(query)` | Payment events by address, role and kind, paged by `seq`. |
306
+ | `events.watch(query)` | Every event from `after` on, then each new one or change. |
307
+ | `events.waitFor(match, query)` | The first event that matches. |
308
+ | `webhooks.subscribe({ url })` | Start receiving this address's payments at `url`. Returns the signing secret once. |
309
+ | `webhooks.rotate({ url })` | A new secret; the old one keeps signing alongside it for a day. |
310
+ | `webhooks.revoke({ url })` | Stop. |
311
+ | `webhooks.replay({ url, days })` | Send failed deliveries again. |
312
+ | `agents.find(agentId)` | An ERC-8004 agent's wallet, from `getAgentWallet` only, and its payee profile. |
313
+ | `payLink(input)` | The link that opens a payer's form prefilled, to this account by default. |
314
+
315
+ ### Exports
316
+
317
+ | | |
318
+ |---|---|
319
+ | `createRemesso`, `Remesso`, `RemessoOptions` | The client. |
320
+ | `webhookHandler(options)` | A fetch-style `(Request) => Promise<Response>` handler: answers the challenge for your address, verifies every delivery, calls `onPayment`. |
321
+ | `verifyWebhook(raw, headers, secrets)` | Whether a delivery was signed with one of your secrets in the last five minutes. |
322
+ | `parseWebhook(raw, headers, secrets)` | A verified delivery, parsed; throws otherwise. |
323
+ | `payLink(input)`, `parsePayLink(url)` | Build a pay link, or read one, keeping only what validates. |
324
+ | `feeOf`, `netOf`, `grossFor` | The commission in base units, rounded up as the contract rounds it. |
325
+ | `token`, `findToken` | A token by symbol (USAT for USA₮) or address. |
326
+ | `toUnits`, `formatAmount` | Decimal strings to base units and back. |
327
+ | `runLimits(token)` | The per-run bounds the executor sends. |
328
+ | `memo32(memo)` | A memo as bytes32, refused over 32 bytes. |
329
+ | `memoryStore()` | The default payment store. |
330
+ | `deployment` | Celo mainnet as Remesso runs it, generated from what is live. |
331
+ | `CHAIN_ID`, `EXECUTOR`, `ALLOWANCE`, `REGISTRY`, `CURRENT_EXECUTOR`, `FEE_BPS` | The chain, the contracts, Remesso's trigger sentinel and the commission. |
332
+ | `executorAbi`, `allowanceAbi`, `erc20Abi`, `registryAbi` | ABIs. |
333
+ | `ATTRIBUTION` | The ERC-8021 suffix the SDK adds to transactions. |
334
+ | `attributionFor(builderCodes)` | That suffix with your codes after ours, checked by the executor's rules: for a transaction you send yourself. |
335
+ | `CADENCES` | `week`, `fortnight`, `month` and `quarter`, in seconds. |
336
+ | `RemessoError` | Every refusal; see the codes below. |
337
+
338
+ Types: `Cadence`, `CreateScheduleInput`, `CreatedSchedule`, `Schedule`,
339
+ `PayInput`, `PaymentResult`, `Policy`, `CreatePolicyInput`,
340
+ `CreateRequestInput`, `RequestState`, `RequestStatus`, `RequestSummary`,
341
+ `EventKind`, `EventQuery`, `PaymentEvent`, `WebhookHandlerOptions`,
342
+ `WebhookMessage`, `WebhookType`, `FoundAgent`, `PayeeProfile`, `PayLink`,
343
+ `PayLinkInput`, `PaymentRecord`, `PaymentStore`, `Token`.
344
+
345
+ ### `remesso-sdk/node`
346
+
347
+ | | |
348
+ |---|---|
349
+ | `fileStore(path?)` | Agent payment records in a 0600 JSON file, default `~/.remesso-sdk/payments.json`. One process per file. |
350
+ | `safeFetchText(url)` | Fetches a remote registration file only from public addresses, connecting to the address it checked. |
351
+ | `toNodeListener(handler)` | Runs a fetch-style handler under `http.createServer` or Express. Mount it before any JSON body parser. |
352
+
353
+ ## Errors
354
+
355
+ Every refusal is a `RemessoError` with a `code` (type `RemessoErrorCode`):
356
+
357
+ | Code | |
358
+ |---|---|
359
+ | `invalid` | The input is wrong: an address that isn't one, an unknown token, an amount out of range. |
360
+ | `wrong_chain` | The RPC isn't Celo mainnet, so nothing is signed. |
361
+ | `no_account` | A write needs an account and none was given. |
362
+ | `refused` | The contract or Remesso's backend would refuse it, caught before anything was sent. |
363
+ | `conflict` | Already done or taken: a subscription that exists (rotate its secret instead), a request already answered, a nonce already spent. |
364
+ | `reverted` | Mined, and reverted: nothing moved. |
365
+ | `no_gas` | Neither CELO nor a token that can pay for gas. |
366
+ | `key_reused` | An idempotency key already used for a different payment. |
367
+ | `repeat` | An identical payment went out minutes ago and no idempotency key was given. |
368
+ | `http` | A Remesso endpoint answered with an error. |
369
+ | `not_found` | A request, a schedule or a subscription that doesn't exist. |
370
+ | `timeout` | Waited, and it didn't happen in time. |
371
+
372
+ ## Links
373
+
374
+ - Developer page: https://remesso.xyz/developers
375
+ - The same endpoints over plain HTTP: https://bitbucket.org/crackedstudios/remesso/src/main/docs/AGENTS.md
376
+ - Example agents: https://bitbucket.org/crackedstudios/remesso/src/main/examples/
377
+ - Every endpoint, the contracts and the registrations: https://remesso.xyz/.well-known/agent.json
378
+ - Source: https://bitbucket.org/crackedstudios/remesso
379
+
380
+ MIT licensed.
package/dist/abi.d.ts ADDED
@@ -0,0 +1,63 @@
1
+ export { allowanceAbi, executorAbi } from "./generated/abi.js";
2
+ export declare const erc20Abi: readonly [{
3
+ readonly name: "balanceOf";
4
+ readonly type: "function";
5
+ readonly stateMutability: "view";
6
+ readonly inputs: readonly [{
7
+ readonly type: "address";
8
+ }];
9
+ readonly outputs: readonly [{
10
+ readonly type: "uint256";
11
+ }];
12
+ }, {
13
+ readonly name: "allowance";
14
+ readonly type: "function";
15
+ readonly stateMutability: "view";
16
+ readonly inputs: readonly [{
17
+ readonly type: "address";
18
+ readonly name: "owner";
19
+ }, {
20
+ readonly type: "address";
21
+ readonly name: "spender";
22
+ }];
23
+ readonly outputs: readonly [{
24
+ readonly type: "uint256";
25
+ }];
26
+ }, {
27
+ readonly name: "approve";
28
+ readonly type: "function";
29
+ readonly stateMutability: "nonpayable";
30
+ readonly inputs: readonly [{
31
+ readonly type: "address";
32
+ readonly name: "spender";
33
+ }, {
34
+ readonly type: "uint256";
35
+ readonly name: "value";
36
+ }];
37
+ readonly outputs: readonly [{
38
+ readonly type: "bool";
39
+ }];
40
+ }];
41
+ export declare const registryAbi: readonly [{
42
+ readonly name: "getAgentWallet";
43
+ readonly type: "function";
44
+ readonly stateMutability: "view";
45
+ readonly inputs: readonly [{
46
+ readonly type: "uint256";
47
+ readonly name: "agentId";
48
+ }];
49
+ readonly outputs: readonly [{
50
+ readonly type: "address";
51
+ }];
52
+ }, {
53
+ readonly name: "tokenURI";
54
+ readonly type: "function";
55
+ readonly stateMutability: "view";
56
+ readonly inputs: readonly [{
57
+ readonly type: "uint256";
58
+ readonly name: "agentId";
59
+ }];
60
+ readonly outputs: readonly [{
61
+ readonly type: "string";
62
+ }];
63
+ }];
package/dist/abi.js ADDED
@@ -0,0 +1,14 @@
1
+ /// The contracts' ABIs, generated from the compiled contracts, and the two
2
+ /// standard ones the SDK reads.
3
+ import { parseAbi } from "viem";
4
+ export { allowanceAbi, executorAbi } from "./generated/abi.js";
5
+ export const erc20Abi = parseAbi([
6
+ "function balanceOf(address) view returns (uint256)",
7
+ "function allowance(address owner, address spender) view returns (uint256)",
8
+ "function approve(address spender, uint256 value) returns (bool)",
9
+ ]);
10
+ /// ERC-8004's identity registry: who an agent is, and the wallet it is paid at.
11
+ export const registryAbi = parseAbi([
12
+ "function getAgentWallet(uint256 agentId) view returns (address)",
13
+ "function tokenURI(uint256 agentId) view returns (string)",
14
+ ]);
@@ -0,0 +1,15 @@
1
+ import type { Address } from "viem";
2
+ import type { Ctx } from "./context.js";
3
+ import { type PayeeProfile } from "./vendor/payee-profile.js";
4
+ export type FoundAgent = {
5
+ agentId: bigint;
6
+ /** Where to pay it, or null when it has set no wallet: then don't. */
7
+ wallet: Address | null;
8
+ profile: PayeeProfile | null;
9
+ /** Why there's no profile, or no wallet. */
10
+ note?: string;
11
+ };
12
+ export declare function makeAgents(ctx: Ctx): {
13
+ find(agentId: bigint | number | string): Promise<FoundAgent>;
14
+ };
15
+ export type Agents = ReturnType<typeof makeAgents>;
package/dist/agents.js ADDED
@@ -0,0 +1,39 @@
1
+ import { registryAbi } from "./abi.js";
2
+ import { REGISTRY } from "./deployment.js";
3
+ import { payeeProfile, uriSource } from "./vendor/payee-profile.js";
4
+ export function makeAgents(ctx) {
5
+ return {
6
+ async find(agentId) {
7
+ const id = BigInt(agentId);
8
+ const [wallet, uri] = await Promise.all([
9
+ ctx.pub.readContract({ address: REGISTRY, abi: registryAbi, functionName: "getAgentWallet", args: [id] }),
10
+ ctx.pub.readContract({ address: REGISTRY, abi: registryAbi, functionName: "tokenURI", args: [id] }).catch(() => null),
11
+ ]);
12
+ const none = /^0x0{40}$/i.test(wallet);
13
+ let read = { profile: null, note: "it has no registration file" };
14
+ if (uri != null) {
15
+ try {
16
+ const src = uriSource(uri);
17
+ if (src.kind === "unsupported")
18
+ read = { profile: null, note: `its registration URI is ${src.reason}` };
19
+ else if (src.kind === "inline")
20
+ read = payeeProfile(JSON.parse(src.json));
21
+ else if (!ctx.fetchText) {
22
+ read = { profile: null, note: "its registration file is remote: pass fetchText (safeFetchText from remesso-sdk/node) to read it" };
23
+ }
24
+ else
25
+ read = payeeProfile(JSON.parse(await ctx.fetchText(src.url)));
26
+ }
27
+ catch (e) {
28
+ read = { profile: null, note: `its registration file couldn't be read: ${String(e.message).slice(0, 160)}` };
29
+ }
30
+ }
31
+ return {
32
+ agentId: id,
33
+ wallet: none ? null : wallet,
34
+ profile: read.profile,
35
+ ...(none ? { note: "it has no agentWallet set, so don't pay it" } : read.note ? { note: read.note } : {}),
36
+ };
37
+ },
38
+ };
39
+ }
@@ -0,0 +1,3 @@
1
+ import type { Hex } from "viem";
2
+ export declare const ATTRIBUTION: "0x72656d6573736f2c63656c6f5f356364333563613535626166190080218021802180218021802180218021";
3
+ export declare function attributionFor(builderCodes?: readonly string[], ours?: boolean): Hex | undefined;
@@ -0,0 +1,29 @@
1
+ import { RemessoError } from "./errors.js";
2
+ import { CODE, MAX_INTEGRATOR_CODES, OWN_CODES, suffixFor } from "./vendor/erc8021.js";
3
+ export const ATTRIBUTION = "0x72656d6573736f2c63656c6f5f356364333563613535626166190080218021802180218021802180218021";
4
+ /// What `ATTRIBUTION` carries.
5
+ const OURS = ["remesso", "celo_5cd35ca55baf"];
6
+ /// The suffix with your own builder codes after ours. Put on the transaction
7
+ /// that creates a schedule, they ride on every run of it too: Remesso's
8
+ /// executor reads them and tags each run with them, so Celo counts the volume
9
+ /// for you as well as for us.
10
+ ///
11
+ /// Checked by the rules the backend applies (`vendor/erc8021.ts`), so a code
12
+ /// accepted here is one the runs carry: Celo's charset, none of Remesso's own,
13
+ /// no repeats, at most three. `ours: false` leaves our codes out.
14
+ export function attributionFor(builderCodes = [], ours = true) {
15
+ for (const code of builderCodes) {
16
+ if (!CODE.test(code)) {
17
+ throw new RemessoError("invalid", `builder code ${JSON.stringify(code)}: 1 to 32 of a-z, 0-9 and _, as Celo's attribution codes are`);
18
+ }
19
+ if (OWN_CODES.includes(code))
20
+ throw new RemessoError("invalid", `${code} is Remesso's own code: pass yours`);
21
+ }
22
+ if (new Set(builderCodes).size !== builderCodes.length)
23
+ throw new RemessoError("invalid", "a builder code is repeated");
24
+ if (builderCodes.length > MAX_INTEGRATOR_CODES) {
25
+ throw new RemessoError("invalid", `at most ${MAX_INTEGRATOR_CODES} builder codes ride on a schedule's runs`);
26
+ }
27
+ const codes = [...(ours ? OURS : []), ...builderCodes];
28
+ return codes.length ? suffixFor(codes) : undefined;
29
+ }
@@ -0,0 +1,75 @@
1
+ import { type Account, type Address, type PublicClient, type WalletClient } from "viem";
2
+ import { type Agents } from "./agents.js";
3
+ import { deployment } from "./deployment.js";
4
+ import { type Events } from "./events.js";
5
+ import { type PayLinkInput } from "./links.js";
6
+ import { type PayInput, type PaymentResult } from "./payments.js";
7
+ import { type Policies } from "./policies.js";
8
+ import { type Requests } from "./requests.js";
9
+ import { type Schedules } from "./schedules.js";
10
+ import { type PaymentStore } from "./store.js";
11
+ import { type Webhooks } from "./webhooks.js";
12
+ export type RemessoOptions = {
13
+ /**
14
+ * The key that pays, signs and receives: `privateKeyToAccount(...)`. Agent
15
+ * payments need a local account like this one. Leave it out to read only.
16
+ */
17
+ account?: Account;
18
+ /** A wallet client instead of an account, e.g. a browser wallet on Celo. */
19
+ walletClient?: WalletClient;
20
+ /** Default https://forno.celo.org. */
21
+ rpcUrl?: string;
22
+ publicClient?: PublicClient;
23
+ /** Where agent payments are recorded so a retry pays once. Default: memory; see `fileStore` in remesso-sdk/node. */
24
+ store?: PaymentStore;
25
+ /** Reads an https or ipfs registration file for `agents.find`; `safeFetchText` from remesso-sdk/node. */
26
+ fetchText?: (url: string) => Promise<string>;
27
+ /** Tag transactions for ERC-8021 attribution. Default true. */
28
+ attribution?: boolean;
29
+ /**
30
+ * Your own ERC-8021 builder codes, up to three, carried after Remesso's on
31
+ * every transaction this client sends. Remesso's executor also carries them
32
+ * on every run of the schedules you create, so Celo counts that volume for
33
+ * you. Throws on a code outside Celo's charset, one of Remesso's own, a
34
+ * repeat, or more than three.
35
+ */
36
+ builderCodes?: string[];
37
+ /** For tests: another Remesso backend, and the app origin links point at. */
38
+ functionsUrl?: string;
39
+ appOrigin?: string;
40
+ fetch?: typeof fetch;
41
+ };
42
+ export type Remesso = {
43
+ /** This client's address, or null when it only reads. */
44
+ address: Address | null;
45
+ publicClient: PublicClient;
46
+ deployment: typeof deployment;
47
+ /** Pay on a schedule: sign once, Remesso's executor sends each payment when due. */
48
+ schedules: Schedules;
49
+ /** As an agent, pay through a policy your person set. At most once per idempotency key. */
50
+ pay(input: PayInput): Promise<PaymentResult>;
51
+ /** Spending limits a person grants an agent. */
52
+ policies: Policies;
53
+ /** Ask to be paid, see requests sent to you, answer or decline them. */
54
+ requests: Requests;
55
+ /** Every payment to or from an address, as Remesso's indexer reads it from the chain. */
56
+ events: Events;
57
+ /** Have payments announced to your server, signed. */
58
+ webhooks: Webhooks;
59
+ /** Other agents in the ERC-8004 registry, and what they accept. */
60
+ agents: Agents;
61
+ /** The link that opens a payer's Remesso form prefilled, to this account by default. */
62
+ payLink(p: Omit<PayLinkInput, "to"> & {
63
+ to?: string;
64
+ }): string;
65
+ };
66
+ export declare function createRemesso(opts: RemessoOptions & ({
67
+ account: Account;
68
+ } | {
69
+ walletClient: WalletClient & {
70
+ account: Account;
71
+ };
72
+ })): Remesso & {
73
+ address: Address;
74
+ };
75
+ export declare function createRemesso(opts?: RemessoOptions): Remesso;