@zoralabs/protocol-sdk 0.10.0 → 0.11.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/.turbo/turbo-build.log +7 -7
  2. package/CHANGELOG.md +11 -0
  3. package/dist/apis/multicall3.d.ts +1 -1
  4. package/dist/apis/multicall3.d.ts.map +1 -1
  5. package/dist/create/1155-create-helper.test.d.ts +3 -0
  6. package/dist/create/1155-create-helper.test.d.ts.map +1 -0
  7. package/dist/fixtures/contract-setup.d.ts +16 -0
  8. package/dist/fixtures/contract-setup.d.ts.map +1 -0
  9. package/dist/index.cjs +538 -28
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.js +556 -28
  12. package/dist/index.js.map +1 -1
  13. package/dist/sdk.d.ts +4 -0
  14. package/dist/sdk.d.ts.map +1 -1
  15. package/dist/secondary/conversions.d.ts +7 -0
  16. package/dist/secondary/conversions.d.ts.map +1 -0
  17. package/dist/secondary/secondary-client.d.ts +67 -0
  18. package/dist/secondary/secondary-client.d.ts.map +1 -0
  19. package/dist/secondary/slippage.d.ts +25 -0
  20. package/dist/secondary/slippage.d.ts.map +1 -0
  21. package/dist/secondary/types.d.ts +37 -0
  22. package/dist/secondary/types.d.ts.map +1 -0
  23. package/dist/secondary/uniswap/abis.d.ts +152 -0
  24. package/dist/secondary/uniswap/abis.d.ts.map +1 -0
  25. package/dist/secondary/uniswap/uniswapQuote.d.ts +13 -0
  26. package/dist/secondary/uniswap/uniswapQuote.d.ts.map +1 -0
  27. package/dist/secondary/uniswap/uniswapQuoteExact.d.ts +37 -0
  28. package/dist/secondary/uniswap/uniswapQuoteExact.d.ts.map +1 -0
  29. package/dist/secondary/uniswap/uniswapReserves.d.ts +3 -0
  30. package/dist/secondary/uniswap/uniswapReserves.d.ts.map +1 -0
  31. package/dist/secondary/utils.d.ts +10 -0
  32. package/dist/secondary/utils.d.ts.map +1 -0
  33. package/dist/sparks/sparks-contracts.d.ts.map +1 -1
  34. package/dist/types.d.ts +3 -2
  35. package/dist/types.d.ts.map +1 -1
  36. package/dist/utils.d.ts +2 -1
  37. package/dist/utils.d.ts.map +1 -1
  38. package/package.json +1 -1
  39. package/src/apis/multicall3.ts +1 -1
  40. package/src/fixtures/contract-setup.ts +57 -0
  41. package/src/rewards/rewards-client.test.ts +15 -111
  42. package/src/sdk.ts +11 -0
  43. package/src/secondary/conversions.ts +20 -0
  44. package/src/secondary/secondary-client.test.ts +276 -0
  45. package/src/secondary/secondary-client.ts +374 -0
  46. package/src/secondary/slippage.ts +42 -0
  47. package/src/secondary/types.ts +64 -0
  48. package/src/secondary/uniswap/abis.ts +20 -0
  49. package/src/secondary/uniswap/uniswapQuote.ts +134 -0
  50. package/src/secondary/uniswap/uniswapQuoteExact.ts +114 -0
  51. package/src/secondary/uniswap/uniswapReserves.ts +34 -0
  52. package/src/secondary/utils.ts +40 -0
  53. package/src/sparks/sparks-contracts.ts +1 -3
  54. package/src/types.ts +6 -7
  55. package/src/utils.ts +7 -1
@@ -1,86 +1,24 @@
1
1
  import { describe, expect, vi } from "vitest";
2
- import {
3
- Address,
4
- Chain,
5
- encodeAbiParameters,
6
- erc20Abi,
7
- parseEther,
8
- PublicClient,
9
- WalletClient,
10
- } from "viem";
2
+ import { encodeAbiParameters, erc20Abi, parseEther } from "viem";
11
3
  import { zoraSepolia } from "viem/chains";
12
4
  import {
13
5
  forkUrls,
14
6
  makeAnvilTest,
15
7
  simulateAndWriteContractWithRetries,
16
8
  } from "src/anvil";
17
- import { createCollectorClient, createCreatorClient } from "src/sdk";
18
- import {
19
- demoContractMetadataURI,
20
- demoTokenMetadataURI,
21
- } from "src/create/1155-create-helper.test";
9
+ import { createCollectorClient } from "src/sdk";
22
10
  import { new1155ContractVersion } from "src/create/contract-setup";
23
11
  import { ISubgraphQuerier } from "src/apis/subgraph-querier";
24
- import { SubgraphMintGetter } from "src/mint/subgraph-mint-getter";
25
12
  import { mockTimedSaleStrategyTokenQueryResult } from "src/fixtures/mint-query-results";
26
13
  import {
27
14
  secondarySwapABI,
28
15
  secondarySwapAddress,
29
16
  zoraCreator1155ImplABI,
30
- zoraTimedSaleStrategyABI,
31
- zoraTimedSaleStrategyAddress,
32
17
  } from "@zoralabs/protocol-deployments";
33
18
  import { makeContractParameters } from "src/utils";
34
- import { SubgraphRewardsGetter } from "./subgraph-rewards-getter";
35
19
  import { mockRewardsQueryResults } from "src/fixtures/rewards-query-results";
36
-
37
- async function setupContractAndToken({
38
- chain,
39
- publicClient,
40
- creatorAccount,
41
- walletClient,
42
- }: {
43
- chain: Chain;
44
- publicClient: PublicClient;
45
- creatorAccount: Address;
46
- walletClient: WalletClient;
47
- }) {
48
- const rewardsGetter = new SubgraphRewardsGetter(chain.id);
49
- const creatorClient = createCreatorClient({
50
- chainId: chain.id,
51
- publicClient,
52
- rewardsGetter,
53
- });
54
-
55
- const mintGetter = new SubgraphMintGetter(chain.id);
56
- // create a new 1155 contract
57
-
58
- const { contractAddress, parameters, newTokenId } =
59
- await creatorClient.create1155({
60
- contract: {
61
- uri: demoContractMetadataURI,
62
- name: `Test 1155-${Math.round(Math.random() * 100_000_000_000)}`,
63
- },
64
- token: {
65
- tokenMetadataURI: demoTokenMetadataURI,
66
- },
67
- account: creatorAccount,
68
- });
69
-
70
- await simulateAndWriteContractWithRetries({
71
- parameters,
72
- walletClient,
73
- publicClient,
74
- });
75
-
76
- return {
77
- creatorClient,
78
- contractAddress,
79
- newTokenId,
80
- mintGetter,
81
- rewardsGetter,
82
- };
83
- }
20
+ import { setupContractAndToken } from "src/fixtures/contract-setup";
21
+ import { advanceToSaleAndAndLaunchMarket } from "src/secondary/secondary-client.test";
84
22
 
85
23
  describe("rewardsClient", () => {
86
24
  makeAnvilTest({
@@ -244,55 +182,21 @@ describe("rewardsClient", () => {
244
182
  publicClient,
245
183
  });
246
184
 
247
- const saleEnd = (
248
- await publicClient.readContract({
249
- abi: zoraTimedSaleStrategyABI,
250
- address:
251
- zoraTimedSaleStrategyAddress[
252
- chain.id as keyof typeof zoraTimedSaleStrategyAddress
253
- ],
254
- functionName: "saleV2",
255
- args: [contractAddress, newTokenId],
256
- })
257
- ).saleEnd;
258
-
259
- // advance to end of sale
260
- await testClient.setNextBlockTimestamp({
261
- timestamp: saleEnd,
262
- });
263
-
264
- await testClient.mine({
265
- blocks: 1,
266
- });
267
-
268
- // advance to end of sale
269
- // launch the market
270
- await simulateAndWriteContractWithRetries({
271
- parameters: makeContractParameters({
272
- abi: zoraTimedSaleStrategyABI,
273
- functionName: "launchMarket",
274
- args: [contractAddress, newTokenId],
275
- address:
276
- zoraTimedSaleStrategyAddress[
277
- chain.id as keyof typeof zoraTimedSaleStrategyAddress
278
- ],
279
- account: collectorAccount,
280
- }),
185
+ await advanceToSaleAndAndLaunchMarket({
186
+ chainId: chain.id,
187
+ account: collectorAccount,
281
188
  publicClient,
282
189
  walletClient,
190
+ collectorClient,
191
+ testClient,
192
+ contractAddress,
193
+ tokenId: newTokenId,
283
194
  });
284
195
 
285
- const erc20z = (
286
- await publicClient.readContract({
287
- abi: zoraTimedSaleStrategyABI,
288
- address:
289
- zoraTimedSaleStrategyAddress[
290
- chain.id as keyof typeof zoraTimedSaleStrategyAddress
291
- ],
292
- functionName: "sale",
293
- args: [contractAddress, newTokenId],
294
- })
295
- ).erc20zAddress;
196
+ const erc20z = (await collectorClient.getSecondaryInfo({
197
+ contract: contractAddress,
198
+ tokenId: newTokenId,
199
+ }))!.erc20z!;
296
200
 
297
201
  // after market is launched, by 100 from the pool. there should be some rewards
298
202
  // balances from secondary royalties
package/src/sdk.ts CHANGED
@@ -17,6 +17,7 @@ import {
17
17
  IRewardsGetter,
18
18
  SubgraphRewardsGetter,
19
19
  } from "./rewards/subgraph-rewards-getter";
20
+ import { SecondaryClient } from "./secondary/secondary-client";
20
21
 
21
22
  export type CreatorClient = {
22
23
  createPremint: PremintClient["createPremint"];
@@ -35,6 +36,9 @@ export type CollectorClient = {
35
36
  getMintCosts: MintClient["getMintCosts"];
36
37
  getToken: MintClient["get"];
37
38
  getTokensOfContract: MintClient["getOfContract"];
39
+ buy1155OnSecondary: SecondaryClient["buy1155OnSecondary"];
40
+ sell1155OnSecondary: SecondaryClient["sell1155OnSecondary"];
41
+ getSecondaryInfo: SecondaryClient["getSecondaryInfo"];
38
42
  };
39
43
 
40
44
  export type CreatorClientConfig = ClientConfig & {
@@ -113,6 +117,10 @@ export function createCollectorClient(
113
117
  premintGetter: premintGetterToUse,
114
118
  mintGetter: mintGetterToUse,
115
119
  });
120
+ const secondaryClient = new SecondaryClient({
121
+ publicClient: params.publicClient,
122
+ chainId: params.chainId,
123
+ });
116
124
 
117
125
  return {
118
126
  getPremint: (p) =>
@@ -126,5 +134,8 @@ export function createCollectorClient(
126
134
  getTokensOfContract: (p) => mintClient.getOfContract(p),
127
135
  mint: (p) => mintClient.mint(p),
128
136
  getMintCosts: (p) => mintClient.getMintCosts(p),
137
+ buy1155OnSecondary: (p) => secondaryClient.buy1155OnSecondary(p),
138
+ sell1155OnSecondary: (p) => secondaryClient.sell1155OnSecondary(p),
139
+ getSecondaryInfo: (p) => secondaryClient.getSecondaryInfo(p),
129
140
  };
130
141
  }
@@ -0,0 +1,20 @@
1
+ import { formatEther } from "viem";
2
+
3
+ export const WEI_PER_SPARK = 1_000_000_000_000n;
4
+
5
+ export function convertWeiToSparks(wei: string | number | bigint) {
6
+ return BigInt(wei) / WEI_PER_SPARK;
7
+ }
8
+
9
+ // Returns the price in USDC given an amount in WEI
10
+ export function convertWeiToUSD({
11
+ amount,
12
+ ethPriceInUSD,
13
+ }: {
14
+ amount: bigint;
15
+ ethPriceInUSD: number;
16
+ }) {
17
+ const amountInEth = formatEther(amount);
18
+
19
+ return parseFloat(amountInEth) * ethPriceInUSD;
20
+ }
@@ -0,0 +1,276 @@
1
+ import { describe, expect, vi } from "vitest";
2
+ import {
3
+ Address,
4
+ parseEther,
5
+ PublicClient,
6
+ TestClient,
7
+ WalletClient,
8
+ Account,
9
+ } from "viem";
10
+ import { zoraSepolia } from "viem/chains";
11
+ import {
12
+ forkUrls,
13
+ makeAnvilTest,
14
+ simulateAndWriteContractWithRetries,
15
+ } from "src/anvil";
16
+ import { createCollectorClient } from "src/sdk";
17
+ import {
18
+ zoraCreator1155ImplABI,
19
+ zoraTimedSaleStrategyABI,
20
+ zoraTimedSaleStrategyAddress,
21
+ } from "@zoralabs/protocol-deployments";
22
+ import { makeContractParameters } from "src/utils";
23
+ import { setupContractAndToken } from "src/fixtures/contract-setup";
24
+ import { CollectorClient } from "src/sdk";
25
+ import { ERROR_SECONDARY_NOT_STARTED } from "./secondary-client";
26
+ import { ISubgraphQuerier } from "src/apis/subgraph-querier";
27
+ import { mockTimedSaleStrategyTokenQueryResult } from "src/fixtures/mint-query-results";
28
+ import { new1155ContractVersion } from "src/create/contract-setup";
29
+
30
+ export async function advanceToSaleAndAndLaunchMarket({
31
+ contractAddress,
32
+ tokenId,
33
+ testClient,
34
+ publicClient,
35
+ walletClient,
36
+ collectorClient,
37
+ chainId,
38
+ account,
39
+ }: {
40
+ contractAddress: Address;
41
+ tokenId: bigint;
42
+ testClient: TestClient;
43
+ publicClient: PublicClient;
44
+ walletClient: WalletClient;
45
+ collectorClient: CollectorClient;
46
+ chainId: number;
47
+ account: Address | Account;
48
+ }) {
49
+ const saleEnd = (await collectorClient.getSecondaryInfo({
50
+ contract: contractAddress,
51
+ tokenId,
52
+ }))!.saleEnd!;
53
+
54
+ // advance to end of sale
55
+ await testClient.setNextBlockTimestamp({
56
+ timestamp: saleEnd,
57
+ });
58
+
59
+ await testClient.mine({
60
+ blocks: 1,
61
+ });
62
+
63
+ // advance to end of sale
64
+ // launch the market
65
+ await simulateAndWriteContractWithRetries({
66
+ parameters: makeContractParameters({
67
+ abi: zoraTimedSaleStrategyABI,
68
+ functionName: "launchMarket",
69
+ args: [contractAddress, tokenId],
70
+ address:
71
+ zoraTimedSaleStrategyAddress[
72
+ chainId as keyof typeof zoraTimedSaleStrategyAddress
73
+ ],
74
+ account: account,
75
+ }),
76
+ publicClient,
77
+ walletClient,
78
+ });
79
+ }
80
+
81
+ describe("secondary", () => {
82
+ makeAnvilTest({
83
+ forkBlockNumber: 13914833,
84
+ forkUrl: forkUrls.zoraSepolia,
85
+ anvilChainId: zoraSepolia.id,
86
+ })(
87
+ "it returns an error when the market is not launched",
88
+ async ({
89
+ viemClients: { publicClient, chain, walletClient, testClient },
90
+ }) => {
91
+ const creatorAccount = (await walletClient.getAddresses()!)[0]!;
92
+ const collectorAccount = (await walletClient.getAddresses()!)[1]!;
93
+
94
+ const { contractAddress, newTokenId } = await setupContractAndToken({
95
+ chain,
96
+ publicClient,
97
+ creatorAccount,
98
+ walletClient,
99
+ });
100
+
101
+ await testClient.setBalance({
102
+ address: collectorAccount,
103
+ value: parseEther("100"),
104
+ });
105
+
106
+ const collectorClient = createCollectorClient({
107
+ chainId: chain.id,
108
+ publicClient,
109
+ });
110
+
111
+ const secondaryInfo = await collectorClient.getSecondaryInfo({
112
+ contract: contractAddress,
113
+ tokenId: newTokenId,
114
+ });
115
+
116
+ expect(secondaryInfo?.secondaryActivated).toBe(false);
117
+
118
+ const buyResult = await collectorClient.buy1155OnSecondary({
119
+ account: collectorAccount,
120
+ quantity: 100n,
121
+ contract: contractAddress,
122
+ tokenId: newTokenId,
123
+ });
124
+
125
+ expect(buyResult.error).toEqual(ERROR_SECONDARY_NOT_STARTED);
126
+ },
127
+ 20_000,
128
+ );
129
+
130
+ makeAnvilTest({
131
+ forkBlockNumber: 13914833,
132
+ forkUrl: forkUrls.zoraSepolia,
133
+ anvilChainId: zoraSepolia.id,
134
+ })(
135
+ "it can buy and sell on secondary when the market is launched",
136
+ async ({
137
+ viemClients: { publicClient, chain, walletClient, testClient },
138
+ }) => {
139
+ const creatorAccount = (await walletClient.getAddresses()!)[0]!;
140
+ const collectorAccount = (await walletClient.getAddresses()!)[1]!;
141
+
142
+ const { contractAddress, newTokenId, mintGetter } =
143
+ await setupContractAndToken({
144
+ chain,
145
+ publicClient,
146
+ creatorAccount,
147
+ walletClient,
148
+ });
149
+
150
+ await testClient.setBalance({
151
+ address: collectorAccount,
152
+ value: parseEther("100"),
153
+ });
154
+
155
+ const quantityToMint = 100_000n;
156
+
157
+ mintGetter.subgraphQuerier.query = vi
158
+ .fn<ISubgraphQuerier["query"]>()
159
+ .mockResolvedValue({
160
+ zoraCreateToken: mockTimedSaleStrategyTokenQueryResult({
161
+ chainId: chain.id,
162
+ tokenId: newTokenId,
163
+ contractAddress,
164
+ contractVersion:
165
+ new1155ContractVersion[
166
+ chain.id as keyof typeof new1155ContractVersion
167
+ ],
168
+ }),
169
+ });
170
+
171
+ const collectorClient = createCollectorClient({
172
+ chainId: chain.id,
173
+ publicClient,
174
+ mintGetter,
175
+ });
176
+
177
+ const { parameters: collectParameters } = await collectorClient.mint({
178
+ minterAccount: collectorAccount,
179
+ mintType: "1155",
180
+ quantityToMint,
181
+ tokenId: newTokenId,
182
+ tokenContract: contractAddress,
183
+ });
184
+
185
+ await simulateAndWriteContractWithRetries({
186
+ parameters: collectParameters,
187
+ walletClient,
188
+ publicClient,
189
+ });
190
+
191
+ await advanceToSaleAndAndLaunchMarket({
192
+ contractAddress,
193
+ tokenId: newTokenId,
194
+ testClient,
195
+ publicClient,
196
+ walletClient,
197
+ collectorClient,
198
+ chainId: chain.id,
199
+ account: collectorAccount,
200
+ });
201
+
202
+ // now get the price ot buy on secondary
203
+ const quantityToBuy = 1000n;
204
+
205
+ const buyResult = await collectorClient.buy1155OnSecondary({
206
+ account: collectorAccount,
207
+ quantity: quantityToBuy,
208
+ contract: contractAddress,
209
+ tokenId: newTokenId,
210
+ });
211
+
212
+ expect(buyResult.error).toBeUndefined();
213
+
214
+ expect(buyResult.price!.wei.perToken).toBeGreaterThan(
215
+ parseEther("0.000111"),
216
+ );
217
+
218
+ // expected amount with slippage is total price * 1.005 considering bigint:
219
+ const expectedTotalWithSlippage =
220
+ buyResult.price!.wei.total + (buyResult.price!.wei.total * 5n) / 1000n;
221
+
222
+ expect(buyResult.parameters!.value).toBe(expectedTotalWithSlippage);
223
+
224
+ // execute the buy
225
+ await simulateAndWriteContractWithRetries({
226
+ parameters: buyResult.parameters!,
227
+ walletClient,
228
+ publicClient,
229
+ });
230
+
231
+ // now get balance of erc1155, should be minted by quantity bought
232
+ let balance = await publicClient.readContract({
233
+ abi: zoraCreator1155ImplABI,
234
+ address: contractAddress,
235
+ functionName: "balanceOf",
236
+ args: [collectorAccount, newTokenId],
237
+ });
238
+
239
+ expect(balance).toBe(quantityToBuy + quantityToMint);
240
+
241
+ // now sell 10_000n tokens
242
+ const quantityToSell = 10_000n;
243
+
244
+ const sellResult = await collectorClient.sell1155OnSecondary({
245
+ account: collectorAccount,
246
+ quantity: quantityToSell,
247
+ contract: contractAddress,
248
+ tokenId: newTokenId,
249
+ });
250
+
251
+ expect(sellResult.error).toBeUndefined();
252
+
253
+ expect(sellResult.price!.wei.perToken).toBeLessThan(
254
+ parseEther("0.000111"),
255
+ );
256
+
257
+ // execute the sell
258
+ await simulateAndWriteContractWithRetries({
259
+ parameters: sellResult.parameters!,
260
+ walletClient,
261
+ publicClient,
262
+ });
263
+
264
+ // now get balance of erc1155, should be minted by quantity bought
265
+ balance = await publicClient.readContract({
266
+ abi: zoraCreator1155ImplABI,
267
+ address: contractAddress,
268
+ functionName: "balanceOf",
269
+ args: [collectorAccount, newTokenId],
270
+ });
271
+
272
+ expect(balance).toBe(quantityToMint + quantityToBuy - quantityToSell);
273
+ },
274
+ 30_000,
275
+ );
276
+ });