gondi 0.42.0 → 0.43.1

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 CHANGED
@@ -6,6 +6,10 @@ A JavaScript library for crypto-native lending: borrow, lend & refinance NFTs (n
6
6
 
7
7
  - [Installation](#installation)
8
8
  - [Getting Started](#getting-started)
9
+ - [Headless lender (scripts and AI agents)](#headless-lender-scripts-and-ai-agents)
10
+ - [Supported chains and currencies](#supported-chains-and-currencies)
11
+ - [Reading lending data](#reading-lending-data)
12
+ - [Risks](#risks)
9
13
  - [Examples](#examples)
10
14
  - [Need help migrating between versions? Check our Changelog!](./CHANGELOG.md)
11
15
 
@@ -42,6 +46,103 @@ const gondi = new Gondi({ wallet });
42
46
 
43
47
  Typescript types are included in the package.
44
48
 
49
+ ## Headless lender (scripts and AI agents)
50
+
51
+ A lender needs no browser: a private key is enough. The first API call signs a Sign-In with
52
+ Ethereum message with the wallet (a signature, no gas) and the SDK reuses the session afterwards.
53
+ Publishing, listing and hiding offers are API calls; approving the currency and cancelling an
54
+ offer are onchain transactions.
55
+
56
+ ```javascript
57
+ import { Gondi, OfferStatus } from 'gondi';
58
+ import { createWalletClient, http, parseEther } from 'viem';
59
+ import { privateKeyToAccount } from 'viem/accounts';
60
+ import { mainnet } from 'viem/chains';
61
+
62
+ const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
63
+ const SECONDS_PER_DAY = 86_400n;
64
+
65
+ const wallet = createWalletClient({
66
+ account: privateKeyToAccount(process.env.PRIVATE_KEY),
67
+ transport: http(process.env.RPC_URL),
68
+ chain: mainnet,
69
+ });
70
+ const gondi = new Gondi({ wallet });
71
+
72
+ // 1. Let the loan contract pull the principal when a borrower takes the offer (onchain, once).
73
+ const principalAmount = parseEther('1');
74
+ if (!(await gondi.isApprovedToken({ tokenAddress: WETH, amount: principalAmount }))) {
75
+ const approval = await gondi.approveToken({ tokenAddress: WETH }); // Unlimited unless you pass `amount`.
76
+ await approval.waitTxInBlock();
77
+ }
78
+
79
+ // 2. Find the collection to lend against (`gondi.collections({})` pages through all of them).
80
+ const collectionId = await gondi.collectionId({ slug: 'pudgypenguins' });
81
+
82
+ // 3. Sign and publish a collection offer.
83
+ const offer = await gondi.makeCollectionOffer({
84
+ collectionId,
85
+ principalAddress: WETH,
86
+ principalAmount,
87
+ capacity: principalAmount, // Total you lend across every loan taken from this offer.
88
+ fee: 0n, // Origination fee, in principal units.
89
+ aprBps: 1_000n, // 10% APR.
90
+ duration: 30n * SECONDS_PER_DAY, // In seconds; see "Loan durations" below.
91
+ expirationTime: BigInt(Math.floor(Date.now() / 1_000)) + SECONDS_PER_DAY, // Valid for one day.
92
+ maxSeniorRepayment: 0n, // 0n: the offer only funds a loan's first (senior) tranche.
93
+ });
94
+
95
+ // 4. List your active offers (pass `cursor` back to read the next page while `hasNextPage`).
96
+ const { offers, cursor, hasNextPage } = await gondi.offers({
97
+ filterBy: { lenders: [wallet.account.address], status: [OfferStatus.Active] },
98
+ });
99
+
100
+ // 5. Withdraw it. `hideOffer` only removes it from the API (no gas): the signed offer stays
101
+ // executable onchain until it expires. `cancelOffer` invalidates it onchain.
102
+ await gondi.hideOffer({ id: offer.offerId, contractAddress: offer.contractAddress });
103
+ const cancellation = await gondi.cancelOffer({
104
+ id: offer.offerId,
105
+ contractAddress: offer.contractAddress,
106
+ });
107
+ await cancellation.waitTxInBlock();
108
+ ```
109
+
110
+ [`examples/headlessLender.ts`](./examples/headlessLender.ts) runs the same flow against the
111
+ local chain the other examples use (see [`examples/common.ts`](./examples/common.ts) for the
112
+ environment it reads).
113
+
114
+ API errors are thrown as an `Error` whose message is the JSON of the GraphQL errors.
115
+
116
+ ### Loan durations
117
+
118
+ `duration` is in seconds and must be a whole number of days: a multiple of 30 days, or one of the
119
+ short terms the API accepts (currently 3, 5, 7, 10, 15, 20 or 45 days). Other values fail with
120
+ `INVALID_OFFER_DURATION_ERROR`.
121
+
122
+ ## Supported chains and currencies
123
+
124
+ The SDK picks the contracts from the wallet's chain. Amounts are in the currency's smallest unit.
125
+
126
+ | Chain | viem chain | Chain id | Principal currencies |
127
+ | ---------------- | ----------- | -------- | ------------------------------------------------------------------------------------------------------------------ |
128
+ | Ethereum mainnet | `mainnet` | 1 | WETH `0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2`, USDC (6 decimals) `0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48` |
129
+ | HyperEVM | `hyperEvm` | 999 | WHYPE `0x5555555555555555555555555555555555555555`, USDC (6 decimals) `0xb88339CB7199b77E23DB6E890353E22632Ba630f` |
130
+ | Robinhood Chain | `robinhood` | 4663 | WETH `0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73`, USDG (6 decimals) `0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168` |
131
+
132
+ ## Reading lending data
133
+
134
+ Lending data is public: the GraphQL API at https://api.gondi.xyz/graphql answers reads without
135
+ authentication and supports introspection, so any GraphQL client can explore the schema and query
136
+ collections, offers and loans directly.
137
+
138
+ ## Risks
139
+
140
+ The collateral is an NFT. If the borrower does not repay by the end of the term, the lender does
141
+ not get the principal back: a loan with a single lender is foreclosed and the lender claims the
142
+ NFT (`liquidateLoan`), whose value may be below what was lent; a loan split across several
143
+ tranches goes to auction instead. The protocol takes a fee on the interest the lender earns (and
144
+ on any origination fee); `getProtocolFee` reads it.
145
+
45
146
  ## Examples
46
147
 
47
148
  ### Getting NFT/Collection ids
@@ -63,19 +164,18 @@ const collectionId = (
63
164
  #### Single NFT Offer
64
165
 
65
166
  ```javascript
66
- const offers = await gondi.makeSingleNftOffer({
67
- nftId=1,
68
- principalAddress="0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // Principal currency address. (e.g. WETH)
69
- principalAmount=1_000_000_000_000_000_000n, // Principal amount. In units of currency (e.g. WETH is wei)(e.g. 1WETH)
70
- capacity=1_000_000_000_000_000_000n, // How much money do you want to loan in total,
71
- // valid for collection offers.
72
- // If you want N loans for example, it should be N*principalAmount.
73
- fee=0n, // Origination fee.
74
- aprBps=100n, // Apr expressed in basis points. (e.g. 1% apr)
75
- expirationTime=1700000000n, // Expiration time expressed in seconds since epoch. (e.g. 2023/11/14)
76
- duration=31_536_000n, // Duration expressed in secconds. (e.g. 1 year)
77
- requiresLiquidation, // Sets the collateral to be liquidated on default.
78
- borrowerAddress, // Optional: allow only this borrower to accept the offer.
167
+ const offer = await gondi.makeSingleNftOffer({
168
+ nftId: 1,
169
+ principalAddress: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // Principal currency address (e.g. WETH).
170
+ principalAmount: 1_000_000_000_000_000_000n, // In the currency's smallest unit (e.g. 1 WETH in wei).
171
+ capacity: 1_000_000_000_000_000_000n, // How much you lend in total; for N loans on a collection offer, N * principalAmount.
172
+ fee: 0n, // Origination fee.
173
+ aprBps: 100n, // APR in basis points (e.g. 1% APR).
174
+ expirationTime: BigInt(Math.floor(Date.now() / 1_000) + 86_400), // Seconds since epoch (e.g. one day from now).
175
+ duration: 2_592_000n, // Seconds (e.g. 30 days); see "Loan durations".
176
+ maxSeniorRepayment: 0n, // 0n: the offer only funds a loan's first (senior) tranche.
177
+ requiresLiquidation, // Sets the collateral to be liquidated on default.
178
+ borrowerAddress, // Optional: allow only this borrower to accept the offer.
79
179
  });
80
180
  ```
81
181
 
@@ -89,12 +189,25 @@ const offer = await gondi.makeCollectionOffer({
89
189
  });
90
190
  ```
91
191
 
192
+ #### Trait Offer
193
+
194
+ ```javascript
195
+ const offer = await gondi.makeTraitOffer({
196
+ traitIds, // The NFT must carry every one of these traits.
197
+ ... // Same as Single NFT Offer
198
+ });
199
+ ```
200
+
201
+ The API resolves the traits to the NFTs carrying them when the offer is generated and encodes
202
+ that set in the offer's validator, so the offer keeps matching those NFTs afterwards. It is
203
+ available where the API has trait loan offers enabled.
204
+
92
205
  ### Listing Offers
93
206
 
94
207
  ```javascript
95
208
  import { OffersSortField, Ordering } from 'gondi';
96
209
  const offer = await gondi.offers({
97
- cusor, // Cursor returned by previous calls.
210
+ cursor, // Cursor returned by previous calls.
98
211
  limit, // Number results.
99
212
  sortBy: {
100
213
  // Sort criteria.
@@ -109,9 +222,8 @@ const offer = await gondi.offers({
109
222
 
110
223
  ```javascript
111
224
  const listings = await gondi.listings({
112
- cusor, // Cursor returned by previous calls.
225
+ cursor, // Cursor returned by previous calls.
113
226
  limit, // Number results.
114
- sortBy, // Sort criteria.
115
- filterBy, // Filter criteria, result is conjunction of components.
227
+ collections, // Optional: only listings of these collection ids.
116
228
  });
117
229
  ```