├── .github
└── CODEOWNERS
├── .gitignore
├── .yarnrc.yml
├── LICENSE.md
├── README.md
├── examples
├── README.md
├── common.ts
├── deposit_nft.ts
├── deposit_sol.ts
├── get_pool_bid_details.ts
├── init_pool.ts
├── list_edit_delist_nft.ts
├── package-lock.json
└── package.json
├── package.json
├── src
├── common.ts
├── index.ts
├── tensor_bid
│ ├── constants.ts
│ ├── idl
│ │ ├── tensor_bid.ts
│ │ └── tensor_bid_v0_1_0.ts
│ ├── index.ts
│ ├── pda.ts
│ └── sdk.ts
├── tensor_whitelist
│ ├── constants.ts
│ ├── idl
│ │ ├── tensor_whitelist.ts
│ │ └── tensor_whitelist_v0_1_0.ts
│ ├── index.ts
│ ├── pda.ts
│ └── sdk.ts
├── tensorswap
│ ├── constants.ts
│ ├── idl
│ │ ├── tensorswap.ts
│ │ ├── tensorswap_v0_1_32.ts
│ │ ├── tensorswap_v0_2_0.ts
│ │ ├── tensorswap_v0_3_0.ts
│ │ ├── tensorswap_v0_3_5.ts
│ │ ├── tensorswap_v1_0_0.ts
│ │ ├── tensorswap_v1_1_0.ts
│ │ ├── tensorswap_v1_3_0.ts
│ │ ├── tensorswap_v1_4_0.ts
│ │ ├── tensorswap_v1_5_0.ts
│ │ ├── tensorswap_v1_6_0.ts
│ │ └── tensorswap_v1_7_0.ts
│ ├── index.ts
│ ├── pda.ts
│ ├── prices.ts
│ ├── sdk.ts
│ └── types.ts
├── token2022.ts
└── types.ts
├── tsconfig.base.json
├── tsconfig.cjs.json
├── tsconfig.json
├── tsconfig.tests.json
└── yarn.lock
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @richardwu @ilmoi
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | target
3 | **/*.rs.bk
4 | node_modules
5 | .yarn/
6 |
7 | # Private keys/secrets
8 | *.jks
9 | *.p8
10 | *.p12
11 | *.key
12 | .env
13 |
14 | # macOS
15 | .DS_Store
16 |
17 | # WSL
18 | *:Zone.Identifier
19 |
20 | # Temp files
21 | *.orig.*
22 | *.swp
23 | *.log
24 |
25 | # Node/tsc
26 | node_modules/
27 | npm-debug.*
28 | dist/
29 |
30 | # Data
31 | *.jsonl
32 | *.gz
33 |
34 | # IDEs
35 | .idea/
36 | .vscode/
37 |
--------------------------------------------------------------------------------
/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | nodeLinker: node-modules
2 |
3 | npmScopes:
4 | tensor-oss:
5 | npmAlwaysAuth: true
6 | tensor-hq:
7 | npmAlwaysAuth: true
8 | npmRegistryServer: "https://registry.npmjs.org"
9 |
10 | yarnPath: .yarn/releases/yarn-3.3.1.cjs
11 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | https://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | Copyright 2013-2018 Docker, Inc.
180 |
181 | Licensed under the Apache License, Version 2.0 (the "License");
182 | you may not use this file except in compliance with the License.
183 | You may obtain a copy of the License at
184 |
185 | https://www.apache.org/licenses/LICENSE-2.0
186 |
187 | Unless required by applicable law or agreed to in writing, software
188 | distributed under the License is distributed on an "AS IS" BASIS,
189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
190 | See the License for the specific language governing permissions and
191 | limitations under the License.
192 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Tensorswap SDK
2 |
3 | - [Getting Started](#getting-started)
4 | - [Example Code](#example-code)
5 | - [API Access](#api-access)
6 |
7 | ## Getting Started
8 |
9 | ### From npm/yarn (RECOMMENDED)
10 |
11 | ```
12 | # yarn
13 | yarn add @tensor-oss/tensorswap-sdk
14 | # npm
15 | npm install @tensor-oss/tensorswap-sdk
16 | ```
17 |
18 | ### From source
19 |
20 | ```sh
21 | git clone https://github.com/tensor-hq/tensorswap-sdk.git
22 | cd tensorswap-sdk/
23 | yarn
24 | # Build JS files
25 | yarn tsc
26 | ```
27 |
28 | ## Example Code
29 |
30 | Working examples can be found under `examples/`.
31 |
32 | ```ts
33 | const { AnchorProvider, Wallet } = require("@project-serum/anchor");
34 | const { Connection, Keypair, PublicKey } = require("@solana/web3.js");
35 | const {
36 | TensorSwapSDK,
37 | TensorWhitelistSDK,
38 | computeTakerPrice,
39 | TakerSide,
40 | castPoolConfigAnchor,
41 | findWhitelistPDA,
42 | } = require("@tensor-oss/tensorswap-sdk");
43 |
44 | const conn = new Connection("https://api.mainnet-beta.solana.com");
45 | const provider = new AnchorProvider(conn, new Wallet(Keypair.generate()));
46 | const swapSdk = new TensorSwapSDK({ provider });
47 | const wlSdk = new TensorWhitelistSDK({ provider });
48 |
49 | // ========= Compute current price (Buy + sell)
50 |
51 | // Fetch the pool PDA for its settings.
52 | const pool = await swapSdk.fetchPool(new PublicKey("
"));
53 | const config = castPoolConfigAnchor(pool.config);
54 |
55 | const price = computeTakerPrice({
56 | takerSide: TakerSide.Buy, // or TakerSide.Sell for selling
57 | extraNFTsSelected: 0,
58 |
59 | // These fields can be extracted from the pool object above.
60 | config,
61 | takerSellCount: pool.takerSellCount,
62 | takerBuyCount: pool.takerBuyCount,
63 | maxTakerSellCount: pool.maxTakerSellCount,
64 | statsTakerSellCount: pool.stats.takerSellCount,
65 | slippage: , // add optional slippage: in case pool updates on-chain
66 | });
67 |
68 |
69 |
70 | // ========= Buying
71 | {
72 | const {
73 | tx: { ixs },
74 | } = await swapSdk.buyNft({
75 | // Whitelist PDA address where name = tensor slug (see TensorWhitelistSDK.nameToBuffer)
76 | whitelist,
77 | // NFT Mint address
78 | nftMint,
79 | // Buyer ATA account (destination)
80 | nftBuyerAcc,
81 | // owner of NFT (in pool PDA)
82 | owner,
83 | // buyer
84 | buyer,
85 | // PoolConfig object: construct from pool PDA
86 | config,
87 | // max price buyer is willing to pay (add ~0.1% for exponential pools b/c of rounding differences)
88 | // see `computeTakerPrice` above to get the current price
89 | maxPrice
90 | });
91 | const buyTx = new Transaction(...ixs);
92 | }
93 |
94 | // ========= Selling
95 |
96 | // uuid = Tensor collection ID (see "Collection UUID" API endpoint below)
97 | const uuid = "..."
98 |
99 | // Remove "-" symbols from uuid, so it's within the 32 seed length limit. Additionally convert the uuid to a Uint8Array
100 | const uuidArray = Buffer.from(uuid.replaceAll("-", "")).toJSON().data;
101 |
102 | // Finding the PDA address
103 | const wlAddr = findWhitelistPDA({uuid: uuidArray})[0];
104 |
105 | // Step 1: Prepare the mint proof PDA (if required).
106 | {
107 | const wl = await wlSdk.fetchWhitelist(wlAddr);
108 |
109 | // Proof is only required if rootHash is NOT a 0 array, o/w not necessary!
110 | if(JSON.stringify(wl.rootHash) !== JSON.stringify(Array(32).fill(0))) {
111 | // Off-chain merkle proof (see "Mint Proof" API endpoint below).
112 | const proof = ...;
113 |
114 | const {
115 | tx: { ixs },
116 | } = await wlSdk.initUpdateMintProof({
117 | // User signing the tx (the seller)
118 | user,
119 | whitelist: wlAddr,
120 | // (NFT) Mint address
121 | mint,
122 | proof,
123 | });
124 | const proofTx = new Transaction(...ixs);
125 | }
126 | }
127 |
128 | // Step 2: Send sell tx.
129 | {
130 | const {
131 | tx: { ixs },
132 | } = await swapSdk.sellNft({
133 | type: "token", // or 'trade' for a trade pool
134 | whitelist: wlAddr,
135 | // NFT Mint address
136 | nftMint,
137 | // Token account holding seller's mint
138 | nftSellerAcc,
139 | // owner of NFT (in pool PDA)
140 | owner,
141 | // seller
142 | seller,
143 | // PoolConfig object: construct from pool PDA
144 | config,
145 | // min price seller is willing to receive (sub ~0.1% for exponential pools b/c of rounding differences)
146 | // see `computeTakerPrice` above to get the current price
147 | minPrice,
148 | });
149 | const sellTx = new Transaction(...ixs);
150 | }
151 |
152 | // ========= TODO: initPool / closePool / editPool / withdrawNft / depositNft / withdrawSol / depositSol
153 | ```
154 |
155 | ## API Access
156 |
157 | Docs can be [found here](https://tensor-hq.notion.site/PUBLIC-Tensor-Trade-API-alpha-b18e1a196187473bac9b5d6de5b47032).
158 |
159 | Ping us in our [Discord](https://www.discord.com/invite/a8spfqxEpC) for access.
160 |
161 | ### Collection UUID
162 |
163 | You can query all Tensor collections and their metadata, including their `id` which
164 | corresponds to `whitelist.uuid` with [this query](https://www.notion.so/tensor-hq/PUBLIC-Tensor-Trade-API-alpha-b18e1a196187473bac9b5d6de5b47032#56b333bfe0b641f8acad51a963a04f4f).
165 |
166 | Alternative, you can find a collection for a corresponding mint:
167 | 1. [Get the mint's Tensor slug](https://www.notion.so/tensor-hq/PUBLIC-Tensor-Trade-API-alpha-b18e1a196187473bac9b5d6de5b47032#5ae4f2d0499a4c6ba3ceed4f9ee949ad)
168 | 2. [Get the collection's ID](https://www.notion.so/tensor-hq/PUBLIC-Tensor-Trade-API-alpha-b18e1a196187473bac9b5d6de5b47032#59c583754aa2477caacd2b436071d564)
169 | 3. Use the ID as the `uuid` for the whitelist
170 |
171 | The ID for a collection will never change, so feel free to cache this locally.
172 |
173 | A mint's collection will almost always never change (99% of the time), so feel free to cache this as needed and update if necessary.
174 |
175 | ### Mint Proof
176 |
177 | For selling and depositing and for some collections (where `whitelist.rootHash` is not a 0-zero),
178 | you will need to fetch the off-chain [merkle proofs](https://en.wikipedia.org/wiki/Merkle_tree) we use for collection whitelisting from our API.
179 |
180 | Endpoint + example can be [found here](https://www.notion.so/tensor-hq/PUBLIC-Tensor-Trade-API-alpha-b18e1a196187473bac9b5d6de5b47032#9be7fb3fc59f49e08cc10a0d7d1d7ba7).
181 |
182 |
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # Tensor SDK examples
2 |
3 | ## Setup
4 |
5 | ```bash
6 | npm ci
7 | ```
8 |
9 | ## Running a script
10 |
11 | ```bash
12 | node_modules/.bin/ts-node init_pool.ts
13 | ```
14 |
--------------------------------------------------------------------------------
/examples/common.ts:
--------------------------------------------------------------------------------
1 | import { getAssociatedTokenAddressSync } from "@solana/spl-token";
2 | import { Connection, Keypair, PublicKey } from "@solana/web3.js";
3 |
4 | export const conn = new Connection("https://api.mainnet-beta.solana.com");
5 | export const keypair = Keypair.generate();
6 |
7 | export const nftMint = new PublicKey(
8 | "AhzUD99Lq9wWXLWQHXF6y3gGZzmxyNU9uMBW7hdtpEg4"
9 | );
10 | export const nftSource = getAssociatedTokenAddressSync(
11 | nftMint,
12 | keypair.publicKey
13 | );
14 | export const whitelist = new PublicKey(
15 | "7GLDrSDBVoBkdX1odXQ6WM8qyTrAoje8mx5LeGbRY8PU"
16 | );
17 |
--------------------------------------------------------------------------------
/examples/deposit_nft.ts:
--------------------------------------------------------------------------------
1 | import { AnchorProvider, BN, Wallet } from "@project-serum/anchor";
2 | import { LAMPORTS_PER_SOL, PublicKey, Transaction } from "@solana/web3.js";
3 | import {
4 | CurveTypeAnchor,
5 | PoolTypeAnchor,
6 | TensorSwapSDK,
7 | } from "@tensor-hq/tensorswap-sdk";
8 | import { conn, keypair, nftMint, nftSource, whitelist } from "./common";
9 |
10 | const provider = new AnchorProvider(conn, new Wallet(keypair), {
11 | commitment: "confirmed",
12 | });
13 | const swapSdk = new TensorSwapSDK({ provider });
14 |
15 | (async () => {
16 | const data = await swapSdk.depositNft({
17 | owner: keypair.publicKey,
18 | // Whitelist used to create the pool.
19 | whitelist,
20 | // NFT to deposit.
21 | nftMint,
22 | nftSource,
23 | // Use the original config of your pool.
24 | config: {
25 | poolType: PoolTypeAnchor.NFT,
26 | curveType: CurveTypeAnchor.Exponential,
27 | startingPrice: new BN(0.1 * LAMPORTS_PER_SOL),
28 | delta: new BN(0),
29 | mmCompoundFees: true,
30 | mmFeeBps: null,
31 | },
32 | });
33 |
34 | const tx = new Transaction().add(...data.tx.ixs);
35 | })();
36 |
--------------------------------------------------------------------------------
/examples/deposit_sol.ts:
--------------------------------------------------------------------------------
1 | import { AnchorProvider, Wallet, BN } from "@project-serum/anchor";
2 | import { PublicKey, LAMPORTS_PER_SOL, Transaction } from "@solana/web3.js";
3 | import { getAssociatedTokenAddress } from "@solana/spl-token";
4 | import {
5 | TensorSwapSDK,
6 | CurveTypeAnchor,
7 | PoolTypeAnchor,
8 | } from "@tensor-hq/tensorswap-sdk";
9 | import { conn, keypair, whitelist } from "./common";
10 |
11 | const provider = new AnchorProvider(conn, new Wallet(keypair), {
12 | commitment: "confirmed",
13 | });
14 | const swapSdk = new TensorSwapSDK({ provider });
15 |
16 | (async () => {
17 | const data = await swapSdk.depositSol({
18 | owner: keypair.publicKey,
19 | // Whitelist used to create the pool.
20 | whitelist,
21 | // Deposit 55 SOL.
22 | lamports: new BN(55 * LAMPORTS_PER_SOL),
23 | // Use the original config of your pool.
24 | config: {
25 | poolType: PoolTypeAnchor.Token,
26 | curveType: CurveTypeAnchor.Exponential,
27 | startingPrice: new BN(0.1 * LAMPORTS_PER_SOL),
28 | delta: new BN(0),
29 | mmCompoundFees: true,
30 | mmFeeBps: null,
31 | },
32 | });
33 |
34 | const tx = new Transaction().add(...data.tx.ixs);
35 | })();
36 |
--------------------------------------------------------------------------------
/examples/get_pool_bid_details.ts:
--------------------------------------------------------------------------------
1 | import { PublicKey } from "@solana/web3.js";
2 | import { AnchorProvider, Wallet, BN } from "@coral-xyz/anchor";
3 | import { TensorSwapSDK, TakerSide, CurveType, PoolType, castPoolConfigAnchor, computeMakerAmountCount } from "@tensor-oss/tensorswap-sdk";
4 | import { conn, keypair } from "./common";
5 | import Big from "big.js";
6 |
7 | const provider = new AnchorProvider(conn, new Wallet(keypair), {
8 | commitment: "confirmed",
9 | });
10 | const swapSdk = new TensorSwapSDK({ provider });
11 | const HUNDRED_PCT_BPS = 100_00;
12 |
13 | async function getPoolBidDetails(poolAddress) {
14 |
15 | // fetch pool
16 | const pool = await swapSdk.fetchPool(new PublicKey(poolAddress));
17 | const config = castPoolConfigAnchor(pool.config);
18 |
19 | // return early if pool is one-sided listing-only
20 | if (config.poolType == PoolType.NFT) {
21 | console.log("Listing-only pool can not bid.");
22 | return null;
23 | }
24 |
25 | // fetch balance of margin account if pool is attached to escrow, fetch balance of solEscrow instead if pool is not attached to escrow
26 | const solBalanceLamports = pool.margin != null ? new BN(await conn.getBalance(pool.margin)) : new BN(await conn.getBalance(pool.solEscrow));
27 |
28 | // retrieve amount of possible bids, total lamports needed for that amount of bids and initial price of the pool
29 | const { allowedCount, totalAmount, initialPrice } = computeMakerAmountCount({
30 | desired: { total: solBalanceLamports },
31 | maxCountWhenInfinite: 1000,
32 | takerSide: TakerSide.Sell,
33 | extraNFTsSelected: 0,
34 | config,
35 | takerSellCount: pool.takerSellCount,
36 | takerBuyCount: pool.takerBuyCount,
37 | maxTakerSellCount: pool.maxTakerSellCount,
38 | statsTakerSellCount: pool.stats.takerSellCount,
39 | statsTakerBuyCount: pool.stats.takerBuyCount,
40 | marginated: pool.margin !== null,
41 | });
42 |
43 | // return early if amount of possible bids is 0
44 | if (allowedCount == 0) {
45 | console.log("Pool is out of funds or reached maxTakerSellCount and won't bid anymore.");
46 | return null;
47 | }
48 |
49 | // retrieve initial highest bid price if pool is double sided (config.startingPrice would be the initial lowest list price in that case)
50 | var startingPriceBidSide: Number;
51 | if (config.poolType == PoolType.Trade) {
52 |
53 | // on linear curvetype, subtract delta once and multiply by (1 - mmFee)
54 | if (config.curveType == CurveType.Linear) {
55 | startingPriceBidSide = config.startingPrice
56 | .sub(config.delta)
57 | .mul(Big(1)
58 | .sub(Big(config.mmFeeBps)
59 | .div(Big(HUNDRED_PCT_BPS))
60 | )
61 | );
62 | }
63 |
64 | // on exponential curvetype, divide by (1 + delta) once and multiply by (1 - mmFee)
65 | else if (config.curveType == CurveType.Exponential) {
66 | startingPriceBidSide = config.startingPrice
67 | .div(Big(1)
68 | .add(config.delta
69 | .div(Big(HUNDRED_PCT_BPS))
70 | )
71 | )
72 | .mul(Big(1)
73 | .sub(Big(config.mmFeeBps)
74 | .div(Big(HUNDRED_PCT_BPS))
75 | )
76 | );
77 | }
78 | }
79 |
80 | // else if one-sided bidding-only pool, config.startingPrice matches initial highest bid already
81 | else if (config.poolType == PoolType.Token) {
82 | startingPriceBidSide = config.startingPrice;
83 | }
84 |
85 | // get the current lowest bid price by shifting price down by allowedCountWithLimit - 1 (since arg = 0 would be the initial highest bid) + pool.takerSellCount - pool.takerBuyCount (to do x less steps depending on how many bids already got fulfilled)
86 | const currentLowestBidPrice = shiftPriceByDelta(
87 | config.curveType,
88 | startingPriceBidSide!,
89 | config.delta,
90 | "down",
91 | allowedCount - 1 + pool.takerSellCount - pool.takerBuyCount
92 | );
93 |
94 | // get the highest bid price by shifting up or down x times depending on how many bids already got fulfilled
95 | var currentHighestBidPrice = pool.takerSellCount - pool.takerBuyCount >= 0 ?
96 | shiftPriceByDelta(config.curveType,
97 | startingPriceBidSide!,
98 | config.delta,
99 | "down",
100 | pool.takerSellCount - pool.takerBuyCount
101 | ) :
102 | shiftPriceByDelta(config.curveType,
103 | startingPriceBidSide!,
104 | config.delta,
105 | "up",
106 | (pool.takerSellCount - pool.takerBuyCount) * -1
107 | );
108 |
109 | console.log("Pool has " + allowedCount + " bids left, bid range is: " + Number(currentHighestBidPrice / 1_000_000_000) + "-" + Number(currentLowestBidPrice / 1_000_000_000) + " SOL.");
110 | }
111 |
112 | // helper function copied from https://github.com/tensor-hq/tensorswap-sdk/blob/main/src/tensorswap/prices.ts#L145
113 | const shiftPriceByDelta = (
114 | curveType: CurveType,
115 | startingPrice: Big,
116 | delta: Big,
117 | direction: "up" | "down",
118 | times: number
119 | ): Big => {
120 | switch (curveType) {
121 | case CurveType.Exponential:
122 | switch (direction) {
123 | // price * (1 + delta)^trade_count
124 | case "up":
125 | return startingPrice.mul(
126 | new Big(1).add(delta.div(HUNDRED_PCT_BPS)).pow(times)
127 | );
128 | case "down":
129 | return startingPrice.div(
130 | new Big(1).add(delta.div(HUNDRED_PCT_BPS)).pow(times)
131 | );
132 | }
133 | break;
134 | case CurveType.Linear:
135 | switch (direction) {
136 | case "up":
137 | return startingPrice.add(delta.mul(times));
138 | case "down":
139 | return startingPrice.sub(delta.mul(times));
140 | }
141 | }
142 | };
143 |
144 | getPoolBidDetails("G498NY38Jxdab9BbGfaKiHze1pcr94ZLqusDoxUwoWsm");
145 |
--------------------------------------------------------------------------------
/examples/init_pool.ts:
--------------------------------------------------------------------------------
1 | import { AnchorProvider, BN, Wallet } from "@project-serum/anchor";
2 | import { LAMPORTS_PER_SOL, PublicKey, Transaction } from "@solana/web3.js";
3 | import {
4 | CurveTypeAnchor,
5 | OrderType,
6 | PoolTypeAnchor,
7 | TensorSwapSDK,
8 | } from "@tensor-hq/tensorswap-sdk";
9 | import { conn, keypair, whitelist } from "./common";
10 |
11 | const provider = new AnchorProvider(conn, new Wallet(keypair), {
12 | commitment: "confirmed",
13 | });
14 | const swapSdk = new TensorSwapSDK({ provider });
15 |
16 | (async () => {
17 | const initPool = await swapSdk.initPool({
18 | owner: keypair.publicKey,
19 | whitelist,
20 | config: {
21 | // Token = Collection-wide bid
22 | // NFT = Listing NFTs on a curve.
23 | // Trade = market-making order.
24 | poolType: PoolTypeAnchor.Token,
25 | // Exponential = % change.
26 | // Linear = SOL change.
27 | curveType: CurveTypeAnchor.Exponential,
28 | startingPrice: new BN(0.1 * LAMPORTS_PER_SOL),
29 | // If curveType = Exponential, this is in BPS (delta = 100 = 1%).
30 | // If curveType = Linear, this is in lamports (delta = 1e8 = 0.1 SOL).
31 | delta: new BN(0),
32 | // ===== These are only valid for poolType = Trade =====
33 | // If true, fees deposited back into pool to buy more NFTs.
34 | mmCompoundFees: true,
35 | // How much fee to collect for each buy + sell (mmFeeBps = 100 = 1%).
36 | mmFeeBps: null,
37 | },
38 | // Maximum # of NFTs to buy from the pool in its lifetime (important to set for shared escrows!).
39 | maxTakerSellCount: 1,
40 | // keep these as is.
41 | customAuthSeed: undefined,
42 | isCosigned: false,
43 | orderType: OrderType.Standard,
44 | });
45 | console.log(initPool);
46 | const tx = new Transaction().add(...initPool.tx.ixs);
47 | console.log(tx);
48 | })();
49 |
--------------------------------------------------------------------------------
/examples/list_edit_delist_nft.ts:
--------------------------------------------------------------------------------
1 | import { AnchorProvider, BN, Wallet } from "@project-serum/anchor";
2 | import { LAMPORTS_PER_SOL, Transaction } from "@solana/web3.js";
3 | import { TensorSwapSDK } from "@tensor-hq/tensorswap-sdk";
4 | import { conn, keypair, nftMint, nftSource } from "./common";
5 |
6 | const provider = new AnchorProvider(conn, new Wallet(keypair), {
7 | commitment: "confirmed",
8 | });
9 | const swapSdk = new TensorSwapSDK({ provider });
10 |
11 | (async () => {
12 | // List the NFT.
13 | {
14 | const data = await swapSdk.list({
15 | owner: keypair.publicKey,
16 | nftMint,
17 | nftSource,
18 | // Create listing to sell for 0.1 SOL.
19 | price: new BN(0.1 * LAMPORTS_PER_SOL),
20 | });
21 |
22 | const tx = new Transaction().add(...data.tx.ixs);
23 | // Sign + send tx.
24 | }
25 |
26 | // Edit the listing.
27 | {
28 | const data = await swapSdk.editSingleListing({
29 | owner: keypair.publicKey,
30 | nftMint,
31 | // Change listing to 0.5 SOL.
32 | price: new BN(0.5 * LAMPORTS_PER_SOL),
33 | });
34 |
35 | const tx = new Transaction().add(...data.tx.ixs);
36 | }
37 |
38 | // Delist.
39 | {
40 | const data = await swapSdk.delist({
41 | owner: keypair.publicKey,
42 | nftMint,
43 | // (!!) Specify the destination ATA for the NFT.
44 | nftDest: nftSource,
45 | });
46 |
47 | const tx = new Transaction().add(...data.tx.ixs);
48 | }
49 | })();
50 |
--------------------------------------------------------------------------------
/examples/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tensorswap-sdk-examples",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "@project-serum/anchor": "^0.26.0",
13 | "@solana/web3.js": "^1.75.0",
14 | "@tensor-hq/tensorswap-sdk": "^1.9.3"
15 | },
16 | "devDependencies": {
17 | "ts-node": "^10.9.1"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@tensor-oss/tensorswap-sdk",
3 | "version": "4.5.0",
4 | "description": "Anchor/JS SDK for interacting with TensorSwap, TensorWhitelist and TensorBid.",
5 | "sideEffects": false,
6 | "module": "./dist/esm/index.js",
7 | "main": "./dist/cjs/index.js",
8 | "types": "dist/cjs/index.d.ts",
9 | "files": [
10 | "/dist/esm/**/*",
11 | "/dist/cjs/**/*"
12 | ],
13 | "repository": "https://github.com/tensor-hq/tensorswap-sdk.git",
14 | "license": "MIT",
15 | "publishConfig": {
16 | "@tensor-hq:registry": "https://registry.npmjs.org"
17 | },
18 | "scripts": {
19 | "build": "anchor build && bash scripts/cp_idl.sh",
20 | "build:test": "bash scripts/test_build.sh",
21 | "build:ts": "rm -rf ./dist && yarn tsc && yarn tsc -p tsconfig.cjs.json",
22 | "fast-test": "anchor test --skip-build -- --features testing",
23 | "test": "yarn build:test && yarn fast-test",
24 | "push": "git push --atomic",
25 | "publish:private": "yarn build:ts && yarn npm publish",
26 | "publish:public": "yarn build:ts && npm publish --access public --registry https://registry.npmjs.org",
27 | "upgrade": "yarn add @tensor-hq/tensor-common"
28 | },
29 | "dependencies": {
30 | "@coral-xyz/anchor": "^0.26.0",
31 | "@msgpack/msgpack": "^2.8.0",
32 | "@saberhq/solana-contrib": "^1.14.11",
33 | "@solana/spl-token": "^0.4.0",
34 | "@solana/web3.js": "^1.73.0",
35 | "@tensor-hq/tensor-common": "^8.0.8",
36 | "@types/bn.js": "^5.1.0",
37 | "big.js": "^6.2.1",
38 | "bn.js": "^5.2.0",
39 | "js-sha256": "^0.9.0",
40 | "keccak256": "^1.0.6",
41 | "math-expression-evaluator": "^2.0.4",
42 | "merkletreejs": "^0.3.11",
43 | "uuid": "^8.3.2"
44 | },
45 | "devDependencies": {
46 | "@metaplex-foundation/mpl-token-auth-rules": "^2.0.0",
47 | "@metaplex-foundation/mpl-token-metadata": "^2.13.0",
48 | "@types/big.js": "^6.1.5",
49 | "@types/chai": "^4.3.10",
50 | "@types/chai-as-promised": "^7.1.4",
51 | "@types/mocha": "^10.0.4",
52 | "@types/uuid": "^8.3.4",
53 | "@types/yargs": "^17.0.12",
54 | "chai": "^4.3.6",
55 | "chai-as-promised": "^7.1.1",
56 | "chai-bn": "^0.3.1",
57 | "exponential-backoff": "^3.1.0",
58 | "jsbi": "^4.1.0",
59 | "mocha": "^10.0.0",
60 | "prettier": "^2.5.1",
61 | "ts-mocha": "^10.0.0",
62 | "ts-node": "^10.9.1",
63 | "typescript": "^4.3.5",
64 | "yargs": "^17.5.1"
65 | },
66 | "packageManager": "yarn@3.3.1"
67 | }
68 |
--------------------------------------------------------------------------------
/src/common.ts:
--------------------------------------------------------------------------------
1 | import { PublicKey } from "@solana/web3.js";
2 | import Mexp from "math-expression-evaluator";
3 |
4 | // pNFTs can be very expensive: just over allocate.
5 | export const DEFAULT_XFER_COMPUTE_UNITS = 800_000;
6 | export const DEFAULT_RULESET_ADDN_COMPUTE_UNITS = 400_000;
7 | export const DEFAULT_MICRO_LAMPORTS = 10_000;
8 |
9 | export type AccountSuffix =
10 | | "Nft Mint"
11 | | "Sol Escrow"
12 | | "Old Sol Escrow"
13 | | "New Sol Escrow"
14 | | "Pool"
15 | | "Old Pool"
16 | | "New Pool"
17 | | "Nft Escrow"
18 | | "Whitelist"
19 | | "Nft Receipt"
20 | | "Buyer"
21 | | "Seller"
22 | | "Owner"
23 | | "Nft Authority"
24 | | "Margin Account"
25 | | "Single Listing"
26 | | "Bid State"
27 | | "Bidder";
28 |
29 | export const evalMathExpr = (str: string) => {
30 | const mexp = new Mexp();
31 | return mexp.eval(str, [], {});
32 | };
33 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./types";
2 | export * from "./common";
3 | export * from "./tensorswap";
4 | export * from "./tensor_whitelist";
5 | export * from "./tensor_bid";
6 | export * from "./token2022";
7 |
--------------------------------------------------------------------------------
/src/tensor_bid/constants.ts:
--------------------------------------------------------------------------------
1 | import { PublicKey } from "@solana/web3.js";
2 |
3 | export const TBID_ADDR = new PublicKey(
4 | process.env.TBID_ADDR || "TB1Dqt8JeKQh7RLDzfYDJsq8KS4fS2yt87avRjyRxMv"
5 | );
6 |
--------------------------------------------------------------------------------
/src/tensor_bid/idl/tensor_bid.ts:
--------------------------------------------------------------------------------
1 | export type TensorBid = {
2 | "version": "1.0.0",
3 | "name": "tensor_bid",
4 | "constants": [
5 | {
6 | "name": "CURRENT_TBID_VERSION",
7 | "type": "u8",
8 | "value": "1"
9 | },
10 | {
11 | "name": "TBID_TAKER_FEE_BPS",
12 | "type": "u16",
13 | "value": "150"
14 | },
15 | {
16 | "name": "MAX_EXPIRY_SEC",
17 | "type": "i64",
18 | "value": "31536000"
19 | },
20 | {
21 | "name": "BID_STATE_SIZE",
22 | "type": {
23 | "defined": "usize"
24 | },
25 | "value": "8 + 1 + 8 + (32 * 2) + 1 + 8 + 33 + 8 + 56"
26 | }
27 | ],
28 | "instructions": [
29 | {
30 | "name": "bid",
31 | "accounts": [
32 | {
33 | "name": "nftMint",
34 | "isMut": false,
35 | "isSigner": false
36 | },
37 | {
38 | "name": "bidState",
39 | "isMut": true,
40 | "isSigner": false
41 | },
42 | {
43 | "name": "bidder",
44 | "isMut": true,
45 | "isSigner": true
46 | },
47 | {
48 | "name": "systemProgram",
49 | "isMut": false,
50 | "isSigner": false
51 | },
52 | {
53 | "name": "rent",
54 | "isMut": false,
55 | "isSigner": false
56 | },
57 | {
58 | "name": "tswap",
59 | "isMut": false,
60 | "isSigner": false
61 | },
62 | {
63 | "name": "marginAccount",
64 | "isMut": true,
65 | "isSigner": false
66 | }
67 | ],
68 | "args": [
69 | {
70 | "name": "lamports",
71 | "type": "u64"
72 | },
73 | {
74 | "name": "expireInSec",
75 | "type": {
76 | "option": "u64"
77 | }
78 | }
79 | ]
80 | },
81 | {
82 | "name": "takeBid",
83 | "accounts": [
84 | {
85 | "name": "tswap",
86 | "isMut": false,
87 | "isSigner": false
88 | },
89 | {
90 | "name": "feeVault",
91 | "isMut": true,
92 | "isSigner": false
93 | },
94 | {
95 | "name": "nftMint",
96 | "isMut": false,
97 | "isSigner": false
98 | },
99 | {
100 | "name": "nftBidderAcc",
101 | "isMut": true,
102 | "isSigner": false
103 | },
104 | {
105 | "name": "nftSellerAcc",
106 | "isMut": true,
107 | "isSigner": false
108 | },
109 | {
110 | "name": "nftTempAcc",
111 | "isMut": true,
112 | "isSigner": false
113 | },
114 | {
115 | "name": "nftMetadata",
116 | "isMut": true,
117 | "isSigner": false
118 | },
119 | {
120 | "name": "bidState",
121 | "isMut": true,
122 | "isSigner": false
123 | },
124 | {
125 | "name": "bidder",
126 | "isMut": true,
127 | "isSigner": false
128 | },
129 | {
130 | "name": "seller",
131 | "isMut": true,
132 | "isSigner": true
133 | },
134 | {
135 | "name": "tokenProgram",
136 | "isMut": false,
137 | "isSigner": false
138 | },
139 | {
140 | "name": "associatedTokenProgram",
141 | "isMut": false,
142 | "isSigner": false
143 | },
144 | {
145 | "name": "systemProgram",
146 | "isMut": false,
147 | "isSigner": false
148 | },
149 | {
150 | "name": "rent",
151 | "isMut": false,
152 | "isSigner": false
153 | },
154 | {
155 | "name": "nftEdition",
156 | "isMut": false,
157 | "isSigner": false
158 | },
159 | {
160 | "name": "sellerTokenRecord",
161 | "isMut": true,
162 | "isSigner": false
163 | },
164 | {
165 | "name": "bidderTokenRecord",
166 | "isMut": true,
167 | "isSigner": false
168 | },
169 | {
170 | "name": "tempTokenRecord",
171 | "isMut": true,
172 | "isSigner": false
173 | },
174 | {
175 | "name": "pnftShared",
176 | "accounts": [
177 | {
178 | "name": "tokenMetadataProgram",
179 | "isMut": false,
180 | "isSigner": false
181 | },
182 | {
183 | "name": "instructions",
184 | "isMut": false,
185 | "isSigner": false
186 | },
187 | {
188 | "name": "authorizationRulesProgram",
189 | "isMut": false,
190 | "isSigner": false
191 | }
192 | ]
193 | },
194 | {
195 | "name": "tensorswapProgram",
196 | "isMut": false,
197 | "isSigner": false
198 | },
199 | {
200 | "name": "authRules",
201 | "isMut": false,
202 | "isSigner": false
203 | },
204 | {
205 | "name": "marginAccount",
206 | "isMut": true,
207 | "isSigner": false
208 | },
209 | {
210 | "name": "takerBroker",
211 | "isMut": true,
212 | "isSigner": false
213 | }
214 | ],
215 | "args": [
216 | {
217 | "name": "lamports",
218 | "type": "u64"
219 | },
220 | {
221 | "name": "rulesAccPresent",
222 | "type": "bool"
223 | },
224 | {
225 | "name": "authorizationData",
226 | "type": {
227 | "option": {
228 | "defined": "AuthorizationDataLocal"
229 | }
230 | }
231 | },
232 | {
233 | "name": "optionalRoyaltyPct",
234 | "type": {
235 | "option": "u16"
236 | }
237 | }
238 | ]
239 | },
240 | {
241 | "name": "takeBidT22",
242 | "accounts": [
243 | {
244 | "name": "tswap",
245 | "isMut": false,
246 | "isSigner": false
247 | },
248 | {
249 | "name": "feeVault",
250 | "isMut": true,
251 | "isSigner": false
252 | },
253 | {
254 | "name": "nftMint",
255 | "isMut": false,
256 | "isSigner": false
257 | },
258 | {
259 | "name": "nftBidderAcc",
260 | "isMut": true,
261 | "isSigner": false
262 | },
263 | {
264 | "name": "nftSellerAcc",
265 | "isMut": true,
266 | "isSigner": false
267 | },
268 | {
269 | "name": "bidState",
270 | "isMut": true,
271 | "isSigner": false
272 | },
273 | {
274 | "name": "bidder",
275 | "isMut": true,
276 | "isSigner": false
277 | },
278 | {
279 | "name": "seller",
280 | "isMut": true,
281 | "isSigner": true
282 | },
283 | {
284 | "name": "tokenProgram",
285 | "isMut": false,
286 | "isSigner": false
287 | },
288 | {
289 | "name": "associatedTokenProgram",
290 | "isMut": false,
291 | "isSigner": false
292 | },
293 | {
294 | "name": "systemProgram",
295 | "isMut": false,
296 | "isSigner": false
297 | },
298 | {
299 | "name": "rent",
300 | "isMut": false,
301 | "isSigner": false
302 | },
303 | {
304 | "name": "tensorswapProgram",
305 | "isMut": false,
306 | "isSigner": false
307 | },
308 | {
309 | "name": "marginAccount",
310 | "isMut": true,
311 | "isSigner": false
312 | },
313 | {
314 | "name": "takerBroker",
315 | "isMut": true,
316 | "isSigner": false
317 | }
318 | ],
319 | "args": [
320 | {
321 | "name": "lamports",
322 | "type": "u64"
323 | }
324 | ]
325 | },
326 | {
327 | "name": "wnsTakeBid",
328 | "accounts": [
329 | {
330 | "name": "tswap",
331 | "isMut": false,
332 | "isSigner": false
333 | },
334 | {
335 | "name": "feeVault",
336 | "isMut": true,
337 | "isSigner": false
338 | },
339 | {
340 | "name": "nftMint",
341 | "isMut": false,
342 | "isSigner": false
343 | },
344 | {
345 | "name": "nftBidderAcc",
346 | "isMut": true,
347 | "isSigner": false
348 | },
349 | {
350 | "name": "nftSellerAcc",
351 | "isMut": true,
352 | "isSigner": false
353 | },
354 | {
355 | "name": "bidState",
356 | "isMut": true,
357 | "isSigner": false
358 | },
359 | {
360 | "name": "bidder",
361 | "isMut": true,
362 | "isSigner": false
363 | },
364 | {
365 | "name": "seller",
366 | "isMut": true,
367 | "isSigner": true
368 | },
369 | {
370 | "name": "tokenProgram",
371 | "isMut": false,
372 | "isSigner": false
373 | },
374 | {
375 | "name": "associatedTokenProgram",
376 | "isMut": false,
377 | "isSigner": false
378 | },
379 | {
380 | "name": "systemProgram",
381 | "isMut": false,
382 | "isSigner": false
383 | },
384 | {
385 | "name": "rent",
386 | "isMut": false,
387 | "isSigner": false
388 | },
389 | {
390 | "name": "tensorswapProgram",
391 | "isMut": false,
392 | "isSigner": false
393 | },
394 | {
395 | "name": "marginAccount",
396 | "isMut": true,
397 | "isSigner": false
398 | },
399 | {
400 | "name": "takerBroker",
401 | "isMut": true,
402 | "isSigner": false
403 | },
404 | {
405 | "name": "approveAccount",
406 | "isMut": true,
407 | "isSigner": false
408 | },
409 | {
410 | "name": "distribution",
411 | "isMut": true,
412 | "isSigner": false
413 | },
414 | {
415 | "name": "wnsProgram",
416 | "isMut": false,
417 | "isSigner": false
418 | },
419 | {
420 | "name": "distributionProgram",
421 | "isMut": false,
422 | "isSigner": false
423 | },
424 | {
425 | "name": "extraMetas",
426 | "isMut": false,
427 | "isSigner": false
428 | }
429 | ],
430 | "args": [
431 | {
432 | "name": "lamports",
433 | "type": "u64"
434 | }
435 | ]
436 | },
437 | {
438 | "name": "cancelBid",
439 | "accounts": [
440 | {
441 | "name": "nftMint",
442 | "isMut": false,
443 | "isSigner": false
444 | },
445 | {
446 | "name": "bidState",
447 | "isMut": true,
448 | "isSigner": false
449 | },
450 | {
451 | "name": "bidder",
452 | "isMut": true,
453 | "isSigner": true
454 | },
455 | {
456 | "name": "systemProgram",
457 | "isMut": false,
458 | "isSigner": false
459 | },
460 | {
461 | "name": "rent",
462 | "isMut": false,
463 | "isSigner": false
464 | }
465 | ],
466 | "args": []
467 | },
468 | {
469 | "name": "closeExpiredBid",
470 | "accounts": [
471 | {
472 | "name": "nftMint",
473 | "isMut": false,
474 | "isSigner": false
475 | },
476 | {
477 | "name": "bidState",
478 | "isMut": true,
479 | "isSigner": false
480 | },
481 | {
482 | "name": "bidder",
483 | "isMut": true,
484 | "isSigner": false
485 | },
486 | {
487 | "name": "systemProgram",
488 | "isMut": false,
489 | "isSigner": false
490 | },
491 | {
492 | "name": "rent",
493 | "isMut": false,
494 | "isSigner": false
495 | }
496 | ],
497 | "args": []
498 | }
499 | ],
500 | "accounts": [
501 | {
502 | "name": "bidState",
503 | "type": {
504 | "kind": "struct",
505 | "fields": [
506 | {
507 | "name": "version",
508 | "type": "u8"
509 | },
510 | {
511 | "name": "bidAmount",
512 | "type": "u64"
513 | },
514 | {
515 | "name": "nftMint",
516 | "type": "publicKey"
517 | },
518 | {
519 | "name": "bidder",
520 | "type": "publicKey"
521 | },
522 | {
523 | "name": "bump",
524 | "type": {
525 | "array": [
526 | "u8",
527 | 1
528 | ]
529 | }
530 | },
531 | {
532 | "name": "expiry",
533 | "type": "i64"
534 | },
535 | {
536 | "name": "margin",
537 | "type": {
538 | "option": "publicKey"
539 | }
540 | },
541 | {
542 | "name": "updatedAt",
543 | "type": "i64"
544 | },
545 | {
546 | "name": "reserved",
547 | "type": {
548 | "array": [
549 | "u8",
550 | 8
551 | ]
552 | }
553 | },
554 | {
555 | "name": "reserved1",
556 | "type": {
557 | "array": [
558 | "u8",
559 | 16
560 | ]
561 | }
562 | },
563 | {
564 | "name": "reserved2",
565 | "type": {
566 | "array": [
567 | "u8",
568 | 32
569 | ]
570 | }
571 | }
572 | ]
573 | }
574 | }
575 | ],
576 | "types": [
577 | {
578 | "name": "AuthorizationDataLocal",
579 | "type": {
580 | "kind": "struct",
581 | "fields": [
582 | {
583 | "name": "payload",
584 | "type": {
585 | "vec": {
586 | "defined": "TaggedPayload"
587 | }
588 | }
589 | }
590 | ]
591 | }
592 | },
593 | {
594 | "name": "TaggedPayload",
595 | "type": {
596 | "kind": "struct",
597 | "fields": [
598 | {
599 | "name": "name",
600 | "type": "string"
601 | },
602 | {
603 | "name": "payload",
604 | "type": {
605 | "defined": "PayloadTypeLocal"
606 | }
607 | }
608 | ]
609 | }
610 | },
611 | {
612 | "name": "SeedsVecLocal",
613 | "type": {
614 | "kind": "struct",
615 | "fields": [
616 | {
617 | "name": "seeds",
618 | "docs": [
619 | "The vector of derivation seeds."
620 | ],
621 | "type": {
622 | "vec": "bytes"
623 | }
624 | }
625 | ]
626 | }
627 | },
628 | {
629 | "name": "ProofInfoLocal",
630 | "type": {
631 | "kind": "struct",
632 | "fields": [
633 | {
634 | "name": "proof",
635 | "docs": [
636 | "The merkle proof."
637 | ],
638 | "type": {
639 | "vec": {
640 | "array": [
641 | "u8",
642 | 32
643 | ]
644 | }
645 | }
646 | }
647 | ]
648 | }
649 | },
650 | {
651 | "name": "PayloadTypeLocal",
652 | "type": {
653 | "kind": "enum",
654 | "variants": [
655 | {
656 | "name": "Pubkey",
657 | "fields": [
658 | "publicKey"
659 | ]
660 | },
661 | {
662 | "name": "Seeds",
663 | "fields": [
664 | {
665 | "defined": "SeedsVecLocal"
666 | }
667 | ]
668 | },
669 | {
670 | "name": "MerkleProof",
671 | "fields": [
672 | {
673 | "defined": "ProofInfoLocal"
674 | }
675 | ]
676 | },
677 | {
678 | "name": "Number",
679 | "fields": [
680 | "u64"
681 | ]
682 | }
683 | ]
684 | }
685 | }
686 | ],
687 | "events": [
688 | {
689 | "name": "BidEvent",
690 | "fields": [
691 | {
692 | "name": "lamports",
693 | "type": "u64",
694 | "index": false
695 | },
696 | {
697 | "name": "expiry",
698 | "type": "i64",
699 | "index": false
700 | },
701 | {
702 | "name": "mint",
703 | "type": "publicKey",
704 | "index": false
705 | },
706 | {
707 | "name": "bidder",
708 | "type": "publicKey",
709 | "index": false
710 | }
711 | ]
712 | },
713 | {
714 | "name": "TakeBidEvent",
715 | "fields": [
716 | {
717 | "name": "lamports",
718 | "type": "u64",
719 | "index": false
720 | },
721 | {
722 | "name": "tswapFee",
723 | "type": "u64",
724 | "index": false
725 | },
726 | {
727 | "name": "creatorsFee",
728 | "type": "u64",
729 | "index": false
730 | },
731 | {
732 | "name": "expiry",
733 | "type": "i64",
734 | "index": false
735 | },
736 | {
737 | "name": "mint",
738 | "type": "publicKey",
739 | "index": false
740 | },
741 | {
742 | "name": "bidder",
743 | "type": "publicKey",
744 | "index": false
745 | }
746 | ]
747 | }
748 | ],
749 | "errors": [
750 | {
751 | "code": 6000,
752 | "name": "BadMargin",
753 | "msg": "bad margin account passed in"
754 | },
755 | {
756 | "code": 6001,
757 | "name": "ExpiryTooLarge",
758 | "msg": "expiry date too far in the future, max expiry 60d"
759 | },
760 | {
761 | "code": 6002,
762 | "name": "PriceMismatch",
763 | "msg": "passed in amount doesnt match that stored"
764 | },
765 | {
766 | "code": 6003,
767 | "name": "BidExpired",
768 | "msg": "bid expired"
769 | },
770 | {
771 | "code": 6004,
772 | "name": "BidNotYetExpired",
773 | "msg": "bid hasn't reached expiry time yet"
774 | }
775 | ]
776 | };
777 |
778 | export const IDL: TensorBid = {
779 | "version": "1.0.0",
780 | "name": "tensor_bid",
781 | "constants": [
782 | {
783 | "name": "CURRENT_TBID_VERSION",
784 | "type": "u8",
785 | "value": "1"
786 | },
787 | {
788 | "name": "TBID_TAKER_FEE_BPS",
789 | "type": "u16",
790 | "value": "150"
791 | },
792 | {
793 | "name": "MAX_EXPIRY_SEC",
794 | "type": "i64",
795 | "value": "31536000"
796 | },
797 | {
798 | "name": "BID_STATE_SIZE",
799 | "type": {
800 | "defined": "usize"
801 | },
802 | "value": "8 + 1 + 8 + (32 * 2) + 1 + 8 + 33 + 8 + 56"
803 | }
804 | ],
805 | "instructions": [
806 | {
807 | "name": "bid",
808 | "accounts": [
809 | {
810 | "name": "nftMint",
811 | "isMut": false,
812 | "isSigner": false
813 | },
814 | {
815 | "name": "bidState",
816 | "isMut": true,
817 | "isSigner": false
818 | },
819 | {
820 | "name": "bidder",
821 | "isMut": true,
822 | "isSigner": true
823 | },
824 | {
825 | "name": "systemProgram",
826 | "isMut": false,
827 | "isSigner": false
828 | },
829 | {
830 | "name": "rent",
831 | "isMut": false,
832 | "isSigner": false
833 | },
834 | {
835 | "name": "tswap",
836 | "isMut": false,
837 | "isSigner": false
838 | },
839 | {
840 | "name": "marginAccount",
841 | "isMut": true,
842 | "isSigner": false
843 | }
844 | ],
845 | "args": [
846 | {
847 | "name": "lamports",
848 | "type": "u64"
849 | },
850 | {
851 | "name": "expireInSec",
852 | "type": {
853 | "option": "u64"
854 | }
855 | }
856 | ]
857 | },
858 | {
859 | "name": "takeBid",
860 | "accounts": [
861 | {
862 | "name": "tswap",
863 | "isMut": false,
864 | "isSigner": false
865 | },
866 | {
867 | "name": "feeVault",
868 | "isMut": true,
869 | "isSigner": false
870 | },
871 | {
872 | "name": "nftMint",
873 | "isMut": false,
874 | "isSigner": false
875 | },
876 | {
877 | "name": "nftBidderAcc",
878 | "isMut": true,
879 | "isSigner": false
880 | },
881 | {
882 | "name": "nftSellerAcc",
883 | "isMut": true,
884 | "isSigner": false
885 | },
886 | {
887 | "name": "nftTempAcc",
888 | "isMut": true,
889 | "isSigner": false
890 | },
891 | {
892 | "name": "nftMetadata",
893 | "isMut": true,
894 | "isSigner": false
895 | },
896 | {
897 | "name": "bidState",
898 | "isMut": true,
899 | "isSigner": false
900 | },
901 | {
902 | "name": "bidder",
903 | "isMut": true,
904 | "isSigner": false
905 | },
906 | {
907 | "name": "seller",
908 | "isMut": true,
909 | "isSigner": true
910 | },
911 | {
912 | "name": "tokenProgram",
913 | "isMut": false,
914 | "isSigner": false
915 | },
916 | {
917 | "name": "associatedTokenProgram",
918 | "isMut": false,
919 | "isSigner": false
920 | },
921 | {
922 | "name": "systemProgram",
923 | "isMut": false,
924 | "isSigner": false
925 | },
926 | {
927 | "name": "rent",
928 | "isMut": false,
929 | "isSigner": false
930 | },
931 | {
932 | "name": "nftEdition",
933 | "isMut": false,
934 | "isSigner": false
935 | },
936 | {
937 | "name": "sellerTokenRecord",
938 | "isMut": true,
939 | "isSigner": false
940 | },
941 | {
942 | "name": "bidderTokenRecord",
943 | "isMut": true,
944 | "isSigner": false
945 | },
946 | {
947 | "name": "tempTokenRecord",
948 | "isMut": true,
949 | "isSigner": false
950 | },
951 | {
952 | "name": "pnftShared",
953 | "accounts": [
954 | {
955 | "name": "tokenMetadataProgram",
956 | "isMut": false,
957 | "isSigner": false
958 | },
959 | {
960 | "name": "instructions",
961 | "isMut": false,
962 | "isSigner": false
963 | },
964 | {
965 | "name": "authorizationRulesProgram",
966 | "isMut": false,
967 | "isSigner": false
968 | }
969 | ]
970 | },
971 | {
972 | "name": "tensorswapProgram",
973 | "isMut": false,
974 | "isSigner": false
975 | },
976 | {
977 | "name": "authRules",
978 | "isMut": false,
979 | "isSigner": false
980 | },
981 | {
982 | "name": "marginAccount",
983 | "isMut": true,
984 | "isSigner": false
985 | },
986 | {
987 | "name": "takerBroker",
988 | "isMut": true,
989 | "isSigner": false
990 | }
991 | ],
992 | "args": [
993 | {
994 | "name": "lamports",
995 | "type": "u64"
996 | },
997 | {
998 | "name": "rulesAccPresent",
999 | "type": "bool"
1000 | },
1001 | {
1002 | "name": "authorizationData",
1003 | "type": {
1004 | "option": {
1005 | "defined": "AuthorizationDataLocal"
1006 | }
1007 | }
1008 | },
1009 | {
1010 | "name": "optionalRoyaltyPct",
1011 | "type": {
1012 | "option": "u16"
1013 | }
1014 | }
1015 | ]
1016 | },
1017 | {
1018 | "name": "takeBidT22",
1019 | "accounts": [
1020 | {
1021 | "name": "tswap",
1022 | "isMut": false,
1023 | "isSigner": false
1024 | },
1025 | {
1026 | "name": "feeVault",
1027 | "isMut": true,
1028 | "isSigner": false
1029 | },
1030 | {
1031 | "name": "nftMint",
1032 | "isMut": false,
1033 | "isSigner": false
1034 | },
1035 | {
1036 | "name": "nftBidderAcc",
1037 | "isMut": true,
1038 | "isSigner": false
1039 | },
1040 | {
1041 | "name": "nftSellerAcc",
1042 | "isMut": true,
1043 | "isSigner": false
1044 | },
1045 | {
1046 | "name": "bidState",
1047 | "isMut": true,
1048 | "isSigner": false
1049 | },
1050 | {
1051 | "name": "bidder",
1052 | "isMut": true,
1053 | "isSigner": false
1054 | },
1055 | {
1056 | "name": "seller",
1057 | "isMut": true,
1058 | "isSigner": true
1059 | },
1060 | {
1061 | "name": "tokenProgram",
1062 | "isMut": false,
1063 | "isSigner": false
1064 | },
1065 | {
1066 | "name": "associatedTokenProgram",
1067 | "isMut": false,
1068 | "isSigner": false
1069 | },
1070 | {
1071 | "name": "systemProgram",
1072 | "isMut": false,
1073 | "isSigner": false
1074 | },
1075 | {
1076 | "name": "rent",
1077 | "isMut": false,
1078 | "isSigner": false
1079 | },
1080 | {
1081 | "name": "tensorswapProgram",
1082 | "isMut": false,
1083 | "isSigner": false
1084 | },
1085 | {
1086 | "name": "marginAccount",
1087 | "isMut": true,
1088 | "isSigner": false
1089 | },
1090 | {
1091 | "name": "takerBroker",
1092 | "isMut": true,
1093 | "isSigner": false
1094 | }
1095 | ],
1096 | "args": [
1097 | {
1098 | "name": "lamports",
1099 | "type": "u64"
1100 | }
1101 | ]
1102 | },
1103 | {
1104 | "name": "wnsTakeBid",
1105 | "accounts": [
1106 | {
1107 | "name": "tswap",
1108 | "isMut": false,
1109 | "isSigner": false
1110 | },
1111 | {
1112 | "name": "feeVault",
1113 | "isMut": true,
1114 | "isSigner": false
1115 | },
1116 | {
1117 | "name": "nftMint",
1118 | "isMut": false,
1119 | "isSigner": false
1120 | },
1121 | {
1122 | "name": "nftBidderAcc",
1123 | "isMut": true,
1124 | "isSigner": false
1125 | },
1126 | {
1127 | "name": "nftSellerAcc",
1128 | "isMut": true,
1129 | "isSigner": false
1130 | },
1131 | {
1132 | "name": "bidState",
1133 | "isMut": true,
1134 | "isSigner": false
1135 | },
1136 | {
1137 | "name": "bidder",
1138 | "isMut": true,
1139 | "isSigner": false
1140 | },
1141 | {
1142 | "name": "seller",
1143 | "isMut": true,
1144 | "isSigner": true
1145 | },
1146 | {
1147 | "name": "tokenProgram",
1148 | "isMut": false,
1149 | "isSigner": false
1150 | },
1151 | {
1152 | "name": "associatedTokenProgram",
1153 | "isMut": false,
1154 | "isSigner": false
1155 | },
1156 | {
1157 | "name": "systemProgram",
1158 | "isMut": false,
1159 | "isSigner": false
1160 | },
1161 | {
1162 | "name": "rent",
1163 | "isMut": false,
1164 | "isSigner": false
1165 | },
1166 | {
1167 | "name": "tensorswapProgram",
1168 | "isMut": false,
1169 | "isSigner": false
1170 | },
1171 | {
1172 | "name": "marginAccount",
1173 | "isMut": true,
1174 | "isSigner": false
1175 | },
1176 | {
1177 | "name": "takerBroker",
1178 | "isMut": true,
1179 | "isSigner": false
1180 | },
1181 | {
1182 | "name": "approveAccount",
1183 | "isMut": true,
1184 | "isSigner": false
1185 | },
1186 | {
1187 | "name": "distribution",
1188 | "isMut": true,
1189 | "isSigner": false
1190 | },
1191 | {
1192 | "name": "wnsProgram",
1193 | "isMut": false,
1194 | "isSigner": false
1195 | },
1196 | {
1197 | "name": "distributionProgram",
1198 | "isMut": false,
1199 | "isSigner": false
1200 | },
1201 | {
1202 | "name": "extraMetas",
1203 | "isMut": false,
1204 | "isSigner": false
1205 | }
1206 | ],
1207 | "args": [
1208 | {
1209 | "name": "lamports",
1210 | "type": "u64"
1211 | }
1212 | ]
1213 | },
1214 | {
1215 | "name": "cancelBid",
1216 | "accounts": [
1217 | {
1218 | "name": "nftMint",
1219 | "isMut": false,
1220 | "isSigner": false
1221 | },
1222 | {
1223 | "name": "bidState",
1224 | "isMut": true,
1225 | "isSigner": false
1226 | },
1227 | {
1228 | "name": "bidder",
1229 | "isMut": true,
1230 | "isSigner": true
1231 | },
1232 | {
1233 | "name": "systemProgram",
1234 | "isMut": false,
1235 | "isSigner": false
1236 | },
1237 | {
1238 | "name": "rent",
1239 | "isMut": false,
1240 | "isSigner": false
1241 | }
1242 | ],
1243 | "args": []
1244 | },
1245 | {
1246 | "name": "closeExpiredBid",
1247 | "accounts": [
1248 | {
1249 | "name": "nftMint",
1250 | "isMut": false,
1251 | "isSigner": false
1252 | },
1253 | {
1254 | "name": "bidState",
1255 | "isMut": true,
1256 | "isSigner": false
1257 | },
1258 | {
1259 | "name": "bidder",
1260 | "isMut": true,
1261 | "isSigner": false
1262 | },
1263 | {
1264 | "name": "systemProgram",
1265 | "isMut": false,
1266 | "isSigner": false
1267 | },
1268 | {
1269 | "name": "rent",
1270 | "isMut": false,
1271 | "isSigner": false
1272 | }
1273 | ],
1274 | "args": []
1275 | }
1276 | ],
1277 | "accounts": [
1278 | {
1279 | "name": "bidState",
1280 | "type": {
1281 | "kind": "struct",
1282 | "fields": [
1283 | {
1284 | "name": "version",
1285 | "type": "u8"
1286 | },
1287 | {
1288 | "name": "bidAmount",
1289 | "type": "u64"
1290 | },
1291 | {
1292 | "name": "nftMint",
1293 | "type": "publicKey"
1294 | },
1295 | {
1296 | "name": "bidder",
1297 | "type": "publicKey"
1298 | },
1299 | {
1300 | "name": "bump",
1301 | "type": {
1302 | "array": [
1303 | "u8",
1304 | 1
1305 | ]
1306 | }
1307 | },
1308 | {
1309 | "name": "expiry",
1310 | "type": "i64"
1311 | },
1312 | {
1313 | "name": "margin",
1314 | "type": {
1315 | "option": "publicKey"
1316 | }
1317 | },
1318 | {
1319 | "name": "updatedAt",
1320 | "type": "i64"
1321 | },
1322 | {
1323 | "name": "reserved",
1324 | "type": {
1325 | "array": [
1326 | "u8",
1327 | 8
1328 | ]
1329 | }
1330 | },
1331 | {
1332 | "name": "reserved1",
1333 | "type": {
1334 | "array": [
1335 | "u8",
1336 | 16
1337 | ]
1338 | }
1339 | },
1340 | {
1341 | "name": "reserved2",
1342 | "type": {
1343 | "array": [
1344 | "u8",
1345 | 32
1346 | ]
1347 | }
1348 | }
1349 | ]
1350 | }
1351 | }
1352 | ],
1353 | "types": [
1354 | {
1355 | "name": "AuthorizationDataLocal",
1356 | "type": {
1357 | "kind": "struct",
1358 | "fields": [
1359 | {
1360 | "name": "payload",
1361 | "type": {
1362 | "vec": {
1363 | "defined": "TaggedPayload"
1364 | }
1365 | }
1366 | }
1367 | ]
1368 | }
1369 | },
1370 | {
1371 | "name": "TaggedPayload",
1372 | "type": {
1373 | "kind": "struct",
1374 | "fields": [
1375 | {
1376 | "name": "name",
1377 | "type": "string"
1378 | },
1379 | {
1380 | "name": "payload",
1381 | "type": {
1382 | "defined": "PayloadTypeLocal"
1383 | }
1384 | }
1385 | ]
1386 | }
1387 | },
1388 | {
1389 | "name": "SeedsVecLocal",
1390 | "type": {
1391 | "kind": "struct",
1392 | "fields": [
1393 | {
1394 | "name": "seeds",
1395 | "docs": [
1396 | "The vector of derivation seeds."
1397 | ],
1398 | "type": {
1399 | "vec": "bytes"
1400 | }
1401 | }
1402 | ]
1403 | }
1404 | },
1405 | {
1406 | "name": "ProofInfoLocal",
1407 | "type": {
1408 | "kind": "struct",
1409 | "fields": [
1410 | {
1411 | "name": "proof",
1412 | "docs": [
1413 | "The merkle proof."
1414 | ],
1415 | "type": {
1416 | "vec": {
1417 | "array": [
1418 | "u8",
1419 | 32
1420 | ]
1421 | }
1422 | }
1423 | }
1424 | ]
1425 | }
1426 | },
1427 | {
1428 | "name": "PayloadTypeLocal",
1429 | "type": {
1430 | "kind": "enum",
1431 | "variants": [
1432 | {
1433 | "name": "Pubkey",
1434 | "fields": [
1435 | "publicKey"
1436 | ]
1437 | },
1438 | {
1439 | "name": "Seeds",
1440 | "fields": [
1441 | {
1442 | "defined": "SeedsVecLocal"
1443 | }
1444 | ]
1445 | },
1446 | {
1447 | "name": "MerkleProof",
1448 | "fields": [
1449 | {
1450 | "defined": "ProofInfoLocal"
1451 | }
1452 | ]
1453 | },
1454 | {
1455 | "name": "Number",
1456 | "fields": [
1457 | "u64"
1458 | ]
1459 | }
1460 | ]
1461 | }
1462 | }
1463 | ],
1464 | "events": [
1465 | {
1466 | "name": "BidEvent",
1467 | "fields": [
1468 | {
1469 | "name": "lamports",
1470 | "type": "u64",
1471 | "index": false
1472 | },
1473 | {
1474 | "name": "expiry",
1475 | "type": "i64",
1476 | "index": false
1477 | },
1478 | {
1479 | "name": "mint",
1480 | "type": "publicKey",
1481 | "index": false
1482 | },
1483 | {
1484 | "name": "bidder",
1485 | "type": "publicKey",
1486 | "index": false
1487 | }
1488 | ]
1489 | },
1490 | {
1491 | "name": "TakeBidEvent",
1492 | "fields": [
1493 | {
1494 | "name": "lamports",
1495 | "type": "u64",
1496 | "index": false
1497 | },
1498 | {
1499 | "name": "tswapFee",
1500 | "type": "u64",
1501 | "index": false
1502 | },
1503 | {
1504 | "name": "creatorsFee",
1505 | "type": "u64",
1506 | "index": false
1507 | },
1508 | {
1509 | "name": "expiry",
1510 | "type": "i64",
1511 | "index": false
1512 | },
1513 | {
1514 | "name": "mint",
1515 | "type": "publicKey",
1516 | "index": false
1517 | },
1518 | {
1519 | "name": "bidder",
1520 | "type": "publicKey",
1521 | "index": false
1522 | }
1523 | ]
1524 | }
1525 | ],
1526 | "errors": [
1527 | {
1528 | "code": 6000,
1529 | "name": "BadMargin",
1530 | "msg": "bad margin account passed in"
1531 | },
1532 | {
1533 | "code": 6001,
1534 | "name": "ExpiryTooLarge",
1535 | "msg": "expiry date too far in the future, max expiry 60d"
1536 | },
1537 | {
1538 | "code": 6002,
1539 | "name": "PriceMismatch",
1540 | "msg": "passed in amount doesnt match that stored"
1541 | },
1542 | {
1543 | "code": 6003,
1544 | "name": "BidExpired",
1545 | "msg": "bid expired"
1546 | },
1547 | {
1548 | "code": 6004,
1549 | "name": "BidNotYetExpired",
1550 | "msg": "bid hasn't reached expiry time yet"
1551 | }
1552 | ]
1553 | };
1554 |
--------------------------------------------------------------------------------
/src/tensor_bid/idl/tensor_bid_v0_1_0.ts:
--------------------------------------------------------------------------------
1 | export type TensorBid = {
2 | version: "0.1.0";
3 | name: "tensor_bid";
4 | constants: [
5 | {
6 | name: "CURRENT_TBID_VERSION";
7 | type: "u8";
8 | value: "1";
9 | },
10 | {
11 | name: "TBID_FEE_BPS";
12 | type: "u16";
13 | value: "100";
14 | },
15 | {
16 | name: "MAX_EXPIRY_SEC";
17 | type: "i64";
18 | value: "5184000";
19 | },
20 | {
21 | name: "BID_STATE_SIZE";
22 | type: {
23 | defined: "usize";
24 | };
25 | value: "8 + 1 + 8 + (32 * 2) + 1 + 8 + 33 + 64";
26 | }
27 | ];
28 | instructions: [
29 | {
30 | name: "bid";
31 | accounts: [
32 | {
33 | name: "nftMint";
34 | isMut: false;
35 | isSigner: false;
36 | },
37 | {
38 | name: "bidState";
39 | isMut: true;
40 | isSigner: false;
41 | },
42 | {
43 | name: "bidder";
44 | isMut: true;
45 | isSigner: true;
46 | },
47 | {
48 | name: "systemProgram";
49 | isMut: false;
50 | isSigner: false;
51 | },
52 | {
53 | name: "rent";
54 | isMut: false;
55 | isSigner: false;
56 | },
57 | {
58 | name: "tswap";
59 | isMut: false;
60 | isSigner: false;
61 | },
62 | {
63 | name: "marginAccount";
64 | isMut: true;
65 | isSigner: false;
66 | }
67 | ];
68 | args: [
69 | {
70 | name: "lamports";
71 | type: "u64";
72 | },
73 | {
74 | name: "expireInSec";
75 | type: {
76 | option: "u64";
77 | };
78 | },
79 | {
80 | name: "fundMargin";
81 | type: "bool";
82 | }
83 | ];
84 | },
85 | {
86 | name: "takeBid";
87 | accounts: [
88 | {
89 | name: "tswap";
90 | isMut: false;
91 | isSigner: false;
92 | },
93 | {
94 | name: "feeVault";
95 | isMut: true;
96 | isSigner: false;
97 | },
98 | {
99 | name: "nftMint";
100 | isMut: false;
101 | isSigner: false;
102 | },
103 | {
104 | name: "nftBidderAcc";
105 | isMut: true;
106 | isSigner: false;
107 | },
108 | {
109 | name: "nftSellerAcc";
110 | isMut: true;
111 | isSigner: false;
112 | },
113 | {
114 | name: "nftTempAcc";
115 | isMut: true;
116 | isSigner: false;
117 | },
118 | {
119 | name: "nftMetadata";
120 | isMut: true;
121 | isSigner: false;
122 | },
123 | {
124 | name: "bidState";
125 | isMut: true;
126 | isSigner: false;
127 | },
128 | {
129 | name: "bidder";
130 | isMut: true;
131 | isSigner: false;
132 | },
133 | {
134 | name: "seller";
135 | isMut: true;
136 | isSigner: true;
137 | },
138 | {
139 | name: "tokenProgram";
140 | isMut: false;
141 | isSigner: false;
142 | },
143 | {
144 | name: "associatedTokenProgram";
145 | isMut: false;
146 | isSigner: false;
147 | },
148 | {
149 | name: "systemProgram";
150 | isMut: false;
151 | isSigner: false;
152 | },
153 | {
154 | name: "rent";
155 | isMut: false;
156 | isSigner: false;
157 | },
158 | {
159 | name: "nftEdition";
160 | isMut: false;
161 | isSigner: false;
162 | },
163 | {
164 | name: "sellerTokenRecord";
165 | isMut: true;
166 | isSigner: false;
167 | },
168 | {
169 | name: "bidderTokenRecord";
170 | isMut: true;
171 | isSigner: false;
172 | },
173 | {
174 | name: "tempTokenRecord";
175 | isMut: true;
176 | isSigner: false;
177 | },
178 | {
179 | name: "pnftShared";
180 | accounts: [
181 | {
182 | name: "tokenMetadataProgram";
183 | isMut: false;
184 | isSigner: false;
185 | },
186 | {
187 | name: "instructions";
188 | isMut: false;
189 | isSigner: false;
190 | },
191 | {
192 | name: "authorizationRulesProgram";
193 | isMut: false;
194 | isSigner: false;
195 | }
196 | ];
197 | },
198 | {
199 | name: "tensorswapProgram";
200 | isMut: false;
201 | isSigner: false;
202 | },
203 | {
204 | name: "authRules";
205 | isMut: false;
206 | isSigner: false;
207 | },
208 | {
209 | name: "marginAccount";
210 | isMut: true;
211 | isSigner: false;
212 | },
213 | {
214 | name: "takerBroker";
215 | isMut: true;
216 | isSigner: false;
217 | }
218 | ];
219 | args: [
220 | {
221 | name: "lamports";
222 | type: "u64";
223 | },
224 | {
225 | name: "rulesAccPresent";
226 | type: "bool";
227 | },
228 | {
229 | name: "authorizationData";
230 | type: {
231 | option: {
232 | defined: "AuthorizationDataLocal";
233 | };
234 | };
235 | },
236 | {
237 | name: "optionalRoyaltyPct";
238 | type: {
239 | option: "u16";
240 | };
241 | }
242 | ];
243 | },
244 | {
245 | name: "cancelBid";
246 | accounts: [
247 | {
248 | name: "nftMint";
249 | isMut: false;
250 | isSigner: false;
251 | },
252 | {
253 | name: "bidState";
254 | isMut: true;
255 | isSigner: false;
256 | },
257 | {
258 | name: "bidder";
259 | isMut: true;
260 | isSigner: true;
261 | },
262 | {
263 | name: "systemProgram";
264 | isMut: false;
265 | isSigner: false;
266 | },
267 | {
268 | name: "rent";
269 | isMut: false;
270 | isSigner: false;
271 | }
272 | ];
273 | args: [];
274 | },
275 | {
276 | name: "closeExpiredBid";
277 | accounts: [
278 | {
279 | name: "nftMint";
280 | isMut: false;
281 | isSigner: false;
282 | },
283 | {
284 | name: "bidState";
285 | isMut: true;
286 | isSigner: false;
287 | },
288 | {
289 | name: "bidder";
290 | isMut: true;
291 | isSigner: false;
292 | },
293 | {
294 | name: "tswap";
295 | isMut: false;
296 | isSigner: false;
297 | },
298 | {
299 | name: "cosigner";
300 | isMut: false;
301 | isSigner: true;
302 | },
303 | {
304 | name: "systemProgram";
305 | isMut: false;
306 | isSigner: false;
307 | },
308 | {
309 | name: "rent";
310 | isMut: false;
311 | isSigner: false;
312 | }
313 | ];
314 | args: [];
315 | }
316 | ];
317 | accounts: [
318 | {
319 | name: "bidState";
320 | type: {
321 | kind: "struct";
322 | fields: [
323 | {
324 | name: "version";
325 | type: "u8";
326 | },
327 | {
328 | name: "bidAmount";
329 | type: "u64";
330 | },
331 | {
332 | name: "nftMint";
333 | type: "publicKey";
334 | },
335 | {
336 | name: "bidder";
337 | type: "publicKey";
338 | },
339 | {
340 | name: "bump";
341 | type: {
342 | array: ["u8", 1];
343 | };
344 | },
345 | {
346 | name: "expiry";
347 | type: "i64";
348 | },
349 | {
350 | name: "margin";
351 | type: {
352 | option: "publicKey";
353 | };
354 | },
355 | {
356 | name: "reserved";
357 | type: {
358 | array: ["u8", 64];
359 | };
360 | }
361 | ];
362 | };
363 | }
364 | ];
365 | types: [
366 | {
367 | name: "AuthorizationDataLocal";
368 | type: {
369 | kind: "struct";
370 | fields: [
371 | {
372 | name: "payload";
373 | type: {
374 | vec: {
375 | defined: "TaggedPayload";
376 | };
377 | };
378 | }
379 | ];
380 | };
381 | },
382 | {
383 | name: "TaggedPayload";
384 | type: {
385 | kind: "struct";
386 | fields: [
387 | {
388 | name: "name";
389 | type: "string";
390 | },
391 | {
392 | name: "payload";
393 | type: {
394 | defined: "PayloadTypeLocal";
395 | };
396 | }
397 | ];
398 | };
399 | },
400 | {
401 | name: "SeedsVecLocal";
402 | type: {
403 | kind: "struct";
404 | fields: [
405 | {
406 | name: "seeds";
407 | docs: ["The vector of derivation seeds."];
408 | type: {
409 | vec: "bytes";
410 | };
411 | }
412 | ];
413 | };
414 | },
415 | {
416 | name: "ProofInfoLocal";
417 | type: {
418 | kind: "struct";
419 | fields: [
420 | {
421 | name: "proof";
422 | docs: ["The merkle proof."];
423 | type: {
424 | vec: {
425 | array: ["u8", 32];
426 | };
427 | };
428 | }
429 | ];
430 | };
431 | },
432 | {
433 | name: "PayloadTypeLocal";
434 | type: {
435 | kind: "enum";
436 | variants: [
437 | {
438 | name: "Pubkey";
439 | fields: ["publicKey"];
440 | },
441 | {
442 | name: "Seeds";
443 | fields: [
444 | {
445 | defined: "SeedsVecLocal";
446 | }
447 | ];
448 | },
449 | {
450 | name: "MerkleProof";
451 | fields: [
452 | {
453 | defined: "ProofInfoLocal";
454 | }
455 | ];
456 | },
457 | {
458 | name: "Number";
459 | fields: ["u64"];
460 | }
461 | ];
462 | };
463 | }
464 | ];
465 | events: [
466 | {
467 | name: "BidEvent";
468 | fields: [
469 | {
470 | name: "lamports";
471 | type: "u64";
472 | index: false;
473 | },
474 | {
475 | name: "expiry";
476 | type: "i64";
477 | index: false;
478 | },
479 | {
480 | name: "mint";
481 | type: "publicKey";
482 | index: false;
483 | },
484 | {
485 | name: "bidder";
486 | type: "publicKey";
487 | index: false;
488 | }
489 | ];
490 | },
491 | {
492 | name: "TakeBidEvent";
493 | fields: [
494 | {
495 | name: "lamports";
496 | type: "u64";
497 | index: false;
498 | },
499 | {
500 | name: "tswapFee";
501 | type: "u64";
502 | index: false;
503 | },
504 | {
505 | name: "creatorsFee";
506 | type: "u64";
507 | index: false;
508 | },
509 | {
510 | name: "expiry";
511 | type: "i64";
512 | index: false;
513 | },
514 | {
515 | name: "mint";
516 | type: "publicKey";
517 | index: false;
518 | },
519 | {
520 | name: "bidder";
521 | type: "publicKey";
522 | index: false;
523 | }
524 | ];
525 | }
526 | ];
527 | errors: [
528 | {
529 | code: 6000;
530 | name: "BadMargin";
531 | msg: "bad margin account passed in";
532 | },
533 | {
534 | code: 6001;
535 | name: "ExpiryTooLarge";
536 | msg: "expiry date too far in the future, max expiry 60d";
537 | },
538 | {
539 | code: 6002;
540 | name: "PriceMismatch";
541 | msg: "passed in amount doesnt match that stored";
542 | },
543 | {
544 | code: 6003;
545 | name: "BidExpired";
546 | msg: "bid expired";
547 | },
548 | {
549 | code: 6004;
550 | name: "BidNotYetExpired";
551 | msg: "bid hasn't reached expiry time yet";
552 | }
553 | ];
554 | };
555 |
556 | export const IDL: TensorBid = {
557 | version: "0.1.0",
558 | name: "tensor_bid",
559 | constants: [
560 | {
561 | name: "CURRENT_TBID_VERSION",
562 | type: "u8",
563 | value: "1",
564 | },
565 | {
566 | name: "TBID_FEE_BPS",
567 | type: "u16",
568 | value: "100",
569 | },
570 | {
571 | name: "MAX_EXPIRY_SEC",
572 | type: "i64",
573 | value: "5184000",
574 | },
575 | {
576 | name: "BID_STATE_SIZE",
577 | type: {
578 | defined: "usize",
579 | },
580 | value: "8 + 1 + 8 + (32 * 2) + 1 + 8 + 33 + 64",
581 | },
582 | ],
583 | instructions: [
584 | {
585 | name: "bid",
586 | accounts: [
587 | {
588 | name: "nftMint",
589 | isMut: false,
590 | isSigner: false,
591 | },
592 | {
593 | name: "bidState",
594 | isMut: true,
595 | isSigner: false,
596 | },
597 | {
598 | name: "bidder",
599 | isMut: true,
600 | isSigner: true,
601 | },
602 | {
603 | name: "systemProgram",
604 | isMut: false,
605 | isSigner: false,
606 | },
607 | {
608 | name: "rent",
609 | isMut: false,
610 | isSigner: false,
611 | },
612 | {
613 | name: "tswap",
614 | isMut: false,
615 | isSigner: false,
616 | },
617 | {
618 | name: "marginAccount",
619 | isMut: true,
620 | isSigner: false,
621 | },
622 | ],
623 | args: [
624 | {
625 | name: "lamports",
626 | type: "u64",
627 | },
628 | {
629 | name: "expireInSec",
630 | type: {
631 | option: "u64",
632 | },
633 | },
634 | {
635 | name: "fundMargin",
636 | type: "bool",
637 | },
638 | ],
639 | },
640 | {
641 | name: "takeBid",
642 | accounts: [
643 | {
644 | name: "tswap",
645 | isMut: false,
646 | isSigner: false,
647 | },
648 | {
649 | name: "feeVault",
650 | isMut: true,
651 | isSigner: false,
652 | },
653 | {
654 | name: "nftMint",
655 | isMut: false,
656 | isSigner: false,
657 | },
658 | {
659 | name: "nftBidderAcc",
660 | isMut: true,
661 | isSigner: false,
662 | },
663 | {
664 | name: "nftSellerAcc",
665 | isMut: true,
666 | isSigner: false,
667 | },
668 | {
669 | name: "nftTempAcc",
670 | isMut: true,
671 | isSigner: false,
672 | },
673 | {
674 | name: "nftMetadata",
675 | isMut: true,
676 | isSigner: false,
677 | },
678 | {
679 | name: "bidState",
680 | isMut: true,
681 | isSigner: false,
682 | },
683 | {
684 | name: "bidder",
685 | isMut: true,
686 | isSigner: false,
687 | },
688 | {
689 | name: "seller",
690 | isMut: true,
691 | isSigner: true,
692 | },
693 | {
694 | name: "tokenProgram",
695 | isMut: false,
696 | isSigner: false,
697 | },
698 | {
699 | name: "associatedTokenProgram",
700 | isMut: false,
701 | isSigner: false,
702 | },
703 | {
704 | name: "systemProgram",
705 | isMut: false,
706 | isSigner: false,
707 | },
708 | {
709 | name: "rent",
710 | isMut: false,
711 | isSigner: false,
712 | },
713 | {
714 | name: "nftEdition",
715 | isMut: false,
716 | isSigner: false,
717 | },
718 | {
719 | name: "sellerTokenRecord",
720 | isMut: true,
721 | isSigner: false,
722 | },
723 | {
724 | name: "bidderTokenRecord",
725 | isMut: true,
726 | isSigner: false,
727 | },
728 | {
729 | name: "tempTokenRecord",
730 | isMut: true,
731 | isSigner: false,
732 | },
733 | {
734 | name: "pnftShared",
735 | accounts: [
736 | {
737 | name: "tokenMetadataProgram",
738 | isMut: false,
739 | isSigner: false,
740 | },
741 | {
742 | name: "instructions",
743 | isMut: false,
744 | isSigner: false,
745 | },
746 | {
747 | name: "authorizationRulesProgram",
748 | isMut: false,
749 | isSigner: false,
750 | },
751 | ],
752 | },
753 | {
754 | name: "tensorswapProgram",
755 | isMut: false,
756 | isSigner: false,
757 | },
758 | {
759 | name: "authRules",
760 | isMut: false,
761 | isSigner: false,
762 | },
763 | {
764 | name: "marginAccount",
765 | isMut: true,
766 | isSigner: false,
767 | },
768 | {
769 | name: "takerBroker",
770 | isMut: true,
771 | isSigner: false,
772 | },
773 | ],
774 | args: [
775 | {
776 | name: "lamports",
777 | type: "u64",
778 | },
779 | {
780 | name: "rulesAccPresent",
781 | type: "bool",
782 | },
783 | {
784 | name: "authorizationData",
785 | type: {
786 | option: {
787 | defined: "AuthorizationDataLocal",
788 | },
789 | },
790 | },
791 | {
792 | name: "optionalRoyaltyPct",
793 | type: {
794 | option: "u16",
795 | },
796 | },
797 | ],
798 | },
799 | {
800 | name: "cancelBid",
801 | accounts: [
802 | {
803 | name: "nftMint",
804 | isMut: false,
805 | isSigner: false,
806 | },
807 | {
808 | name: "bidState",
809 | isMut: true,
810 | isSigner: false,
811 | },
812 | {
813 | name: "bidder",
814 | isMut: true,
815 | isSigner: true,
816 | },
817 | {
818 | name: "systemProgram",
819 | isMut: false,
820 | isSigner: false,
821 | },
822 | {
823 | name: "rent",
824 | isMut: false,
825 | isSigner: false,
826 | },
827 | ],
828 | args: [],
829 | },
830 | {
831 | name: "closeExpiredBid",
832 | accounts: [
833 | {
834 | name: "nftMint",
835 | isMut: false,
836 | isSigner: false,
837 | },
838 | {
839 | name: "bidState",
840 | isMut: true,
841 | isSigner: false,
842 | },
843 | {
844 | name: "bidder",
845 | isMut: true,
846 | isSigner: false,
847 | },
848 | {
849 | name: "tswap",
850 | isMut: false,
851 | isSigner: false,
852 | },
853 | {
854 | name: "cosigner",
855 | isMut: false,
856 | isSigner: true,
857 | },
858 | {
859 | name: "systemProgram",
860 | isMut: false,
861 | isSigner: false,
862 | },
863 | {
864 | name: "rent",
865 | isMut: false,
866 | isSigner: false,
867 | },
868 | ],
869 | args: [],
870 | },
871 | ],
872 | accounts: [
873 | {
874 | name: "bidState",
875 | type: {
876 | kind: "struct",
877 | fields: [
878 | {
879 | name: "version",
880 | type: "u8",
881 | },
882 | {
883 | name: "bidAmount",
884 | type: "u64",
885 | },
886 | {
887 | name: "nftMint",
888 | type: "publicKey",
889 | },
890 | {
891 | name: "bidder",
892 | type: "publicKey",
893 | },
894 | {
895 | name: "bump",
896 | type: {
897 | array: ["u8", 1],
898 | },
899 | },
900 | {
901 | name: "expiry",
902 | type: "i64",
903 | },
904 | {
905 | name: "margin",
906 | type: {
907 | option: "publicKey",
908 | },
909 | },
910 | {
911 | name: "reserved",
912 | type: {
913 | array: ["u8", 64],
914 | },
915 | },
916 | ],
917 | },
918 | },
919 | ],
920 | types: [
921 | {
922 | name: "AuthorizationDataLocal",
923 | type: {
924 | kind: "struct",
925 | fields: [
926 | {
927 | name: "payload",
928 | type: {
929 | vec: {
930 | defined: "TaggedPayload",
931 | },
932 | },
933 | },
934 | ],
935 | },
936 | },
937 | {
938 | name: "TaggedPayload",
939 | type: {
940 | kind: "struct",
941 | fields: [
942 | {
943 | name: "name",
944 | type: "string",
945 | },
946 | {
947 | name: "payload",
948 | type: {
949 | defined: "PayloadTypeLocal",
950 | },
951 | },
952 | ],
953 | },
954 | },
955 | {
956 | name: "SeedsVecLocal",
957 | type: {
958 | kind: "struct",
959 | fields: [
960 | {
961 | name: "seeds",
962 | docs: ["The vector of derivation seeds."],
963 | type: {
964 | vec: "bytes",
965 | },
966 | },
967 | ],
968 | },
969 | },
970 | {
971 | name: "ProofInfoLocal",
972 | type: {
973 | kind: "struct",
974 | fields: [
975 | {
976 | name: "proof",
977 | docs: ["The merkle proof."],
978 | type: {
979 | vec: {
980 | array: ["u8", 32],
981 | },
982 | },
983 | },
984 | ],
985 | },
986 | },
987 | {
988 | name: "PayloadTypeLocal",
989 | type: {
990 | kind: "enum",
991 | variants: [
992 | {
993 | name: "Pubkey",
994 | fields: ["publicKey"],
995 | },
996 | {
997 | name: "Seeds",
998 | fields: [
999 | {
1000 | defined: "SeedsVecLocal",
1001 | },
1002 | ],
1003 | },
1004 | {
1005 | name: "MerkleProof",
1006 | fields: [
1007 | {
1008 | defined: "ProofInfoLocal",
1009 | },
1010 | ],
1011 | },
1012 | {
1013 | name: "Number",
1014 | fields: ["u64"],
1015 | },
1016 | ],
1017 | },
1018 | },
1019 | ],
1020 | events: [
1021 | {
1022 | name: "BidEvent",
1023 | fields: [
1024 | {
1025 | name: "lamports",
1026 | type: "u64",
1027 | index: false,
1028 | },
1029 | {
1030 | name: "expiry",
1031 | type: "i64",
1032 | index: false,
1033 | },
1034 | {
1035 | name: "mint",
1036 | type: "publicKey",
1037 | index: false,
1038 | },
1039 | {
1040 | name: "bidder",
1041 | type: "publicKey",
1042 | index: false,
1043 | },
1044 | ],
1045 | },
1046 | {
1047 | name: "TakeBidEvent",
1048 | fields: [
1049 | {
1050 | name: "lamports",
1051 | type: "u64",
1052 | index: false,
1053 | },
1054 | {
1055 | name: "tswapFee",
1056 | type: "u64",
1057 | index: false,
1058 | },
1059 | {
1060 | name: "creatorsFee",
1061 | type: "u64",
1062 | index: false,
1063 | },
1064 | {
1065 | name: "expiry",
1066 | type: "i64",
1067 | index: false,
1068 | },
1069 | {
1070 | name: "mint",
1071 | type: "publicKey",
1072 | index: false,
1073 | },
1074 | {
1075 | name: "bidder",
1076 | type: "publicKey",
1077 | index: false,
1078 | },
1079 | ],
1080 | },
1081 | ],
1082 | errors: [
1083 | {
1084 | code: 6000,
1085 | name: "BadMargin",
1086 | msg: "bad margin account passed in",
1087 | },
1088 | {
1089 | code: 6001,
1090 | name: "ExpiryTooLarge",
1091 | msg: "expiry date too far in the future, max expiry 60d",
1092 | },
1093 | {
1094 | code: 6002,
1095 | name: "PriceMismatch",
1096 | msg: "passed in amount doesnt match that stored",
1097 | },
1098 | {
1099 | code: 6003,
1100 | name: "BidExpired",
1101 | msg: "bid expired",
1102 | },
1103 | {
1104 | code: 6004,
1105 | name: "BidNotYetExpired",
1106 | msg: "bid hasn't reached expiry time yet",
1107 | },
1108 | ],
1109 | };
1110 |
--------------------------------------------------------------------------------
/src/tensor_bid/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./constants";
2 | export * from "./sdk";
3 | export * from "./pda";
4 |
--------------------------------------------------------------------------------
/src/tensor_bid/pda.ts:
--------------------------------------------------------------------------------
1 | import { PublicKey } from "@solana/web3.js";
2 | import { TBID_ADDR } from "./constants";
3 | import { TENSORSWAP_ADDR } from "../tensorswap";
4 |
5 | export const findBidStatePda = ({
6 | program,
7 | mint,
8 | owner,
9 | }: {
10 | program?: PublicKey;
11 | mint: PublicKey;
12 | owner: PublicKey;
13 | }) => {
14 | return PublicKey.findProgramAddressSync(
15 | [Buffer.from("bid_state"), owner.toBytes(), mint.toBytes()],
16 | program ?? TBID_ADDR
17 | );
18 | };
19 |
20 | export const findNftTempPDA = ({
21 | program,
22 | nftMint,
23 | }: {
24 | program?: PublicKey;
25 | nftMint: PublicKey;
26 | }) => {
27 | return PublicKey.findProgramAddressSync(
28 | [Buffer.from("nft_temp_acc"), nftMint.toBytes()],
29 | program ?? TBID_ADDR
30 | );
31 | };
32 |
--------------------------------------------------------------------------------
/src/tensor_bid/sdk.ts:
--------------------------------------------------------------------------------
1 | import {
2 | AnchorProvider,
3 | BorshCoder,
4 | Coder,
5 | EventParser,
6 | Instruction,
7 | Program,
8 | } from "@coral-xyz/anchor";
9 | import {
10 | ASSOCIATED_TOKEN_PROGRAM_ID,
11 | getAssociatedTokenAddressSync,
12 | getExtraAccountMetaAddress,
13 | TOKEN_2022_PROGRAM_ID,
14 | } from "@solana/spl-token";
15 | import {
16 | AccountInfo,
17 | AccountMeta,
18 | Commitment,
19 | PublicKey,
20 | SystemProgram,
21 | SYSVAR_INSTRUCTIONS_PUBKEY,
22 | SYSVAR_RENT_PUBKEY,
23 | TransactionResponse,
24 | } from "@solana/web3.js";
25 | import {
26 | AcctDiscHexMap,
27 | AUTH_PROGRAM_ID,
28 | Cluster,
29 | decodeAnchorAcct,
30 | genAcctDiscHexMap,
31 | getRent,
32 | getRentSync,
33 | hexCode,
34 | parseAnchorIxs,
35 | ParsedAnchorIx,
36 | PnftArgs,
37 | prependComputeIxs,
38 | prepPnftAccounts,
39 | TMETA_PROGRAM_ID,
40 | } from "@tensor-hq/tensor-common";
41 | import BN from "bn.js";
42 | import {
43 | AccountSuffix,
44 | DEFAULT_MICRO_LAMPORTS,
45 | DEFAULT_XFER_COMPUTE_UNITS,
46 | evalMathExpr,
47 | } from "../common";
48 | import {
49 | WNS_PROGRAM_ID,
50 | WNS_DISTRIBUTION_PROGRAM_ID,
51 | getApprovalAccount,
52 | getDistributionAccount,
53 | } from "../token2022";
54 | import { findTSwapPDA, TensorSwapSDK, TENSORSWAP_ADDR } from "../tensorswap";
55 | import { ParsedAccount } from "../types";
56 | import { TBID_ADDR } from "./constants";
57 | import { findBidStatePda, findNftTempPDA } from "./pda";
58 |
59 | // ---------------------------------------- Versioned IDLs for backwards compat when parsing.
60 | import {
61 | IDL as IDL_v0_1_0,
62 | TensorBid as TBid_v0_1_0,
63 | } from "./idl/tensor_bid_v0_1_0";
64 |
65 | import { IDL as IDL_latest, TensorBid as TBid_latest } from "./idl/tensor_bid";
66 |
67 | // initial deployment: https://explorer.solana.com/tx/2pLEU4Bvvd6xtRasDMQa9pRjhEsJKzqRoaQ4oDBG38AWadHUPudi8WjNACrB4neR5ap1GAxK6kvgcMuYYRvSVg11
68 | export const TBidIDL_v0_1_0 = IDL_v0_1_0;
69 | export const TBidIDL_v0_1_0_EffSlot_Mainnet = 183865849;
70 |
71 | // remove margin funding during bidding: https://solscan.io/tx/5A7XWvgicH1hDYAPtWhZd2SX7WCvUB2jjKDFqyRr6MwtfnGuTyfPkngTvQ7dFfcSTvjihLuBSETftPo1u5iixpp
72 | export const TBidIDL_latest = IDL_latest;
73 | export const TBidIDL_latest_EffSlot_Mainnet = 184669012;
74 | export const TBidIDL_latest_EffSlot_Devnet = 203536603;
75 |
76 | export type TBidIDL = TBid_v0_1_0 | TBid_latest;
77 |
78 | // Use this function to figure out which IDL to use based on the slot # of historical txs.
79 | export const triageBidIDL = (
80 | slot: number | bigint,
81 | cluster: Cluster
82 | ): TBidIDL | null => {
83 | switch (cluster) {
84 | case Cluster.Mainnet:
85 | if (slot < TBidIDL_v0_1_0_EffSlot_Mainnet) return null;
86 | if (slot < TBidIDL_latest_EffSlot_Mainnet) return TBidIDL_v0_1_0;
87 | return TBidIDL_latest;
88 | case Cluster.Devnet:
89 | if (slot < TBidIDL_latest_EffSlot_Devnet) return null;
90 | return TBidIDL_latest;
91 | }
92 | };
93 |
94 | // --------------------------------------- constants
95 |
96 | export const CURRENT_TBID_VERSION: number = +IDL_latest.constants.find(
97 | (c) => c.name === "CURRENT_TBID_VERSION"
98 | )!.value;
99 | export const TBID_TAKER_FEE_BPS: number = +IDL_latest.constants.find(
100 | (c) => c.name === "TBID_TAKER_FEE_BPS"
101 | )!.value;
102 | export const MAX_EXPIRY_SEC: number = +IDL_latest.constants.find(
103 | (c) => c.name === "MAX_EXPIRY_SEC"
104 | )!.value;
105 | export const BID_STATE_SIZE: number = evalMathExpr(
106 | IDL_latest.constants.find((c) => c.name === "BID_STATE_SIZE")!.value
107 | );
108 |
109 | export const APPROX_BID_STATE_RENT = getRentSync(BID_STATE_SIZE);
110 |
111 | // --------------------------------------- state structs
112 |
113 | export type BidStateAnchor = {
114 | version: number;
115 | bidAmount: BN;
116 | nftMint: PublicKey;
117 | bidder: PublicKey;
118 | bump: number[];
119 | expiry: BN;
120 | margin: PublicKey | null;
121 | updatedAt: BN;
122 | };
123 |
124 | export type TensorBidPdaAnchor = BidStateAnchor;
125 |
126 | export type TaggedTensorBidPdaAnchor = {
127 | name: "bidState";
128 | account: BidStateAnchor;
129 | };
130 |
131 | // ------------- Types for parsed ixs from raw tx.
132 |
133 | export type TBidIxName = (typeof IDL_latest)["instructions"][number]["name"];
134 | export type TBidIx = Omit & { name: TBidIxName };
135 | export type ParsedTBidIx = ParsedAnchorIx;
136 | export type TBidPricedIx = { lamports: BN };
137 |
138 | // --------------------------------------- sdk
139 |
140 | export class TensorBidSDK {
141 | program: Program;
142 | discMap: AcctDiscHexMap;
143 | coder: BorshCoder;
144 | eventParser: EventParser;
145 |
146 | constructor({
147 | idl = IDL_latest,
148 | addr = TBID_ADDR,
149 | provider,
150 | coder,
151 | }: {
152 | idl?: any; //todo better typing
153 | addr?: PublicKey;
154 | provider?: AnchorProvider;
155 | coder?: Coder;
156 | }) {
157 | this.program = new Program(idl, addr, provider, coder);
158 | this.discMap = genAcctDiscHexMap(idl);
159 | this.coder = new BorshCoder(idl);
160 | this.eventParser = new EventParser(addr, this.coder);
161 | }
162 |
163 | // --------------------------------------- fetchers
164 |
165 | async fetchBidState(bidState: PublicKey, commitment?: Commitment) {
166 | return (await this.program.account.bidState.fetch(
167 | bidState,
168 | commitment
169 | )) as BidStateAnchor;
170 | }
171 |
172 | // --------------------------------------- account methods
173 |
174 | decode(acct: AccountInfo): TaggedTensorBidPdaAnchor | null {
175 | if (!acct.owner.equals(this.program.programId)) return null;
176 | return decodeAnchorAcct(acct, this.discMap);
177 | }
178 |
179 | // --------------------------------------- ixs
180 |
181 | async bid({
182 | bidder,
183 | nftMint,
184 | lamports,
185 | margin = null,
186 | expireIn = null,
187 | }: {
188 | bidder: PublicKey;
189 | nftMint: PublicKey;
190 | lamports: BN;
191 | margin?: PublicKey | null;
192 | expireIn?: BN | null;
193 | }) {
194 | const [bidState, bidStateBump] = findBidStatePda({
195 | mint: nftMint,
196 | owner: bidder,
197 | });
198 | const [tswapPda, tswapPdaBump] = findTSwapPDA({});
199 |
200 | const builder = this.program.methods.bid(lamports, expireIn).accounts({
201 | nftMint,
202 | bidder,
203 | bidState,
204 | rent: SYSVAR_RENT_PUBKEY,
205 | systemProgram: SystemProgram.programId,
206 | tswap: tswapPda,
207 | //optional, as a default pick another mutable account
208 | marginAccount: margin ?? bidder,
209 | });
210 |
211 | return {
212 | builder,
213 | tx: { ixs: [await builder.instruction()], extraSigners: [] },
214 | bidState,
215 | bidStateBump,
216 | tswapPda,
217 | tswapPdaBump,
218 | };
219 | }
220 |
221 | async takeBid({
222 | bidder,
223 | seller,
224 | nftMint,
225 | lamports,
226 | tokenProgram,
227 | margin = null,
228 | nftSellerAcc,
229 | meta,
230 | authData,
231 | compute = DEFAULT_XFER_COMPUTE_UNITS,
232 | priorityMicroLamports = DEFAULT_MICRO_LAMPORTS,
233 | optionalRoyaltyPct = null,
234 | takerBroker = null,
235 | }: {
236 | bidder: PublicKey;
237 | seller: PublicKey;
238 | nftMint: PublicKey;
239 | lamports: BN;
240 | tokenProgram: PublicKey;
241 | margin?: PublicKey | null;
242 | nftSellerAcc: PublicKey;
243 | //optional % OF full royalty amount, so eg 50% of 10% royalty would be 5%
244 | optionalRoyaltyPct?: number | null;
245 | //optional taker broker account
246 | takerBroker?: PublicKey | null;
247 | } & PnftArgs) {
248 | const [bidState, bidStateBump] = findBidStatePda({
249 | mint: nftMint,
250 | owner: bidder,
251 | });
252 | const [tswapPda, tswapPdaBump] = findTSwapPDA({});
253 |
254 | const swapSdk = new TensorSwapSDK({
255 | provider: this.program.provider as AnchorProvider,
256 | });
257 | const tSwapAcc = await swapSdk.fetchTSwap(tswapPda);
258 | const [tempPda, tempPdaBump] = findNftTempPDA({ nftMint });
259 |
260 | const destAta = getAssociatedTokenAddressSync(
261 | nftMint,
262 | bidder,
263 | true,
264 | tokenProgram
265 | );
266 |
267 | //prepare 2 pnft account sets
268 | const {
269 | meta: newMeta,
270 | creators,
271 | ownerTokenRecordBump,
272 | ownerTokenRecordPda,
273 | destTokenRecordBump: tempDestTokenRecordBump,
274 | destTokenRecordPda: tempDestTokenRecordPda,
275 | ruleSet,
276 | nftEditionPda,
277 | authDataSerialized,
278 | } = await prepPnftAccounts({
279 | connection: this.program.provider.connection,
280 | meta,
281 | nftMint,
282 | destAta: tempPda,
283 | authData,
284 | sourceAta: nftSellerAcc,
285 | });
286 | meta = newMeta;
287 | const {
288 | destTokenRecordBump: destTokenRecordBump,
289 | destTokenRecordPda: destTokenRecordPda,
290 | } = await prepPnftAccounts({
291 | connection: this.program.provider.connection,
292 | meta,
293 | nftMint,
294 | destAta,
295 | authData,
296 | sourceAta: tempPda,
297 | });
298 |
299 | const builder = this.program.methods
300 | .takeBid(lamports, !!ruleSet, authDataSerialized, optionalRoyaltyPct)
301 | .accounts({
302 | nftMint,
303 | tswap: tswapPda,
304 | feeVault: tSwapAcc.feeVault,
305 | bidState,
306 | bidder,
307 | nftSellerAcc,
308 | nftMetadata: meta.address,
309 | nftBidderAcc: destAta,
310 | nftTempAcc: tempPda,
311 | seller,
312 | tensorswapProgram: TENSORSWAP_ADDR,
313 | tokenProgram,
314 | associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
315 | systemProgram: SystemProgram.programId,
316 | rent: SYSVAR_RENT_PUBKEY,
317 | nftEdition: nftEditionPda,
318 | bidderTokenRecord: destTokenRecordPda,
319 | sellerTokenRecord: ownerTokenRecordPda,
320 | tempTokenRecord: tempDestTokenRecordPda,
321 | marginAccount: margin ?? seller,
322 | authRules: ruleSet ?? SystemProgram.programId,
323 | takerBroker: takerBroker ?? tSwapAcc.feeVault,
324 | pnftShared: {
325 | authorizationRulesProgram: AUTH_PROGRAM_ID,
326 | tokenMetadataProgram: TMETA_PROGRAM_ID,
327 | instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
328 | },
329 | })
330 | .remainingAccounts(
331 | creators.map((c) => ({
332 | pubkey: c.address,
333 | isWritable: c.share > 0, //only writable if they have a share
334 | isSigner: false,
335 | }))
336 | );
337 |
338 | return {
339 | builder,
340 | tx: {
341 | ixs: prependComputeIxs(
342 | [await builder.instruction()],
343 | compute,
344 | priorityMicroLamports
345 | ),
346 | extraSigners: [],
347 | },
348 | bidState,
349 | bidStateBump,
350 | tswapPda,
351 | tswapPdaBump,
352 | tempPda,
353 | tempPdaBump,
354 | meta,
355 | ownerTokenRecordBump,
356 | ownerTokenRecordPda,
357 | destTokenRecordBump,
358 | destTokenRecordPda,
359 | tempDestTokenRecordBump,
360 | tempDestTokenRecordPda,
361 | ruleSet,
362 | nftEditionPda,
363 | authDataSerialized,
364 | nftbidderAcc: destAta,
365 | };
366 | }
367 |
368 | async cancelBid({
369 | bidder,
370 | nftMint,
371 | }: {
372 | bidder: PublicKey;
373 | nftMint: PublicKey;
374 | }) {
375 | const [bidState, bidStateBump] = findBidStatePda({
376 | mint: nftMint,
377 | owner: bidder,
378 | });
379 |
380 | const builder = this.program.methods.cancelBid().accounts({
381 | nftMint,
382 | bidder,
383 | bidState,
384 | rent: SYSVAR_RENT_PUBKEY,
385 | systemProgram: SystemProgram.programId,
386 | });
387 |
388 | return {
389 | builder,
390 | tx: { ixs: [await builder.instruction()], extraSigners: [] },
391 | bidState,
392 | bidStateBump,
393 | };
394 | }
395 |
396 | async closeExpiredBid({
397 | bidder,
398 | nftMint,
399 | }: {
400 | bidder: PublicKey;
401 | nftMint: PublicKey;
402 | }) {
403 | const [bidState, bidStateBump] = findBidStatePda({
404 | mint: nftMint,
405 | owner: bidder,
406 | });
407 | const builder = this.program.methods.closeExpiredBid().accounts({
408 | nftMint,
409 | bidder,
410 | bidState,
411 | rent: SYSVAR_RENT_PUBKEY,
412 | systemProgram: SystemProgram.programId,
413 | });
414 |
415 | return {
416 | builder,
417 | tx: { ixs: [await builder.instruction()], extraSigners: [] },
418 | bidState,
419 | bidStateBump,
420 | };
421 | }
422 |
423 | // --------------------------------------- T22
424 |
425 | async takeBidT22({
426 | bidder,
427 | seller,
428 | nftMint,
429 | lamports,
430 | margin = null,
431 | nftSellerAcc,
432 | compute = DEFAULT_XFER_COMPUTE_UNITS,
433 | priorityMicroLamports = DEFAULT_MICRO_LAMPORTS,
434 | takerBroker = null,
435 | }: {
436 | bidder: PublicKey;
437 | seller: PublicKey;
438 | nftMint: PublicKey;
439 | lamports: BN;
440 | margin?: PublicKey | null;
441 | nftSellerAcc: PublicKey;
442 | compute?: number | null | undefined;
443 | priorityMicroLamports?: number | null | undefined;
444 | //optional taker broker account
445 | takerBroker?: PublicKey | null;
446 | }) {
447 | const [bidState, bidStateBump] = findBidStatePda({
448 | mint: nftMint,
449 | owner: bidder,
450 | });
451 | const [tswapPda, tswapPdaBump] = findTSwapPDA({});
452 |
453 | const swapSdk = new TensorSwapSDK({
454 | provider: this.program.provider as AnchorProvider,
455 | });
456 | const tSwapAcc = await swapSdk.fetchTSwap(tswapPda);
457 | const [tempPda, tempPdaBump] = findNftTempPDA({ nftMint });
458 |
459 | const destAta = getAssociatedTokenAddressSync(
460 | nftMint,
461 | bidder,
462 | true,
463 | TOKEN_2022_PROGRAM_ID
464 | );
465 |
466 | const builder = this.program.methods.takeBidT22(lamports).accounts({
467 | nftMint,
468 | tswap: tswapPda,
469 | feeVault: tSwapAcc.feeVault,
470 | bidState,
471 | bidder,
472 | nftSellerAcc,
473 | nftBidderAcc: destAta,
474 | seller,
475 | tensorswapProgram: TENSORSWAP_ADDR,
476 | tokenProgram: TOKEN_2022_PROGRAM_ID,
477 | associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
478 | systemProgram: SystemProgram.programId,
479 | rent: SYSVAR_RENT_PUBKEY,
480 | marginAccount: margin ?? seller,
481 | takerBroker: takerBroker ?? tSwapAcc.feeVault,
482 | });
483 |
484 | return {
485 | builder,
486 | tx: {
487 | ixs: prependComputeIxs(
488 | [await builder.instruction()],
489 | compute,
490 | priorityMicroLamports
491 | ),
492 | extraSigners: [],
493 | },
494 | bidState,
495 | bidStateBump,
496 | tswapPda,
497 | tswapPdaBump,
498 | tempPda,
499 | tempPdaBump,
500 | nftbidderAcc: destAta,
501 | };
502 | }
503 |
504 | // --------------------------------------- WNS
505 |
506 | async wnsTakeBid({
507 | bidder,
508 | seller,
509 | nftMint,
510 | lamports,
511 | margin = null,
512 | nftSellerAcc,
513 | collectionMint,
514 | compute = DEFAULT_XFER_COMPUTE_UNITS,
515 | priorityMicroLamports = DEFAULT_MICRO_LAMPORTS,
516 | takerBroker = null,
517 | }: {
518 | bidder: PublicKey;
519 | seller: PublicKey;
520 | nftMint: PublicKey;
521 | lamports: BN;
522 | margin?: PublicKey | null;
523 | nftSellerAcc: PublicKey;
524 | collectionMint: PublicKey;
525 | compute?: number | null | undefined;
526 | priorityMicroLamports?: number | null | undefined;
527 | //optional taker broker account
528 | takerBroker?: PublicKey | null;
529 | }) {
530 | const [bidState, bidStateBump] = findBidStatePda({
531 | mint: nftMint,
532 | owner: bidder,
533 | });
534 | const [tswapPda, tswapPdaBump] = findTSwapPDA({});
535 |
536 | const swapSdk = new TensorSwapSDK({
537 | provider: this.program.provider as AnchorProvider,
538 | });
539 | const tSwapAcc = await swapSdk.fetchTSwap(tswapPda);
540 | const [tempPda, tempPdaBump] = findNftTempPDA({ nftMint });
541 |
542 | const destAta = getAssociatedTokenAddressSync(
543 | nftMint,
544 | bidder,
545 | true,
546 | TOKEN_2022_PROGRAM_ID
547 | );
548 |
549 | const approveAccount = getApprovalAccount(nftMint);
550 | const distribution = getDistributionAccount(collectionMint);
551 | const extraMetas = getExtraAccountMetaAddress(nftMint, WNS_PROGRAM_ID);
552 |
553 | const builder = this.program.methods.wnsTakeBid(lamports).accounts({
554 | nftMint,
555 | tswap: tswapPda,
556 | feeVault: tSwapAcc.feeVault,
557 | bidState,
558 | bidder,
559 | nftSellerAcc,
560 | nftBidderAcc: destAta,
561 | seller,
562 | tensorswapProgram: TENSORSWAP_ADDR,
563 | tokenProgram: TOKEN_2022_PROGRAM_ID,
564 | associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
565 | systemProgram: SystemProgram.programId,
566 | rent: SYSVAR_RENT_PUBKEY,
567 | marginAccount: margin ?? seller,
568 | takerBroker: takerBroker ?? tSwapAcc.feeVault,
569 | approveAccount,
570 | distribution,
571 | distributionProgram: WNS_DISTRIBUTION_PROGRAM_ID,
572 | wnsProgram: WNS_PROGRAM_ID,
573 | extraMetas,
574 | });
575 |
576 | return {
577 | builder,
578 | tx: {
579 | ixs: prependComputeIxs(
580 | [await builder.instruction()],
581 | compute,
582 | priorityMicroLamports
583 | ),
584 | extraSigners: [],
585 | },
586 | bidState,
587 | bidStateBump,
588 | tswapPda,
589 | tswapPdaBump,
590 | tempPda,
591 | tempPdaBump,
592 | nftbidderAcc: destAta,
593 | };
594 | }
595 |
596 | // --------------------------------------- helpers
597 |
598 | async getBidStateRent() {
599 | return await getRent(
600 | this.program.provider.connection,
601 | this.program.account.bidState
602 | );
603 | }
604 |
605 | getError(
606 | name: (typeof IDL_latest)["errors"][number]["name"]
607 | ): (typeof IDL_latest)["errors"][number] {
608 | //@ts-ignore (throwing weird ts errors for me)
609 | return this.program.idl.errors.find((e) => e.name === name)!;
610 | }
611 |
612 | getErrorCodeHex(name: (typeof IDL_latest)["errors"][number]["name"]): string {
613 | return hexCode(this.getError(name).code);
614 | }
615 |
616 | // --------------------------------------- parsing raw txs
617 |
618 | /** This only works for the latest IDL. This is intentional: otherwise we'll need to switch/case all historical deprecated ixs downstream. */
619 | parseIxs(tx: TransactionResponse): ParsedTBidIx[] {
620 | return parseAnchorIxs({
621 | tx,
622 | coder: this.coder,
623 | eventParser: this.eventParser,
624 | programId: this.program.programId,
625 | });
626 | }
627 |
628 | getFeeAmount(ix: ParsedTBidIx): BN | null {
629 | switch (ix.ix.name) {
630 | case "takeBid":
631 | case "takeBidT22":
632 | case "wnsTakeBid": {
633 | const event = ix.events[0].data;
634 | return event.tswapFee.add(event.creatorsFee);
635 | }
636 | case "bid":
637 | case "cancelBid":
638 | case "closeExpiredBid":
639 | return null;
640 | }
641 | }
642 |
643 | getSolAmount(ix: ParsedTBidIx): BN | null {
644 | switch (ix.ix.name) {
645 | case "takeBid":
646 | case "takeBidT22":
647 | case "wnsTakeBid":
648 | case "bid":
649 | return (ix.ix.data as TBidPricedIx).lamports;
650 | case "cancelBid":
651 | case "closeExpiredBid":
652 | return null;
653 | }
654 | }
655 |
656 | // FYI: accounts under InstructioNDisplay is the space-separated capitalized
657 | // version of the fields for the corresponding #[Accounts].
658 | // eg sol_escrow -> "Sol Escrow', or tswap -> "Tswap"
659 | // shared.sol_escrow -> "Shared > Sol Escrow"
660 | getAccountByName(
661 | ix: ParsedTBidIx,
662 | name: AccountSuffix
663 | ): ParsedAccount | undefined {
664 | // We use endsWith since composite nested accounts (eg shared.sol_escrow)
665 | // will prefix it as "Shared > Sol Escrow"
666 | return ix.formatted?.accounts.find((acc) => acc.name?.endsWith(name));
667 | }
668 | }
669 |
--------------------------------------------------------------------------------
/src/tensor_whitelist/constants.ts:
--------------------------------------------------------------------------------
1 | import { PublicKey } from "@solana/web3.js";
2 |
3 | export const TLIST_ADDR = new PublicKey(
4 | process.env.TLIST_ADDR || "TL1ST2iRBzuGTqLn1KXnGdSnEow62BzPnGiqyRXhWtW"
5 | );
6 |
7 | export const TLIST_COSIGNER = new PublicKey(
8 | process.env.TLIST_COSIGNER || "5aB7nyNJTuQZdKnhZXQHNhT16tBNevCuLRp14btvANxu"
9 | );
10 |
11 | export const TLIST_OWNER = new PublicKey(
12 | process.env.TLIST_OWNER || "99cmWwQMqMFzMPx85rvZYKwusGSjZUDsu6mqYV4iisiz"
13 | );
14 |
15 | export const MAX_PROOF_LEN = 28;
16 |
--------------------------------------------------------------------------------
/src/tensor_whitelist/idl/tensor_whitelist.ts:
--------------------------------------------------------------------------------
1 | export type TensorWhitelist = {
2 | "version": "0.2.1",
3 | "name": "tensor_whitelist",
4 | "constants": [
5 | {
6 | "name": "AUTHORITY_SIZE",
7 | "type": {
8 | "defined": "usize"
9 | },
10 | "value": "8 + 1 + (32 * 2) + 64"
11 | },
12 | {
13 | "name": "WHITELIST_SIZE",
14 | "type": {
15 | "defined": "usize"
16 | },
17 | "value": "8 + 1 + 1 + 1 + (32 * 3) + 1 + (33 * 2) + 64"
18 | },
19 | {
20 | "name": "MINT_PROOF_SIZE",
21 | "type": {
22 | "defined": "usize"
23 | },
24 | "value": "8 + (32 * 28) + 1"
25 | }
26 | ],
27 | "instructions": [
28 | {
29 | "name": "initUpdateAuthority",
30 | "accounts": [
31 | {
32 | "name": "whitelistAuthority",
33 | "isMut": true,
34 | "isSigner": false
35 | },
36 | {
37 | "name": "cosigner",
38 | "isMut": true,
39 | "isSigner": true,
40 | "docs": [
41 | "both have to sign on any updates"
42 | ]
43 | },
44 | {
45 | "name": "owner",
46 | "isMut": false,
47 | "isSigner": true
48 | },
49 | {
50 | "name": "systemProgram",
51 | "isMut": false,
52 | "isSigner": false
53 | }
54 | ],
55 | "args": [
56 | {
57 | "name": "newCosigner",
58 | "type": {
59 | "option": "publicKey"
60 | }
61 | },
62 | {
63 | "name": "newOwner",
64 | "type": {
65 | "option": "publicKey"
66 | }
67 | }
68 | ]
69 | },
70 | {
71 | "name": "initUpdateWhitelist",
72 | "docs": [
73 | "Store min 1, max 3, check in priority order"
74 | ],
75 | "accounts": [
76 | {
77 | "name": "whitelist",
78 | "isMut": true,
79 | "isSigner": false
80 | },
81 | {
82 | "name": "whitelistAuthority",
83 | "isMut": false,
84 | "isSigner": false,
85 | "docs": [
86 | "there can only be 1 whitelist authority (due to seeds),",
87 | "and we're checking that 1)the correct cosigner is present on it, and 2)is a signer"
88 | ]
89 | },
90 | {
91 | "name": "cosigner",
92 | "isMut": true,
93 | "isSigner": true,
94 | "docs": [
95 | "only cosigner has to sign for unfrozen, for frozen owner also has to sign"
96 | ]
97 | },
98 | {
99 | "name": "systemProgram",
100 | "isMut": false,
101 | "isSigner": false
102 | }
103 | ],
104 | "args": [
105 | {
106 | "name": "uuid",
107 | "type": {
108 | "array": [
109 | "u8",
110 | 32
111 | ]
112 | }
113 | },
114 | {
115 | "name": "rootHash",
116 | "type": {
117 | "option": {
118 | "array": [
119 | "u8",
120 | 32
121 | ]
122 | }
123 | }
124 | },
125 | {
126 | "name": "name",
127 | "type": {
128 | "option": {
129 | "array": [
130 | "u8",
131 | 32
132 | ]
133 | }
134 | }
135 | },
136 | {
137 | "name": "voc",
138 | "type": {
139 | "option": "publicKey"
140 | }
141 | },
142 | {
143 | "name": "fvc",
144 | "type": {
145 | "option": "publicKey"
146 | }
147 | }
148 | ]
149 | },
150 | {
151 | "name": "initUpdateMintProof",
152 | "accounts": [
153 | {
154 | "name": "whitelist",
155 | "isMut": false,
156 | "isSigner": false
157 | },
158 | {
159 | "name": "mint",
160 | "isMut": false,
161 | "isSigner": false
162 | },
163 | {
164 | "name": "mintProof",
165 | "isMut": true,
166 | "isSigner": false
167 | },
168 | {
169 | "name": "user",
170 | "isMut": true,
171 | "isSigner": true
172 | },
173 | {
174 | "name": "systemProgram",
175 | "isMut": false,
176 | "isSigner": false
177 | }
178 | ],
179 | "args": [
180 | {
181 | "name": "proof",
182 | "type": {
183 | "vec": {
184 | "array": [
185 | "u8",
186 | 32
187 | ]
188 | }
189 | }
190 | }
191 | ]
192 | },
193 | {
194 | "name": "reallocAuthority",
195 | "accounts": [
196 | {
197 | "name": "whitelistAuthority",
198 | "isMut": true,
199 | "isSigner": false,
200 | "docs": [
201 | "there can only be 1 whitelist authority (due to seeds),",
202 | "and we're checking that 1)the correct cosigner is present on it, and 2)is a signer"
203 | ]
204 | },
205 | {
206 | "name": "cosigner",
207 | "isMut": true,
208 | "isSigner": true
209 | },
210 | {
211 | "name": "systemProgram",
212 | "isMut": false,
213 | "isSigner": false
214 | }
215 | ],
216 | "args": []
217 | },
218 | {
219 | "name": "reallocWhitelist",
220 | "accounts": [
221 | {
222 | "name": "whitelist",
223 | "isMut": true,
224 | "isSigner": false
225 | },
226 | {
227 | "name": "whitelistAuthority",
228 | "isMut": false,
229 | "isSigner": false,
230 | "docs": [
231 | "there can only be 1 whitelist authority (due to seeds),",
232 | "and we're checking that 1)the correct cosigner is present on it, and 2)is a signer"
233 | ]
234 | },
235 | {
236 | "name": "cosigner",
237 | "isMut": true,
238 | "isSigner": true
239 | },
240 | {
241 | "name": "systemProgram",
242 | "isMut": false,
243 | "isSigner": false
244 | }
245 | ],
246 | "args": []
247 | },
248 | {
249 | "name": "freezeWhitelist",
250 | "accounts": [
251 | {
252 | "name": "whitelist",
253 | "isMut": true,
254 | "isSigner": false
255 | },
256 | {
257 | "name": "whitelistAuthority",
258 | "isMut": false,
259 | "isSigner": false,
260 | "docs": [
261 | "there can only be 1 whitelist authority (due to seeds),",
262 | "and we're checking that 1)the correct cosigner is present on it, and 2)is a signer"
263 | ]
264 | },
265 | {
266 | "name": "cosigner",
267 | "isMut": true,
268 | "isSigner": true,
269 | "docs": [
270 | "freezing only requires cosigner"
271 | ]
272 | },
273 | {
274 | "name": "systemProgram",
275 | "isMut": false,
276 | "isSigner": false
277 | }
278 | ],
279 | "args": []
280 | },
281 | {
282 | "name": "unfreezeWhitelist",
283 | "accounts": [
284 | {
285 | "name": "whitelist",
286 | "isMut": true,
287 | "isSigner": false
288 | },
289 | {
290 | "name": "whitelistAuthority",
291 | "isMut": false,
292 | "isSigner": false,
293 | "docs": [
294 | "there can only be 1 whitelist authority (due to seeds),",
295 | "and we're checking that 1)the correct cosigner is present on it, and 2)is a signer"
296 | ]
297 | },
298 | {
299 | "name": "owner",
300 | "isMut": true,
301 | "isSigner": true,
302 | "docs": [
303 | "unfreezing requires owner"
304 | ]
305 | },
306 | {
307 | "name": "systemProgram",
308 | "isMut": false,
309 | "isSigner": false
310 | }
311 | ],
312 | "args": []
313 | }
314 | ],
315 | "accounts": [
316 | {
317 | "name": "authority",
318 | "type": {
319 | "kind": "struct",
320 | "fields": [
321 | {
322 | "name": "bump",
323 | "type": "u8"
324 | },
325 | {
326 | "name": "cosigner",
327 | "docs": [
328 | "cosigner of the whitelist - has rights to update it if unfrozen"
329 | ],
330 | "type": "publicKey"
331 | },
332 | {
333 | "name": "owner",
334 | "docs": [
335 | "owner of the whitelist (stricter, should be handled more carefully)",
336 | "has rights to 1)freeze, 2)unfreeze, 3)update frozen whitelists"
337 | ],
338 | "type": "publicKey"
339 | },
340 | {
341 | "name": "reserved",
342 | "type": {
343 | "array": [
344 | "u8",
345 | 64
346 | ]
347 | }
348 | }
349 | ]
350 | }
351 | },
352 | {
353 | "name": "whitelist",
354 | "type": {
355 | "kind": "struct",
356 | "fields": [
357 | {
358 | "name": "version",
359 | "type": "u8"
360 | },
361 | {
362 | "name": "bump",
363 | "type": "u8"
364 | },
365 | {
366 | "name": "verified",
367 | "docs": [
368 | "DEPRECATED, doesn't do anything"
369 | ],
370 | "type": "bool"
371 | },
372 | {
373 | "name": "rootHash",
374 | "docs": [
375 | "in the case when not present will be [u8; 32]"
376 | ],
377 | "type": {
378 | "array": [
379 | "u8",
380 | 32
381 | ]
382 | }
383 | },
384 | {
385 | "name": "uuid",
386 | "type": {
387 | "array": [
388 | "u8",
389 | 32
390 | ]
391 | }
392 | },
393 | {
394 | "name": "name",
395 | "type": {
396 | "array": [
397 | "u8",
398 | 32
399 | ]
400 | }
401 | },
402 | {
403 | "name": "frozen",
404 | "type": "bool"
405 | },
406 | {
407 | "name": "voc",
408 | "type": {
409 | "option": "publicKey"
410 | }
411 | },
412 | {
413 | "name": "fvc",
414 | "type": {
415 | "option": "publicKey"
416 | }
417 | },
418 | {
419 | "name": "reserved",
420 | "type": {
421 | "array": [
422 | "u8",
423 | 64
424 | ]
425 | }
426 | }
427 | ]
428 | }
429 | },
430 | {
431 | "name": "mintProof",
432 | "type": {
433 | "kind": "struct",
434 | "fields": [
435 | {
436 | "name": "proofLen",
437 | "type": "u8"
438 | },
439 | {
440 | "name": "proof",
441 | "type": {
442 | "array": [
443 | {
444 | "array": [
445 | "u8",
446 | 32
447 | ]
448 | },
449 | 28
450 | ]
451 | }
452 | }
453 | ]
454 | }
455 | }
456 | ],
457 | "types": [
458 | {
459 | "name": "FullMerkleProof",
460 | "type": {
461 | "kind": "struct",
462 | "fields": [
463 | {
464 | "name": "proof",
465 | "type": {
466 | "vec": {
467 | "array": [
468 | "u8",
469 | 32
470 | ]
471 | }
472 | }
473 | },
474 | {
475 | "name": "leaf",
476 | "type": {
477 | "array": [
478 | "u8",
479 | 32
480 | ]
481 | }
482 | }
483 | ]
484 | }
485 | }
486 | ],
487 | "errors": [
488 | {
489 | "code": 6000,
490 | "name": "BadCosigner",
491 | "msg": "passed in cosigner doesnt have the rights to do this"
492 | },
493 | {
494 | "code": 6001,
495 | "name": "MissingVerification",
496 | "msg": "missing all 3 verification methods: at least one must be present"
497 | },
498 | {
499 | "code": 6002,
500 | "name": "MissingName",
501 | "msg": "missing name"
502 | },
503 | {
504 | "code": 6003,
505 | "name": "BadWhitelist",
506 | "msg": "bad whitelist"
507 | },
508 | {
509 | "code": 6004,
510 | "name": "ProofTooLong",
511 | "msg": "proof provided exceeds the limit of 32 hashes"
512 | },
513 | {
514 | "code": 6005,
515 | "name": "BadOwner",
516 | "msg": "passed in owner doesnt have the rights to do this"
517 | },
518 | {
519 | "code": 6006,
520 | "name": "FailedVocVerification",
521 | "msg": "failed voc verification"
522 | },
523 | {
524 | "code": 6007,
525 | "name": "FailedFvcVerification",
526 | "msg": "failed fvc verification"
527 | },
528 | {
529 | "code": 6008,
530 | "name": "FailedMerkleProofVerification",
531 | "msg": "failed merkle proof verification"
532 | }
533 | ]
534 | };
535 |
536 | export const IDL: TensorWhitelist = {
537 | "version": "0.2.1",
538 | "name": "tensor_whitelist",
539 | "constants": [
540 | {
541 | "name": "AUTHORITY_SIZE",
542 | "type": {
543 | "defined": "usize"
544 | },
545 | "value": "8 + 1 + (32 * 2) + 64"
546 | },
547 | {
548 | "name": "WHITELIST_SIZE",
549 | "type": {
550 | "defined": "usize"
551 | },
552 | "value": "8 + 1 + 1 + 1 + (32 * 3) + 1 + (33 * 2) + 64"
553 | },
554 | {
555 | "name": "MINT_PROOF_SIZE",
556 | "type": {
557 | "defined": "usize"
558 | },
559 | "value": "8 + (32 * 28) + 1"
560 | }
561 | ],
562 | "instructions": [
563 | {
564 | "name": "initUpdateAuthority",
565 | "accounts": [
566 | {
567 | "name": "whitelistAuthority",
568 | "isMut": true,
569 | "isSigner": false
570 | },
571 | {
572 | "name": "cosigner",
573 | "isMut": true,
574 | "isSigner": true,
575 | "docs": [
576 | "both have to sign on any updates"
577 | ]
578 | },
579 | {
580 | "name": "owner",
581 | "isMut": false,
582 | "isSigner": true
583 | },
584 | {
585 | "name": "systemProgram",
586 | "isMut": false,
587 | "isSigner": false
588 | }
589 | ],
590 | "args": [
591 | {
592 | "name": "newCosigner",
593 | "type": {
594 | "option": "publicKey"
595 | }
596 | },
597 | {
598 | "name": "newOwner",
599 | "type": {
600 | "option": "publicKey"
601 | }
602 | }
603 | ]
604 | },
605 | {
606 | "name": "initUpdateWhitelist",
607 | "docs": [
608 | "Store min 1, max 3, check in priority order"
609 | ],
610 | "accounts": [
611 | {
612 | "name": "whitelist",
613 | "isMut": true,
614 | "isSigner": false
615 | },
616 | {
617 | "name": "whitelistAuthority",
618 | "isMut": false,
619 | "isSigner": false,
620 | "docs": [
621 | "there can only be 1 whitelist authority (due to seeds),",
622 | "and we're checking that 1)the correct cosigner is present on it, and 2)is a signer"
623 | ]
624 | },
625 | {
626 | "name": "cosigner",
627 | "isMut": true,
628 | "isSigner": true,
629 | "docs": [
630 | "only cosigner has to sign for unfrozen, for frozen owner also has to sign"
631 | ]
632 | },
633 | {
634 | "name": "systemProgram",
635 | "isMut": false,
636 | "isSigner": false
637 | }
638 | ],
639 | "args": [
640 | {
641 | "name": "uuid",
642 | "type": {
643 | "array": [
644 | "u8",
645 | 32
646 | ]
647 | }
648 | },
649 | {
650 | "name": "rootHash",
651 | "type": {
652 | "option": {
653 | "array": [
654 | "u8",
655 | 32
656 | ]
657 | }
658 | }
659 | },
660 | {
661 | "name": "name",
662 | "type": {
663 | "option": {
664 | "array": [
665 | "u8",
666 | 32
667 | ]
668 | }
669 | }
670 | },
671 | {
672 | "name": "voc",
673 | "type": {
674 | "option": "publicKey"
675 | }
676 | },
677 | {
678 | "name": "fvc",
679 | "type": {
680 | "option": "publicKey"
681 | }
682 | }
683 | ]
684 | },
685 | {
686 | "name": "initUpdateMintProof",
687 | "accounts": [
688 | {
689 | "name": "whitelist",
690 | "isMut": false,
691 | "isSigner": false
692 | },
693 | {
694 | "name": "mint",
695 | "isMut": false,
696 | "isSigner": false
697 | },
698 | {
699 | "name": "mintProof",
700 | "isMut": true,
701 | "isSigner": false
702 | },
703 | {
704 | "name": "user",
705 | "isMut": true,
706 | "isSigner": true
707 | },
708 | {
709 | "name": "systemProgram",
710 | "isMut": false,
711 | "isSigner": false
712 | }
713 | ],
714 | "args": [
715 | {
716 | "name": "proof",
717 | "type": {
718 | "vec": {
719 | "array": [
720 | "u8",
721 | 32
722 | ]
723 | }
724 | }
725 | }
726 | ]
727 | },
728 | {
729 | "name": "reallocAuthority",
730 | "accounts": [
731 | {
732 | "name": "whitelistAuthority",
733 | "isMut": true,
734 | "isSigner": false,
735 | "docs": [
736 | "there can only be 1 whitelist authority (due to seeds),",
737 | "and we're checking that 1)the correct cosigner is present on it, and 2)is a signer"
738 | ]
739 | },
740 | {
741 | "name": "cosigner",
742 | "isMut": true,
743 | "isSigner": true
744 | },
745 | {
746 | "name": "systemProgram",
747 | "isMut": false,
748 | "isSigner": false
749 | }
750 | ],
751 | "args": []
752 | },
753 | {
754 | "name": "reallocWhitelist",
755 | "accounts": [
756 | {
757 | "name": "whitelist",
758 | "isMut": true,
759 | "isSigner": false
760 | },
761 | {
762 | "name": "whitelistAuthority",
763 | "isMut": false,
764 | "isSigner": false,
765 | "docs": [
766 | "there can only be 1 whitelist authority (due to seeds),",
767 | "and we're checking that 1)the correct cosigner is present on it, and 2)is a signer"
768 | ]
769 | },
770 | {
771 | "name": "cosigner",
772 | "isMut": true,
773 | "isSigner": true
774 | },
775 | {
776 | "name": "systemProgram",
777 | "isMut": false,
778 | "isSigner": false
779 | }
780 | ],
781 | "args": []
782 | },
783 | {
784 | "name": "freezeWhitelist",
785 | "accounts": [
786 | {
787 | "name": "whitelist",
788 | "isMut": true,
789 | "isSigner": false
790 | },
791 | {
792 | "name": "whitelistAuthority",
793 | "isMut": false,
794 | "isSigner": false,
795 | "docs": [
796 | "there can only be 1 whitelist authority (due to seeds),",
797 | "and we're checking that 1)the correct cosigner is present on it, and 2)is a signer"
798 | ]
799 | },
800 | {
801 | "name": "cosigner",
802 | "isMut": true,
803 | "isSigner": true,
804 | "docs": [
805 | "freezing only requires cosigner"
806 | ]
807 | },
808 | {
809 | "name": "systemProgram",
810 | "isMut": false,
811 | "isSigner": false
812 | }
813 | ],
814 | "args": []
815 | },
816 | {
817 | "name": "unfreezeWhitelist",
818 | "accounts": [
819 | {
820 | "name": "whitelist",
821 | "isMut": true,
822 | "isSigner": false
823 | },
824 | {
825 | "name": "whitelistAuthority",
826 | "isMut": false,
827 | "isSigner": false,
828 | "docs": [
829 | "there can only be 1 whitelist authority (due to seeds),",
830 | "and we're checking that 1)the correct cosigner is present on it, and 2)is a signer"
831 | ]
832 | },
833 | {
834 | "name": "owner",
835 | "isMut": true,
836 | "isSigner": true,
837 | "docs": [
838 | "unfreezing requires owner"
839 | ]
840 | },
841 | {
842 | "name": "systemProgram",
843 | "isMut": false,
844 | "isSigner": false
845 | }
846 | ],
847 | "args": []
848 | }
849 | ],
850 | "accounts": [
851 | {
852 | "name": "authority",
853 | "type": {
854 | "kind": "struct",
855 | "fields": [
856 | {
857 | "name": "bump",
858 | "type": "u8"
859 | },
860 | {
861 | "name": "cosigner",
862 | "docs": [
863 | "cosigner of the whitelist - has rights to update it if unfrozen"
864 | ],
865 | "type": "publicKey"
866 | },
867 | {
868 | "name": "owner",
869 | "docs": [
870 | "owner of the whitelist (stricter, should be handled more carefully)",
871 | "has rights to 1)freeze, 2)unfreeze, 3)update frozen whitelists"
872 | ],
873 | "type": "publicKey"
874 | },
875 | {
876 | "name": "reserved",
877 | "type": {
878 | "array": [
879 | "u8",
880 | 64
881 | ]
882 | }
883 | }
884 | ]
885 | }
886 | },
887 | {
888 | "name": "whitelist",
889 | "type": {
890 | "kind": "struct",
891 | "fields": [
892 | {
893 | "name": "version",
894 | "type": "u8"
895 | },
896 | {
897 | "name": "bump",
898 | "type": "u8"
899 | },
900 | {
901 | "name": "verified",
902 | "docs": [
903 | "DEPRECATED, doesn't do anything"
904 | ],
905 | "type": "bool"
906 | },
907 | {
908 | "name": "rootHash",
909 | "docs": [
910 | "in the case when not present will be [u8; 32]"
911 | ],
912 | "type": {
913 | "array": [
914 | "u8",
915 | 32
916 | ]
917 | }
918 | },
919 | {
920 | "name": "uuid",
921 | "type": {
922 | "array": [
923 | "u8",
924 | 32
925 | ]
926 | }
927 | },
928 | {
929 | "name": "name",
930 | "type": {
931 | "array": [
932 | "u8",
933 | 32
934 | ]
935 | }
936 | },
937 | {
938 | "name": "frozen",
939 | "type": "bool"
940 | },
941 | {
942 | "name": "voc",
943 | "type": {
944 | "option": "publicKey"
945 | }
946 | },
947 | {
948 | "name": "fvc",
949 | "type": {
950 | "option": "publicKey"
951 | }
952 | },
953 | {
954 | "name": "reserved",
955 | "type": {
956 | "array": [
957 | "u8",
958 | 64
959 | ]
960 | }
961 | }
962 | ]
963 | }
964 | },
965 | {
966 | "name": "mintProof",
967 | "type": {
968 | "kind": "struct",
969 | "fields": [
970 | {
971 | "name": "proofLen",
972 | "type": "u8"
973 | },
974 | {
975 | "name": "proof",
976 | "type": {
977 | "array": [
978 | {
979 | "array": [
980 | "u8",
981 | 32
982 | ]
983 | },
984 | 28
985 | ]
986 | }
987 | }
988 | ]
989 | }
990 | }
991 | ],
992 | "types": [
993 | {
994 | "name": "FullMerkleProof",
995 | "type": {
996 | "kind": "struct",
997 | "fields": [
998 | {
999 | "name": "proof",
1000 | "type": {
1001 | "vec": {
1002 | "array": [
1003 | "u8",
1004 | 32
1005 | ]
1006 | }
1007 | }
1008 | },
1009 | {
1010 | "name": "leaf",
1011 | "type": {
1012 | "array": [
1013 | "u8",
1014 | 32
1015 | ]
1016 | }
1017 | }
1018 | ]
1019 | }
1020 | }
1021 | ],
1022 | "errors": [
1023 | {
1024 | "code": 6000,
1025 | "name": "BadCosigner",
1026 | "msg": "passed in cosigner doesnt have the rights to do this"
1027 | },
1028 | {
1029 | "code": 6001,
1030 | "name": "MissingVerification",
1031 | "msg": "missing all 3 verification methods: at least one must be present"
1032 | },
1033 | {
1034 | "code": 6002,
1035 | "name": "MissingName",
1036 | "msg": "missing name"
1037 | },
1038 | {
1039 | "code": 6003,
1040 | "name": "BadWhitelist",
1041 | "msg": "bad whitelist"
1042 | },
1043 | {
1044 | "code": 6004,
1045 | "name": "ProofTooLong",
1046 | "msg": "proof provided exceeds the limit of 32 hashes"
1047 | },
1048 | {
1049 | "code": 6005,
1050 | "name": "BadOwner",
1051 | "msg": "passed in owner doesnt have the rights to do this"
1052 | },
1053 | {
1054 | "code": 6006,
1055 | "name": "FailedVocVerification",
1056 | "msg": "failed voc verification"
1057 | },
1058 | {
1059 | "code": 6007,
1060 | "name": "FailedFvcVerification",
1061 | "msg": "failed fvc verification"
1062 | },
1063 | {
1064 | "code": 6008,
1065 | "name": "FailedMerkleProofVerification",
1066 | "msg": "failed merkle proof verification"
1067 | }
1068 | ]
1069 | };
1070 |
--------------------------------------------------------------------------------
/src/tensor_whitelist/idl/tensor_whitelist_v0_1_0.ts:
--------------------------------------------------------------------------------
1 | export type TensorWhitelist = {
2 | version: "0.1.0";
3 | name: "tensor_whitelist";
4 | instructions: [
5 | {
6 | name: "initUpdateAuthority";
7 | accounts: [
8 | {
9 | name: "whitelistAuthority";
10 | isMut: true;
11 | isSigner: false;
12 | },
13 | {
14 | name: "owner";
15 | isMut: true;
16 | isSigner: true;
17 | },
18 | {
19 | name: "systemProgram";
20 | isMut: false;
21 | isSigner: false;
22 | }
23 | ];
24 | args: [
25 | {
26 | name: "newOwner";
27 | type: "publicKey";
28 | }
29 | ];
30 | },
31 | {
32 | name: "initUpdateWhitelist";
33 | accounts: [
34 | {
35 | name: "whitelist";
36 | isMut: true;
37 | isSigner: false;
38 | },
39 | {
40 | name: "whitelistAuthority";
41 | isMut: false;
42 | isSigner: false;
43 | docs: [
44 | "there can only be 1 whitelist authority (due to seeds),",
45 | "and we're checking that 1)the correct owner is present on it, and 2)is a signer"
46 | ];
47 | },
48 | {
49 | name: "owner";
50 | isMut: true;
51 | isSigner: true;
52 | },
53 | {
54 | name: "systemProgram";
55 | isMut: false;
56 | isSigner: false;
57 | }
58 | ];
59 | args: [
60 | {
61 | name: "uuid";
62 | type: {
63 | array: ["u8", 32];
64 | };
65 | },
66 | {
67 | name: "rootHash";
68 | type: {
69 | option: {
70 | array: ["u8", 32];
71 | };
72 | };
73 | },
74 | {
75 | name: "name";
76 | type: {
77 | option: {
78 | array: ["u8", 32];
79 | };
80 | };
81 | }
82 | ];
83 | },
84 | {
85 | name: "initUpdateMintProof";
86 | accounts: [
87 | {
88 | name: "whitelist";
89 | isMut: false;
90 | isSigner: false;
91 | },
92 | {
93 | name: "mint";
94 | isMut: false;
95 | isSigner: false;
96 | },
97 | {
98 | name: "mintProof";
99 | isMut: true;
100 | isSigner: false;
101 | },
102 | {
103 | name: "user";
104 | isMut: true;
105 | isSigner: true;
106 | },
107 | {
108 | name: "systemProgram";
109 | isMut: false;
110 | isSigner: false;
111 | }
112 | ];
113 | args: [
114 | {
115 | name: "proof";
116 | type: {
117 | vec: {
118 | array: ["u8", 32];
119 | };
120 | };
121 | }
122 | ];
123 | }
124 | ];
125 | accounts: [
126 | {
127 | name: "authority";
128 | type: {
129 | kind: "struct";
130 | fields: [
131 | {
132 | name: "bump";
133 | type: "u8";
134 | },
135 | {
136 | name: "owner";
137 | type: "publicKey";
138 | }
139 | ];
140 | };
141 | },
142 | {
143 | name: "whitelist";
144 | type: {
145 | kind: "struct";
146 | fields: [
147 | {
148 | name: "version";
149 | type: "u8";
150 | },
151 | {
152 | name: "bump";
153 | type: "u8";
154 | },
155 | {
156 | name: "verified";
157 | type: "bool";
158 | },
159 | {
160 | name: "rootHash";
161 | type: {
162 | array: ["u8", 32];
163 | };
164 | },
165 | {
166 | name: "uuid";
167 | type: {
168 | array: ["u8", 32];
169 | };
170 | },
171 | {
172 | name: "name";
173 | type: {
174 | array: ["u8", 32];
175 | };
176 | }
177 | ];
178 | };
179 | },
180 | {
181 | name: "mintProof";
182 | type: {
183 | kind: "struct";
184 | fields: [
185 | {
186 | name: "proofLen";
187 | type: "u8";
188 | },
189 | {
190 | name: "proof";
191 | type: {
192 | array: [
193 | {
194 | array: ["u8", 32];
195 | },
196 | 28
197 | ];
198 | };
199 | }
200 | ];
201 | };
202 | }
203 | ];
204 | errors: [
205 | {
206 | code: 6000;
207 | name: "BadOwner";
208 | msg: "passed in owner doesnt have the rights to do this";
209 | },
210 | {
211 | code: 6001;
212 | name: "MissingRootHash";
213 | msg: "missing root hash";
214 | },
215 | {
216 | code: 6002;
217 | name: "MissingName";
218 | msg: "missing name";
219 | },
220 | {
221 | code: 6003;
222 | name: "InvalidProof";
223 | msg: "invalid merkle proof, token not whitelisted";
224 | },
225 | {
226 | code: 6004;
227 | name: "ProofTooLong";
228 | msg: "proof provided exceeds the limit of 32 hashes";
229 | }
230 | ];
231 | };
232 |
233 | export const IDL: TensorWhitelist = {
234 | version: "0.1.0",
235 | name: "tensor_whitelist",
236 | instructions: [
237 | {
238 | name: "initUpdateAuthority",
239 | accounts: [
240 | {
241 | name: "whitelistAuthority",
242 | isMut: true,
243 | isSigner: false,
244 | },
245 | {
246 | name: "owner",
247 | isMut: true,
248 | isSigner: true,
249 | },
250 | {
251 | name: "systemProgram",
252 | isMut: false,
253 | isSigner: false,
254 | },
255 | ],
256 | args: [
257 | {
258 | name: "newOwner",
259 | type: "publicKey",
260 | },
261 | ],
262 | },
263 | {
264 | name: "initUpdateWhitelist",
265 | accounts: [
266 | {
267 | name: "whitelist",
268 | isMut: true,
269 | isSigner: false,
270 | },
271 | {
272 | name: "whitelistAuthority",
273 | isMut: false,
274 | isSigner: false,
275 | docs: [
276 | "there can only be 1 whitelist authority (due to seeds),",
277 | "and we're checking that 1)the correct owner is present on it, and 2)is a signer",
278 | ],
279 | },
280 | {
281 | name: "owner",
282 | isMut: true,
283 | isSigner: true,
284 | },
285 | {
286 | name: "systemProgram",
287 | isMut: false,
288 | isSigner: false,
289 | },
290 | ],
291 | args: [
292 | {
293 | name: "uuid",
294 | type: {
295 | array: ["u8", 32],
296 | },
297 | },
298 | {
299 | name: "rootHash",
300 | type: {
301 | option: {
302 | array: ["u8", 32],
303 | },
304 | },
305 | },
306 | {
307 | name: "name",
308 | type: {
309 | option: {
310 | array: ["u8", 32],
311 | },
312 | },
313 | },
314 | ],
315 | },
316 | {
317 | name: "initUpdateMintProof",
318 | accounts: [
319 | {
320 | name: "whitelist",
321 | isMut: false,
322 | isSigner: false,
323 | },
324 | {
325 | name: "mint",
326 | isMut: false,
327 | isSigner: false,
328 | },
329 | {
330 | name: "mintProof",
331 | isMut: true,
332 | isSigner: false,
333 | },
334 | {
335 | name: "user",
336 | isMut: true,
337 | isSigner: true,
338 | },
339 | {
340 | name: "systemProgram",
341 | isMut: false,
342 | isSigner: false,
343 | },
344 | ],
345 | args: [
346 | {
347 | name: "proof",
348 | type: {
349 | vec: {
350 | array: ["u8", 32],
351 | },
352 | },
353 | },
354 | ],
355 | },
356 | ],
357 | accounts: [
358 | {
359 | name: "authority",
360 | type: {
361 | kind: "struct",
362 | fields: [
363 | {
364 | name: "bump",
365 | type: "u8",
366 | },
367 | {
368 | name: "owner",
369 | type: "publicKey",
370 | },
371 | ],
372 | },
373 | },
374 | {
375 | name: "whitelist",
376 | type: {
377 | kind: "struct",
378 | fields: [
379 | {
380 | name: "version",
381 | type: "u8",
382 | },
383 | {
384 | name: "bump",
385 | type: "u8",
386 | },
387 | {
388 | name: "verified",
389 | type: "bool",
390 | },
391 | {
392 | name: "rootHash",
393 | type: {
394 | array: ["u8", 32],
395 | },
396 | },
397 | {
398 | name: "uuid",
399 | type: {
400 | array: ["u8", 32],
401 | },
402 | },
403 | {
404 | name: "name",
405 | type: {
406 | array: ["u8", 32],
407 | },
408 | },
409 | ],
410 | },
411 | },
412 | {
413 | name: "mintProof",
414 | type: {
415 | kind: "struct",
416 | fields: [
417 | {
418 | name: "proofLen",
419 | type: "u8",
420 | },
421 | {
422 | name: "proof",
423 | type: {
424 | array: [
425 | {
426 | array: ["u8", 32],
427 | },
428 | 28,
429 | ],
430 | },
431 | },
432 | ],
433 | },
434 | },
435 | ],
436 | errors: [
437 | {
438 | code: 6000,
439 | name: "BadOwner",
440 | msg: "passed in owner doesnt have the rights to do this",
441 | },
442 | {
443 | code: 6001,
444 | name: "MissingRootHash",
445 | msg: "missing root hash",
446 | },
447 | {
448 | code: 6002,
449 | name: "MissingName",
450 | msg: "missing name",
451 | },
452 | {
453 | code: 6003,
454 | name: "InvalidProof",
455 | msg: "invalid merkle proof, token not whitelisted",
456 | },
457 | {
458 | code: 6004,
459 | name: "ProofTooLong",
460 | msg: "proof provided exceeds the limit of 32 hashes",
461 | },
462 | ],
463 | };
464 |
--------------------------------------------------------------------------------
/src/tensor_whitelist/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./constants";
2 | export * from "./sdk";
3 | export * from "./pda";
4 |
--------------------------------------------------------------------------------
/src/tensor_whitelist/pda.ts:
--------------------------------------------------------------------------------
1 | import { PublicKey } from "@solana/web3.js";
2 | import { TLIST_ADDR } from "./constants";
3 |
4 | export const findWhitelistAuthPDA = ({ program }: { program?: PublicKey }) => {
5 | return PublicKey.findProgramAddressSync([], program ?? TLIST_ADDR);
6 | };
7 |
8 | export const findWhitelistPDA = ({
9 | program,
10 | uuid,
11 | }: {
12 | program?: PublicKey;
13 | uuid: number[];
14 | }) => {
15 | return PublicKey.findProgramAddressSync(
16 | [Buffer.from(uuid)],
17 | program ?? TLIST_ADDR
18 | );
19 | };
20 |
21 | export const findMintProofPDA = ({
22 | program,
23 | mint,
24 | whitelist,
25 | }: {
26 | program?: PublicKey;
27 | mint: PublicKey;
28 | whitelist: PublicKey;
29 | }) => {
30 | return PublicKey.findProgramAddressSync(
31 | [Buffer.from("mint_proof"), mint.toBytes(), whitelist.toBytes()],
32 | program ?? TLIST_ADDR
33 | );
34 | };
35 |
--------------------------------------------------------------------------------
/src/tensor_whitelist/sdk.ts:
--------------------------------------------------------------------------------
1 | import {
2 | AccountInfo,
3 | Commitment,
4 | PublicKey,
5 | SystemProgram,
6 | } from "@solana/web3.js";
7 | import { Coder, Program, Provider } from "@coral-xyz/anchor";
8 | import { TLIST_ADDR, TLIST_COSIGNER, TLIST_OWNER } from "./constants";
9 | import {
10 | findMintProofPDA,
11 | findWhitelistAuthPDA,
12 | findWhitelistPDA,
13 | } from "./pda";
14 | import { v4 } from "uuid";
15 | import MerkleTree from "merkletreejs";
16 | import keccak256 from "keccak256";
17 | import {
18 | DEFAULT_MICRO_LAMPORTS,
19 | DEFAULT_XFER_COMPUTE_UNITS,
20 | evalMathExpr,
21 | } from "../common";
22 |
23 | // ---------------------------------------- Versioned IDLs for backwards compat when parsing.
24 | import {
25 | IDL as IDL_v0_1_0,
26 | TensorWhitelist as TensorWhitelist_v0_1_0,
27 | } from "./idl/tensor_whitelist_v0_1_0";
28 |
29 | import {
30 | IDL as IDL_latest,
31 | TensorWhitelist as TensorWhitelist_latest,
32 | } from "./idl/tensor_whitelist";
33 | import {
34 | AcctDiscHexMap,
35 | Cluster,
36 | decodeAnchorAcct,
37 | genAcctDiscHexMap,
38 | getRent,
39 | getRentSync,
40 | hexCode,
41 | prependComputeIxs,
42 | removeNullBytes,
43 | } from "@tensor-hq/tensor-common";
44 |
45 | //a non-breaking update to migrate account space to exportable constants: https://explorer.solana.com/tx/5czMUGttDttcXwhTTGH8QzyTffwcVfeUAQbY2FzSh8WGxRFBQAmdrYeGBQxfEfS1bog4CfTvqPvXmvxdygQ5aJKE
46 | export const TensorWhitelistIDL_v0_1_0 = IDL_v0_1_0;
47 | export const TensorWhitelistIDL_v0_1_0_EffSlot = 0; //todo find slot
48 |
49 | // added 3 types of verification: https://solscan.io/tx/55gtoZSTKf96XL6XDD5e9F4nkoiPqXHtP4mJoYNT6eZVwtHw2FRRhVxfg9jHADMLrVS2FmNRh2VAWVCqnTxrX3Ro
50 | export const TensorWhitelistIDL_latest = IDL_latest;
51 | export const TensorWhitelistIDL_latest_EffSlot_Mainnet = 172170872;
52 | export const TensorWhitelistIDL_latest_EffSlot_Devnet = 203539290;
53 |
54 | export type TensorWhitelistIDL =
55 | | TensorWhitelist_v0_1_0
56 | | TensorWhitelist_latest;
57 |
58 | // Use this function to figure out which IDL to use based on the slot # of historical txs.
59 | export const triageWhitelistIDL = (
60 | slot: number | bigint,
61 | cluster: Cluster
62 | ): TensorWhitelistIDL | null => {
63 | switch (cluster) {
64 | case Cluster.Mainnet:
65 | //cba to parse really old txs, this was before public launch
66 | if (slot < TensorWhitelistIDL_v0_1_0_EffSlot) return null;
67 | if (slot < TensorWhitelistIDL_latest_EffSlot_Mainnet)
68 | return TensorWhitelistIDL_v0_1_0;
69 | return TensorWhitelistIDL_latest;
70 | case Cluster.Devnet:
71 | if (slot < TensorWhitelistIDL_latest_EffSlot_Devnet)
72 | return TensorWhitelistIDL_v0_1_0;
73 | return TensorWhitelistIDL_latest;
74 | }
75 | };
76 |
77 | // --------------------------------------- constants
78 |
79 | export const WHITELIST_SIZE: number = evalMathExpr(
80 | IDL_latest.constants.find((c) => c.name === "WHITELIST_SIZE")!.value
81 | );
82 | export const AUTHORITY_SIZE: number = evalMathExpr(
83 | IDL_latest.constants.find((c) => c.name === "AUTHORITY_SIZE")!.value
84 | );
85 | export const MINT_PROOF_SIZE: number = evalMathExpr(
86 | IDL_latest.constants.find((c) => c.name === "MINT_PROOF_SIZE")!.value
87 | );
88 |
89 | export const APPROX_WHITELIST_RENT = getRentSync(WHITELIST_SIZE);
90 | export const APPROX_AUTHORITY_RENT = getRentSync(AUTHORITY_SIZE);
91 | export const APPROX_MINT_PROOF_RENT = getRentSync(MINT_PROOF_SIZE);
92 |
93 | // --------------------------------------- state structs
94 |
95 | export type AuthorityAnchor = {
96 | bump: number;
97 | cosigner: PublicKey;
98 | owner: PublicKey;
99 | };
100 |
101 | export type WhitelistAnchor = {
102 | version: number;
103 | bump: number;
104 | verified: boolean;
105 | rootHash: number[];
106 | uuid: number[];
107 | name: number[];
108 | frozen: boolean;
109 | voc?: PublicKey;
110 | fvc?: PublicKey;
111 | };
112 |
113 | export type MintProofAnchor = {
114 | proofLen: number;
115 | proof: number[][];
116 | };
117 |
118 | export type TensorWhitelistPdaAnchor = AuthorityAnchor | WhitelistAnchor;
119 |
120 | export type TaggedTensorWhitelistPdaAnchor =
121 | | {
122 | name: "authority";
123 | account: AuthorityAnchor;
124 | }
125 | | {
126 | name: "whitelist";
127 | account: WhitelistAnchor;
128 | }
129 | | {
130 | name: "mintProof";
131 | account: MintProofAnchor;
132 | };
133 |
134 | // --------------------------------------- sdk
135 |
136 | export class TensorWhitelistSDK {
137 | program: Program;
138 | discMap: AcctDiscHexMap;
139 |
140 | constructor({
141 | idl = IDL_latest,
142 | addr = TLIST_ADDR,
143 | provider,
144 | coder,
145 | }: {
146 | idl?: any; //todo better typing
147 | addr?: PublicKey;
148 | provider?: Provider;
149 | coder?: Coder;
150 | }) {
151 | this.program = new Program(idl, addr, provider, coder);
152 | this.discMap = genAcctDiscHexMap(idl);
153 | }
154 |
155 | // --------------------------------------- fetchers
156 |
157 | async fetchAuthority(authority: PublicKey, commitment?: Commitment) {
158 | return (await this.program.account.authority.fetch(
159 | authority,
160 | commitment
161 | )) as AuthorityAnchor;
162 | }
163 |
164 | async fetchWhitelist(whitelist: PublicKey, commitment?: Commitment) {
165 | return (await this.program.account.whitelist.fetch(
166 | whitelist,
167 | commitment
168 | )) as WhitelistAnchor;
169 | }
170 |
171 | async fetchMintProof(mintProof: PublicKey, commitment?: Commitment) {
172 | return (await this.program.account.mintProof.fetch(
173 | mintProof,
174 | commitment
175 | )) as MintProofAnchor;
176 | }
177 |
178 | // --------------------------------------- account methods
179 |
180 | decode(acct: AccountInfo): TaggedTensorWhitelistPdaAnchor | null {
181 | if (!acct.owner.equals(this.program.programId)) return null;
182 | return decodeAnchorAcct(acct, this.discMap);
183 | }
184 |
185 | // --------------------------------------- authority methods
186 |
187 | //main signature: cosigner
188 | async initUpdateAuthority({
189 | cosigner = TLIST_COSIGNER,
190 | owner = TLIST_OWNER,
191 | newCosigner,
192 | newOwner,
193 | }: {
194 | cosigner?: PublicKey;
195 | owner?: PublicKey;
196 | newCosigner: PublicKey | null;
197 | newOwner: PublicKey | null;
198 | }) {
199 | const [authPda] = findWhitelistAuthPDA({});
200 |
201 | const builder = this.program.methods
202 | .initUpdateAuthority(newCosigner, newOwner)
203 | .accounts({
204 | whitelistAuthority: authPda,
205 | owner,
206 | cosigner,
207 | systemProgram: SystemProgram.programId,
208 | });
209 |
210 | return {
211 | builder,
212 | tx: { ixs: [await builder.instruction()], extraSigners: [] },
213 | authPda,
214 | };
215 | }
216 |
217 | // --------------------------------------- whitelist methods
218 |
219 | //main signature: cosigner
220 | async initUpdateWhitelist({
221 | cosigner = TLIST_COSIGNER,
222 | owner, //can't pass default here, coz then it'll be auto-included in rem accs
223 | uuid,
224 | rootHash = null,
225 | name = null,
226 | voc = null,
227 | fvc = null,
228 | compute = null,
229 | priorityMicroLamports = DEFAULT_MICRO_LAMPORTS,
230 | }: {
231 | cosigner?: PublicKey;
232 | owner?: PublicKey;
233 | uuid: number[];
234 | rootHash?: number[] | null;
235 | name?: number[] | null;
236 | voc?: PublicKey | null;
237 | fvc?: PublicKey | null;
238 | priorityMicroLamports?: number | null;
239 | compute?: number | null;
240 | }) {
241 | const [authPda] = findWhitelistAuthPDA({});
242 | const [whitelistPda] = findWhitelistPDA({
243 | uuid,
244 | });
245 |
246 | //only needed for frozen whitelists
247 | const remAcc = owner
248 | ? [
249 | {
250 | pubkey: owner,
251 | isWritable: false,
252 | isSigner: true,
253 | },
254 | ]
255 | : [];
256 |
257 | const builder = this.program.methods
258 | .initUpdateWhitelist(uuid, rootHash, name, voc, fvc)
259 | .accounts({
260 | whitelist: whitelistPda,
261 | whitelistAuthority: authPda,
262 | cosigner,
263 | systemProgram: SystemProgram.programId,
264 | })
265 | .remainingAccounts(remAcc);
266 |
267 | return {
268 | builder,
269 | tx: {
270 | ixs: prependComputeIxs(
271 | [await builder.instruction()],
272 | compute,
273 | priorityMicroLamports
274 | ),
275 | extraSigners: [],
276 | },
277 | authPda,
278 | whitelistPda,
279 | };
280 | }
281 |
282 | //main signature: cosigner
283 | async freezeWhitelist({
284 | uuid,
285 | cosigner = TLIST_COSIGNER,
286 | }: {
287 | uuid: number[];
288 | cosigner?: PublicKey;
289 | }) {
290 | const [authPda] = findWhitelistAuthPDA({});
291 | const [whitelistPda] = findWhitelistPDA({
292 | uuid,
293 | });
294 |
295 | const builder = this.program.methods.freezeWhitelist().accounts({
296 | whitelist: whitelistPda,
297 | whitelistAuthority: authPda,
298 | cosigner,
299 | systemProgram: SystemProgram.programId,
300 | });
301 |
302 | return {
303 | builder,
304 | tx: { ixs: [await builder.instruction()], extraSigners: [] },
305 | authPda,
306 | whitelistPda,
307 | };
308 | }
309 |
310 | //main signature: owner
311 | async unfreezeWhitelist({
312 | uuid,
313 | owner = TLIST_OWNER,
314 | }: {
315 | uuid: number[];
316 | owner?: PublicKey;
317 | }) {
318 | const [authPda] = findWhitelistAuthPDA({});
319 | const [whitelistPda] = findWhitelistPDA({
320 | uuid,
321 | });
322 |
323 | const builder = this.program.methods.unfreezeWhitelist().accounts({
324 | whitelist: whitelistPda,
325 | whitelistAuthority: authPda,
326 | owner,
327 | systemProgram: SystemProgram.programId,
328 | });
329 |
330 | return {
331 | builder,
332 | tx: { ixs: [await builder.instruction()], extraSigners: [] },
333 | authPda,
334 | whitelistPda,
335 | };
336 | }
337 |
338 | // --------------------------------------- mint proof methods
339 |
340 | //main signature: user
341 | async initUpdateMintProof({
342 | user,
343 | mint,
344 | whitelist,
345 | proof,
346 | }: {
347 | user: PublicKey;
348 | mint: PublicKey;
349 | whitelist: PublicKey;
350 | proof: Buffer[];
351 | }) {
352 | const [mintProofPda] = findMintProofPDA({ mint, whitelist });
353 |
354 | const builder = this.program.methods.initUpdateMintProof(proof).accounts({
355 | whitelist,
356 | mint,
357 | user,
358 | mintProof: mintProofPda,
359 | systemProgram: SystemProgram.programId,
360 | });
361 |
362 | return {
363 | builder,
364 | tx: { ixs: [await builder.instruction()], extraSigners: [] },
365 | mintProofPda,
366 | };
367 | }
368 |
369 | // --------------------------------------- reallocs
370 |
371 | async reallocAuthority({
372 | cosigner = TLIST_COSIGNER,
373 | }: {
374 | cosigner?: PublicKey;
375 | }) {
376 | const [authPda] = findWhitelistAuthPDA({});
377 |
378 | const builder = this.program.methods.reallocAuthority().accounts({
379 | whitelistAuthority: authPda,
380 | cosigner,
381 | systemProgram: SystemProgram.programId,
382 | });
383 |
384 | return {
385 | builder,
386 | tx: { ixs: [await builder.instruction()], extraSigners: [] },
387 | authPda,
388 | };
389 | }
390 |
391 | async reallocWhitelist({
392 | uuid,
393 | cosigner = TLIST_COSIGNER,
394 | }: {
395 | uuid: number[];
396 | cosigner?: PublicKey;
397 | }) {
398 | const [authPda] = findWhitelistAuthPDA({});
399 | const [whitelistPda] = findWhitelistPDA({
400 | uuid,
401 | });
402 |
403 | const builder = this.program.methods.reallocWhitelist().accounts({
404 | whitelist: whitelistPda,
405 | whitelistAuthority: authPda,
406 | cosigner,
407 | systemProgram: SystemProgram.programId,
408 | });
409 |
410 | return {
411 | builder,
412 | tx: { ixs: [await builder.instruction()], extraSigners: [] },
413 | authPda,
414 | whitelistPda,
415 | };
416 | }
417 |
418 | // --------------------------------------- helper methods
419 |
420 | async getWhitelistRent() {
421 | return await getRent(
422 | this.program.provider.connection,
423 | this.program.account.whitelist
424 | );
425 | }
426 |
427 | async getAuthorityRent() {
428 | return await getRent(
429 | this.program.provider.connection,
430 | this.program.account.authority
431 | );
432 | }
433 |
434 | async getMintProofRent() {
435 | return await getRent(
436 | this.program.provider.connection,
437 | this.program.account.mintProof
438 | );
439 | }
440 |
441 | getError(
442 | name: typeof IDL_latest["errors"][number]["name"]
443 | ): typeof IDL_latest["errors"][number] {
444 | //@ts-ignore (throwing weird ts errors for me)
445 | return this.program.idl.errors.find((e) => e.name === name)!;
446 | }
447 |
448 | getErrorCodeHex(name: typeof IDL_latest["errors"][number]["name"]): string {
449 | return hexCode(this.getError(name).code);
450 | }
451 |
452 | static uuidToBuffer = (uuid: string) => {
453 | return Buffer.from(uuid.replaceAll("-", "")).toJSON().data;
454 | };
455 |
456 | static bufferToUuid = (buffer: number[]) => {
457 | const raw = String.fromCharCode(...buffer);
458 | return `${raw.slice(0, 8)}-${raw.slice(8, 12)}-${raw.slice(
459 | 12,
460 | 16
461 | )}-${raw.slice(16, 20)}-${raw.slice(20)}`;
462 | };
463 |
464 | // NB: this truncates names to 32 bytes (32 chars if ascii, < if unicode).
465 | static nameToBuffer = (name: string) => {
466 | return Buffer.from(name.padEnd(32, "\0")).toJSON().data.slice(0, 32);
467 | };
468 |
469 | static bufferToName = (buffer: number[]) => {
470 | return removeNullBytes(String.fromCharCode(...buffer));
471 | };
472 |
473 | // Generates a Merkle tree + root hash + proofs for a set of mints.
474 | static createTreeForMints = (mints: PublicKey[], skipVerify: boolean = false) => {
475 | const buffers = mints.map((m) => m.toBuffer());
476 |
477 | // Create hashes
478 | const leaves = buffers.map(keccak256);
479 |
480 | // Create an array of { leaf, mint } to preserve mapping
481 | const leafMintPairs = leaves.map((leaf, index) => {
482 | return { leaf: leaf, mint: mints[index] };
483 | });
484 |
485 | // Presort the array based on the leaves, so that original leaves remain in the same order after tree construction
486 | const sortedLeafMintPairs = leafMintPairs.slice().sort((a, b) => Buffer.compare(a.leaf, b.leaf));
487 |
488 | // Extract only the leaves from sortedLeafMintPairs
489 | const sortedLeaves = sortedLeafMintPairs.map(pair => pair.leaf);
490 |
491 | const tree = new MerkleTree(sortedLeaves, keccak256, {
492 | sortPairs: true,
493 | });
494 |
495 | const rootHash = tree.getRoot();
496 |
497 | // Get all proofs (order should be same as leaves)
498 | const allProofs = tree.getProofs();
499 |
500 | // This assumes proofs indices align with mints indices (which appears to be the case).
501 | const proofs: { mint: PublicKey; proof: Buffer[] }[] = sortedLeafMintPairs.map((val, index) => {
502 | const proof = allProofs[index];
503 | const mint = val.mint;
504 |
505 | if (!skipVerify && !tree.verify(proof, keccak256(mint.toBuffer()), rootHash)){
506 | throw new Error(`Invalid proof for mint at index ${index}`);
507 | }
508 |
509 | const validProof: Buffer[] = proof.map((p) => p.data);
510 |
511 | return { mint, proof: validProof };
512 | });
513 |
514 | return { tree, root: tree.getRoot().toJSON().data, proofs };
515 | };
516 |
517 | genWhitelistUUID() {
518 | return v4().toString();
519 | }
520 | }
521 |
--------------------------------------------------------------------------------
/src/tensorswap/constants.ts:
--------------------------------------------------------------------------------
1 | import { PublicKey } from "@solana/web3.js";
2 |
3 | // constants separated from sdk.ts, used by the frontend
4 |
5 | //(!) Keep in sync with Tensorswap contract and TBID_FEE_BPS
6 | export const TSWAP_TAKER_FEE_BPS: number = 150;
7 | export const MAKER_REBATE_BPS: number = 25;
8 |
9 | export const TENSORSWAP_ADDR = new PublicKey(
10 | process.env.TENSORSWAP_ADDR || "TSWAPaqyCSx2KABk68Shruf4rp7CxcNi8hAsbdwmHbN"
11 | );
12 |
13 | //@Deprecated, not used inside of Tswap anymore
14 | export const TSWAP_FEE_ACC = new PublicKey(
15 | process.env.TSWAP_FEE_ACC || "4zdNGgAtFsW1cQgHqkiWyRsxaAgxrSRRynnuunxzjxue"
16 | );
17 |
18 | export const TSWAP_COSIGNER = new PublicKey(
19 | process.env.TSWAP_COSIGNER || "6WQvG9Z6D1NZM76Ljz3WjgR7gGXRBJohHASdQxXyKi8q"
20 | );
21 |
22 | export const TSWAP_OWNER = new PublicKey(
23 | process.env.TSWAP_OWNER || "99cmWwQMqMFzMPx85rvZYKwusGSjZUDsu6mqYV4iisiz"
24 | );
25 |
--------------------------------------------------------------------------------
/src/tensorswap/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./prices";
2 | export * from "./types";
3 | export * from "./constants";
4 | export * from "./sdk";
5 | export * from "./pda";
6 |
--------------------------------------------------------------------------------
/src/tensorswap/pda.ts:
--------------------------------------------------------------------------------
1 | import { AccountInfo, Connection, PublicKey } from "@solana/web3.js";
2 | import BN from "bn.js";
3 | import { TENSORSWAP_ADDR } from "./constants";
4 |
5 | export const findPoolPDA = ({
6 | program,
7 | tswap,
8 | owner,
9 | whitelist,
10 | poolType,
11 | curveType,
12 | startingPrice,
13 | delta,
14 | }: {
15 | program?: PublicKey;
16 | tswap: PublicKey;
17 | owner: PublicKey;
18 | whitelist: PublicKey;
19 | poolType: number;
20 | curveType: number;
21 | startingPrice: BN;
22 | delta: BN;
23 | }): [PublicKey, number] => {
24 | return PublicKey.findProgramAddressSync(
25 | [
26 | tswap.toBytes(),
27 | owner.toBytes(),
28 | whitelist.toBytes(),
29 | //u8s, hence 1 byte each
30 | new BN(poolType).toArrayLike(Uint8Array as any, "le", 1),
31 | new BN(curveType).toArrayLike(Uint8Array as any, "le", 1),
32 | //u64s, hence 8 bytes each
33 | startingPrice.toArrayLike(Uint8Array as any, "le", 8),
34 | delta.toArrayLike(Uint8Array as any, "le", 8),
35 | ],
36 | program ?? TENSORSWAP_ADDR
37 | );
38 | };
39 |
40 | export const findTSwapPDA = ({ program }: { program?: PublicKey }) => {
41 | return PublicKey.findProgramAddressSync([], program ?? TENSORSWAP_ADDR);
42 | };
43 |
44 | export type FindMarginArgs = {
45 | tswap: PublicKey;
46 | owner: PublicKey;
47 | marginNr: number;
48 | program?: PublicKey;
49 | };
50 | export const findMarginPDA = ({
51 | tswap,
52 | owner,
53 | marginNr,
54 | program,
55 | }: FindMarginArgs) => {
56 | return PublicKey.findProgramAddressSync(
57 | [
58 | Buffer.from("margin"),
59 | tswap.toBytes(),
60 | owner.toBytes(),
61 | //u16, hence 2 bytes
62 | new BN(marginNr).toArrayLike(Uint8Array as any, "le", 2),
63 | ],
64 | program ?? TENSORSWAP_ADDR
65 | );
66 | };
67 |
68 | export const findNextFreeMarginNr = async ({
69 | connection,
70 | startNr,
71 | tswap,
72 | owner,
73 | program,
74 | }: {
75 | connection: Connection;
76 | startNr?: number;
77 | } & Omit) => {
78 | let marginNr = startNr ?? 0;
79 | let marginPda;
80 | let marginBump;
81 | let account: AccountInfo | null = null;
82 | while (marginNr < 2 ** 16) {
83 | [marginPda, marginBump] = findMarginPDA({
84 | tswap,
85 | owner,
86 | marginNr,
87 | program,
88 | });
89 | account = await connection.getAccountInfo(marginPda);
90 | if (!account) {
91 | return { marginNr, marginPda, marginBump };
92 | }
93 | marginNr++;
94 | }
95 | throw new Error("margin number > u16::MAX");
96 | };
97 |
98 | export const findNftEscrowPDA = ({
99 | program,
100 | nftMint,
101 | }: {
102 | program?: PublicKey;
103 | nftMint: PublicKey;
104 | }) => {
105 | return PublicKey.findProgramAddressSync(
106 | [Buffer.from("nft_escrow"), nftMint.toBytes()],
107 | program ?? TENSORSWAP_ADDR
108 | );
109 | };
110 |
111 | export const findNftDepositReceiptPDA = ({
112 | program,
113 | nftMint,
114 | }: {
115 | program?: PublicKey;
116 | nftMint: PublicKey;
117 | }) => {
118 | return PublicKey.findProgramAddressSync(
119 | [Buffer.from("nft_receipt"), nftMint.toBytes()],
120 | program ?? TENSORSWAP_ADDR
121 | );
122 | };
123 |
124 | export const findSolEscrowPDA = ({
125 | program,
126 | pool,
127 | }: {
128 | program?: PublicKey;
129 | pool: PublicKey;
130 | }) => {
131 | return PublicKey.findProgramAddressSync(
132 | [Buffer.from("sol_escrow"), pool.toBytes()],
133 | program ?? TENSORSWAP_ADDR
134 | );
135 | };
136 |
137 | export const findNftAuthorityPDA = ({
138 | program,
139 | authSeed,
140 | }: {
141 | program?: PublicKey;
142 | authSeed: number[];
143 | }) => {
144 | return PublicKey.findProgramAddressSync(
145 | [Buffer.from("nft_auth"), Buffer.from(authSeed)],
146 | program ?? TENSORSWAP_ADDR
147 | );
148 | };
149 |
150 | export const findSingleListingPDA = ({
151 | program,
152 | nftMint,
153 | }: {
154 | program?: PublicKey;
155 | nftMint: PublicKey;
156 | }) => {
157 | return PublicKey.findProgramAddressSync(
158 | [Buffer.from("single_listing"), nftMint.toBytes()],
159 | program ?? TENSORSWAP_ADDR
160 | );
161 | };
162 |
--------------------------------------------------------------------------------
/src/tensorswap/prices.ts:
--------------------------------------------------------------------------------
1 | import { CurveType, PoolConfig, PoolType, TakerSide } from "../types";
2 | import Big from "big.js";
3 | import BN from "bn.js";
4 |
5 | export const HUNDRED_PCT_BPS = 100_00;
6 | // 0.1% seems to be enough to deal with truncation divergence b/w off-chain and on-chain.
7 | const EXPO_SLIPPAGE = 0.001;
8 |
9 | export type ComputePriceArgs = {
10 | config: PoolConfig;
11 | takerSellCount: number;
12 | takerBuyCount: number;
13 | takerSide: TakerSide;
14 |
15 | //v1.1: for selling, (statsTakerSellCount - statsTakerBuyCount) < maxTakerSellCount (o/w no taking).
16 | maxTakerSellCount: number;
17 | statsTakerSellCount: number;
18 | //for bids this is 0, only relevant for MM orders
19 | statsTakerBuyCount: number;
20 |
21 | //single "extra" selection field, instead of 2 (nftsSelectedToBuy / nftsSelectedToSell)
22 | //that's because for Trade pools we don't want user's selection in buy tab to influence price in sell tab and vv
23 | //takerSide basically decides which way we add it
24 | extraNFTsSelected: number;
25 |
26 | // In addition to your standard slippage,
27 | // for exponential prices, we MUST add a small tolerance/slippage
28 | // since on-chain and off-chain rounding is not exactly the same.
29 | // 0.01 = 1%.
30 | slippage?: number;
31 |
32 | //indicate if the pool is marginated or not
33 | marginated: boolean;
34 | };
35 |
36 | // This is what should be displayed to the user ((!) no slippage, since slippage is only used for rounding errors).
37 | // In contrast, computeTakerPrice is what should be passed to the ix itself (has some noise slippage).
38 | export const computeTakerDisplayPrice = (args: ComputePriceArgs) => {
39 | // Explicitly set slippage to 0.
40 | return computeTakerPrice({ ...args, slippage: 0 });
41 | };
42 |
43 | // This includes MM fees (when taker is selling into a trade pool).
44 | // This should be used when computing deposit amounts + display (see computeTakerDisplayPrice) and nothing else.
45 | // (doesn't take into account mm fees).
46 | export const computeTakerPrice = (args: ComputePriceArgs): Big | null => {
47 | let currentPrice = computeCurrentPrice(args);
48 | if (currentPrice === null) return null;
49 |
50 | let priceWithMMFees = currentPrice;
51 | if (
52 | args.config.poolType === PoolType.Trade &&
53 | args.takerSide === TakerSide.Sell
54 | ) {
55 | priceWithMMFees = priceWithMMFees.sub(
56 | priceWithMMFees.mul(args.config.mmFeeBps ?? 0).div(HUNDRED_PCT_BPS)
57 | );
58 | }
59 |
60 | return priceWithMMFees;
61 | };
62 |
63 | // Computes the current (base) price of a pool (WITHOUT MM FEES),
64 | // optionally with slippage (so minPrice for Sell, maxPrice for Buy).
65 | // Note even w/ 0 slippage this price will differ from the on-chain current price
66 | // for Exponential curves b/c of rounding differences.
67 | // Will return null if price is neagtive (ie cannot sell anymore).
68 | const computeCurrentPrice = ({
69 | config,
70 | takerSellCount,
71 | takerBuyCount,
72 | takerSide,
73 | maxTakerSellCount,
74 | statsTakerSellCount,
75 | statsTakerBuyCount,
76 | extraNFTsSelected,
77 | // Default small tolerance for exponential curves.
78 | slippage = config.curveType === CurveType.Linear ? 0 : EXPO_SLIPPAGE,
79 | marginated,
80 | }: ComputePriceArgs): Big | null => {
81 | // Cannot sell anymore into capped pool.
82 | if (
83 | takerSide === TakerSide.Sell &&
84 | marginated &&
85 | maxTakerSellCount != 0 &&
86 | statsTakerSellCount - statsTakerBuyCount >= maxTakerSellCount
87 | ) {
88 | return null;
89 | }
90 |
91 | let basePrice = (() => {
92 | switch (config.poolType) {
93 | case PoolType.Token:
94 | return _shiftPriceByDelta(
95 | config.curveType,
96 | config.startingPrice,
97 | config.delta,
98 | "down",
99 | takerSellCount + extraNFTsSelected
100 | );
101 | case PoolType.NFT:
102 | return _shiftPriceByDelta(
103 | config.curveType,
104 | config.startingPrice,
105 | config.delta,
106 | "up",
107 | takerBuyCount + extraNFTsSelected
108 | );
109 | case PoolType.Trade:
110 | const isSelling = takerSide === TakerSide.Sell;
111 | const offset = isSelling ? 1 : 0;
112 | const modSellCount =
113 | takerSellCount + offset + +isSelling * extraNFTsSelected;
114 | const modBuyCount =
115 | takerBuyCount + (1 - +isSelling) * extraNFTsSelected;
116 | if (modBuyCount > modSellCount) {
117 | return _shiftPriceByDelta(
118 | config.curveType,
119 | config.startingPrice,
120 | config.delta,
121 | "up",
122 | modBuyCount - modSellCount
123 | );
124 | } else {
125 | return _shiftPriceByDelta(
126 | config.curveType,
127 | config.startingPrice,
128 | config.delta,
129 | "down",
130 | modSellCount - modBuyCount
131 | );
132 | }
133 | }
134 | })();
135 |
136 | if (basePrice.lt(0)) return null;
137 |
138 | basePrice = basePrice.mul(
139 | 1 + (takerSide === TakerSide.Buy ? 1 : -1) * slippage
140 | );
141 |
142 | return basePrice;
143 | };
144 |
145 | const _shiftPriceByDelta = (
146 | curveType: CurveType,
147 | startingPrice: Big,
148 | delta: Big,
149 | direction: "up" | "down",
150 | times: number
151 | ): Big => {
152 | switch (curveType) {
153 | case CurveType.Exponential:
154 | switch (direction) {
155 | // price * (1 + delta)^trade_count
156 | case "up":
157 | return startingPrice.mul(
158 | new Big(1).add(delta.div(HUNDRED_PCT_BPS)).pow(times)
159 | );
160 | case "down":
161 | return startingPrice.div(
162 | new Big(1).add(delta.div(HUNDRED_PCT_BPS)).pow(times)
163 | );
164 | }
165 | break;
166 | case CurveType.Linear:
167 | switch (direction) {
168 | case "up":
169 | return startingPrice.add(delta.mul(times));
170 | case "down":
171 | return startingPrice.sub(delta.mul(times));
172 | }
173 | }
174 | };
175 |
176 | // Use this to figure out (from the maker perspective):
177 | // (1) desired = count - how much SOL lamports (totalAmount) required to sell/buy `count`
178 | // (2) desired = total - how many NFTs (allowedCount) one can sell/buy with `total`
179 | // What's special about this fn is we add a bit of negative slippage exponential `totalAmount`.
180 | // This ensures that the maker deposits more than enough (for rounding issues).
181 | export const computeMakerAmountCount = ({
182 | desired,
183 | maxCountWhenInfinite = 1000,
184 | ...priceArgs
185 | }: Omit & {
186 | desired: { count: number } | { total: BN };
187 | // Necessary since when price = 0 or when selling (lin or exp), this can be infinity.
188 | // allowedCount can still exceed this if it can be computed & is finite.
189 | maxCountWhenInfinite?: number;
190 | }): {
191 | totalAmount: BN;
192 | allowedCount: number;
193 | initialPrice: BN | null;
194 | } => {
195 | const currPriceArgs = {
196 | ...priceArgs,
197 | slippage: 0,
198 | };
199 | const initTakerPrice = computeTakerPrice(currPriceArgs);
200 | if (!initTakerPrice) {
201 | return { totalAmount: new BN(0), allowedCount: 0, initialPrice: null };
202 | }
203 |
204 | const initialPrice = new BN(initTakerPrice.round().toString());
205 |
206 | // For calculations, we need to apply MM fees AFTER we sum things up (for sells).
207 | const initTakerPriceNoMM = computeTakerPrice({
208 | ...currPriceArgs,
209 | config: {
210 | ...currPriceArgs.config,
211 | mmFeeBps: 0,
212 | },
213 | })!;
214 |
215 | const { takerSide, config } = priceArgs;
216 |
217 | /*
218 | Constants:
219 | p = initial price
220 | d = delta
221 | T = total amount
222 | n = allowed count
223 | +/- = plus (buying) / minus (selling)
224 |
225 | Linear:
226 | Solving for T: T = p +/- n(n-1)d/2
227 | Solving for n:
228 | BUYS (https://www.wolframalpha.com/input?i=solve+for+x+in+T+%3Dxp+%2B+x%28x-1%29d%2F2):
229 | only need positive root
230 | n = ( sqrt((d-2p)^2 + 8dT) + d - 2p ) / (2d)
231 | SELLS, (https://www.wolframalpha.com/input?i=solve+for+x+in+T+%3Dxp+-+x%28x-1%29d%2F2):
232 | only need negative root
233 | if (d-2p)62 - 8dT < 0 (ie we can buy until negative prices):
234 | n = maxCountWhenInfinite (see above)
235 | else:
236 | n = ( - sqrt((d-2p)^2 - 8dT) + d + 2p ) / (2d)
237 |
238 | Exponential:
239 | r = (1 +/- delta)
240 | Solving for T: T = p (1 - r^n) / (1 - r)
241 | Solving for n:
242 | BUYS: (https://www.wolframalpha.com/input?i=solve+for+x+in+T+%3D+p%281-r%5Ex%29+%2F+%281-r%29)
243 | n = log[(r - 1)T/p + 1] / log(r)
244 | SELLS: infinite (can always sell at 0)
245 | */
246 |
247 | /// Clips allowed count by taking into account maxTakerSellCount cap.
248 | const adjustByMaxTakerCount = (allowedCount: number) => {
249 | if (takerSide !== TakerSide.Sell) return allowedCount;
250 | if (!priceArgs.marginated || priceArgs.maxTakerSellCount === 0)
251 | return allowedCount;
252 |
253 | // Negative should be fine. Eg taker sold 3, bought 5, adjTakerSellCount for pool is -2
254 | // if hook will never trigger, and subtracting a negative gives a positive (capped at allowedCount)
255 | const adjMaxTakerSellCount =
256 | priceArgs.statsTakerSellCount - (priceArgs.statsTakerBuyCount ?? 0);
257 |
258 | if (adjMaxTakerSellCount >= priceArgs.maxTakerSellCount) return 0;
259 | return Math.min(
260 | priceArgs.maxTakerSellCount - adjMaxTakerSellCount,
261 | allowedCount
262 | );
263 | };
264 |
265 | /// `undo` = add MM fee back; otherwise subtract MM fee.
266 | const adjustTotalMMFee = (total: Big, undo: boolean = false) => {
267 | if (config.poolType !== PoolType.Trade) return total;
268 | if (takerSide !== TakerSide.Sell) return total;
269 |
270 | const feePct = new Big(HUNDRED_PCT_BPS)
271 | .minus(config.mmFeeBps ?? 0)
272 | .div(HUNDRED_PCT_BPS);
273 |
274 | if (undo) {
275 | // Shouldn't be possible (mmFeeBps < 100_000), but if just return total.
276 | if (feePct.eq(0)) return total;
277 | return total.div(feePct);
278 | }
279 | return total.mul(feePct);
280 | };
281 |
282 | /// This is how many times to price can decrease when selling before we reach 0.
283 | /// Specifically for sell,
284 | /// k = # of times we decrement delta before hitting 0 price
285 | /// p = initial price
286 | /// d = delta
287 | /// p - kd >= 0 --> k <= p / d
288 | /// So delta count = 1 + k
289 | /// Buying we just take desired count.
290 | const getMaxSellCountLinear = () => {
291 | if (initTakerPriceNoMM.eq(0)) {
292 | if (config.delta.eq(0)) return maxCountWhenInfinite;
293 | return 1;
294 | }
295 |
296 | if (config.delta.eq(0)) return maxCountWhenInfinite;
297 |
298 | return (
299 | 1 +
300 | initTakerPriceNoMM.div(config.delta).round(0, Big.roundDown).toNumber()
301 | );
302 | };
303 |
304 | const getTotalAmountLinear = (desiredCount: number) => {
305 | const allowedCount = adjustByMaxTakerCount(
306 | takerSide === TakerSide.Buy
307 | ? desiredCount
308 | : Math.min(desiredCount, getMaxSellCountLinear())
309 | );
310 |
311 | // This is basically an arithmetic series:
312 | // T = p + (p +/- d) + p +/- 2d) + ... + (p +/- (n-1) d)
313 | // = p +/- n(n-1)d/2
314 | const base = initTakerPriceNoMM.mul(allowedCount);
315 | const deltas = config.delta.mul(allowedCount * (allowedCount - 1)).div(2);
316 | const totalAmount =
317 | takerSide === TakerSide.Buy ? base.add(deltas) : base.minus(deltas);
318 |
319 | return {
320 | allowedCount,
321 | totalAmount: new BN(adjustTotalMMFee(totalAmount).round().toString()),
322 | };
323 | };
324 |
325 | const getRateExp = () => {
326 | const base = 1 + config.delta.div(HUNDRED_PCT_BPS).toNumber();
327 | if (takerSide === TakerSide.Buy) return base;
328 | return 1 / base;
329 | };
330 |
331 | const getTotalAmountExp = (count: number) => {
332 | /*
333 | Exponential = geometric series.
334 | We have:
335 | r = decay (1 + delta for buy, 1 - delta for sell)
336 | Thus:
337 | T = p + pr + pr^2 + ... + pr^(n-1)
338 | = p * geosum(r, n-1)
339 | = p * (1 - r^n) / (1 - r), r != 1
340 | */
341 | const allowedCount = adjustByMaxTakerCount(count);
342 | const r = getRateExp();
343 |
344 | const geosum = (1 - Math.pow(r, allowedCount)) / (1 - r);
345 | const totalAmount = initTakerPriceNoMM.mul(geosum);
346 |
347 | return {
348 | allowedCount,
349 | // Negative slippage.
350 | totalAmount: new BN(
351 | adjustTotalMMFee(totalAmount)
352 | .mul(1 + EXPO_SLIPPAGE)
353 | .round()
354 | .toString()
355 | ),
356 | };
357 | };
358 |
359 | // delta = 0 when exp -> linear (degenerate) (o/w getRateExp will return 1 rate -> divide by 0s)
360 | const isLinear = config.curveType === CurveType.Linear || config.delta.eq(0);
361 |
362 | // ====================== By count
363 |
364 | if ("count" in desired) {
365 | if (isLinear) {
366 | const { allowedCount, totalAmount } = getTotalAmountLinear(desired.count);
367 | return { totalAmount, allowedCount, initialPrice };
368 | } else {
369 | const { allowedCount, totalAmount } = getTotalAmountExp(desired.count);
370 | return { totalAmount, allowedCount, initialPrice };
371 | }
372 | }
373 |
374 | // ====================== By total
375 |
376 | // Since our series are done PRE-MM fee, we need to "undo" the MM fee for our
377 | // desired total first, o/w it won't add up.
378 | const total = adjustTotalMMFee(new Big(desired.total.toString()), true);
379 | if (total.lt(initTakerPriceNoMM)) {
380 | return { totalAmount: new BN(0), allowedCount: 0, initialPrice };
381 | }
382 |
383 | if (isLinear) {
384 | const twoP = initTakerPriceNoMM.mul(2);
385 | // These are the a, b, c in the quadratic formula.
386 | const fourAC = total.mul(config.delta).mul(8);
387 | const twoA = config.delta.mul(2);
388 |
389 | let tempCount: number;
390 | if (initTakerPriceNoMM.eq(0)) {
391 | // Protects against divide by 0.
392 | tempCount = maxCountWhenInfinite;
393 | } else if (config.delta.eq(0)) {
394 | // Protects against divide by 0.
395 | tempCount = total
396 | .div(initTakerPriceNoMM)
397 | .round(0, Big.roundDown)
398 | .toNumber();
399 | } else {
400 | if (takerSide === TakerSide.Buy) {
401 | const bSquared = config.delta.minus(twoP).pow(2);
402 | // n = ( sqrt((d-2p)^2 + 8dT) + d - 2p ) / (2d)
403 | tempCount = bSquared
404 | .add(fourAC)
405 | .sqrt()
406 | .add(config.delta)
407 | .minus(twoP)
408 | .div(twoA)
409 | .round(0, Big.roundDown)
410 | .toNumber();
411 | } else {
412 | // if (d-2p)^2 - 8dT < 0 (ie we can buy until negative prices):
413 | // n = maxCountWhenInfinite (see above)
414 | // else:
415 | // n = ( - sqrt((d+2p)^2 - 8dT) + d + 2p ) / (2d)
416 | const bSquared = config.delta.plus(twoP).pow(2);
417 | const operand = bSquared.minus(fourAC);
418 | if (operand.lt(0)) {
419 | tempCount = getMaxSellCountLinear();
420 | } else {
421 | tempCount = operand
422 | .sqrt()
423 | .mul(-1)
424 | .add(config.delta)
425 | .add(twoP)
426 | .div(twoA)
427 | .round(0, Big.roundDown)
428 | .toNumber();
429 | }
430 | }
431 | }
432 | tempCount = Math.max(0, tempCount);
433 | const { allowedCount, totalAmount } = getTotalAmountLinear(tempCount);
434 | return { allowedCount, totalAmount, initialPrice };
435 | }
436 |
437 | // Exponential.
438 | // n = log[(r - 1)T/p + 1] / log(r), r != 1
439 | const r = getRateExp();
440 | let tempCount: number;
441 | if (initTakerPriceNoMM.eq(0)) {
442 | // Protects against divide by 0.
443 | tempCount = maxCountWhenInfinite;
444 | } else {
445 | const operand = total
446 | .mul(r - 1)
447 | .div(initTakerPriceNoMM)
448 | .add(1)
449 | .toNumber();
450 |
451 | if (operand <= 0) {
452 | // Only possible when r < 1 ie selling (since we can sell infinitely).
453 | tempCount = maxCountWhenInfinite;
454 | } else {
455 | tempCount = Math.floor(Math.log(operand) / Math.log(r));
456 | }
457 | }
458 | tempCount = Math.max(0, tempCount);
459 |
460 | const { allowedCount, totalAmount } = getTotalAmountExp(tempCount);
461 | return { allowedCount, totalAmount, initialPrice };
462 | };
463 |
--------------------------------------------------------------------------------
/src/tensorswap/types.ts:
--------------------------------------------------------------------------------
1 | import { PublicKey } from "@solana/web3.js";
2 | import Big from "big.js";
3 | import BN from "bn.js";
4 | import { CurveType, PoolConfig, PoolType } from "../types";
5 |
6 | // --------------------------------------- pool type
7 |
8 | export const PoolTypeAnchor = {
9 | Token: { token: {} },
10 | NFT: { nft: {} },
11 | Trade: { trade: {} },
12 | };
13 | type PoolTypeAnchor = typeof PoolTypeAnchor[keyof typeof PoolTypeAnchor];
14 |
15 | export const poolTypeU8 = (poolType: PoolTypeAnchor): 0 | 1 | 2 => {
16 | const order: Record = {
17 | token: 0,
18 | nft: 1,
19 | trade: 2,
20 | };
21 | return order[Object.keys(poolType)[0]];
22 | };
23 |
24 | export const castPoolTypeAnchor = (poolType: PoolTypeAnchor): PoolType =>
25 | ({
26 | 0: PoolType.Token,
27 | 1: PoolType.NFT,
28 | 2: PoolType.Trade,
29 | }[poolTypeU8(poolType)]);
30 |
31 | export const castPoolType = (poolType: PoolType): PoolTypeAnchor =>
32 | poolType === PoolType.NFT
33 | ? PoolTypeAnchor.NFT
34 | : poolType === PoolType.Token
35 | ? PoolTypeAnchor.Token
36 | : PoolTypeAnchor.Trade;
37 |
38 | // --------------------------------------- curve type
39 |
40 | export const CurveTypeAnchor = {
41 | Linear: { linear: {} },
42 | Exponential: { exponential: {} },
43 | };
44 |
45 | type CurveTypeAnchor = typeof CurveTypeAnchor[keyof typeof CurveTypeAnchor];
46 |
47 | export const curveTypeU8 = (curveType: CurveTypeAnchor): 0 | 1 => {
48 | const order: Record = {
49 | linear: 0,
50 | exponential: 1,
51 | };
52 | return order[Object.keys(curveType)[0]];
53 | };
54 |
55 | export const castCurveTypeAnchor = (curveType: CurveTypeAnchor): CurveType =>
56 | ({
57 | 0: CurveType.Linear,
58 | 1: CurveType.Exponential,
59 | }[curveTypeU8(curveType)]);
60 |
61 | export const castCurveType = (curveType: CurveType): CurveTypeAnchor =>
62 | curveType === CurveType.Linear
63 | ? CurveTypeAnchor.Linear
64 | : CurveTypeAnchor.Exponential;
65 |
66 | // --------------------------------------- config
67 |
68 | export type TSwapConfigAnchor = {
69 | feeBps: number;
70 | };
71 |
72 | export type PoolConfigAnchor = {
73 | poolType: PoolTypeAnchor;
74 | curveType: CurveTypeAnchor;
75 | startingPrice: BN;
76 | delta: BN;
77 | mmCompoundFees: boolean;
78 | mmFeeBps: number | null; // null for non-trade pools
79 | };
80 |
81 | export const castPoolConfigAnchor = (config: PoolConfigAnchor): PoolConfig => ({
82 | poolType: castPoolTypeAnchor(config.poolType),
83 | curveType: castCurveTypeAnchor(config.curveType),
84 | startingPrice: new Big(config.startingPrice.toString()),
85 | delta: new Big(config.delta.toString()),
86 | mmCompoundFees: config.mmCompoundFees,
87 | mmFeeBps: config.mmFeeBps,
88 | });
89 |
90 | export const castPoolConfig = (config: PoolConfig): PoolConfigAnchor => ({
91 | poolType: castPoolType(config.poolType),
92 | curveType: castCurveType(config.curveType),
93 | startingPrice: new BN(config.startingPrice.round().toString()),
94 | delta: new BN(config.delta.round().toString()),
95 | mmCompoundFees: config.mmCompoundFees,
96 | mmFeeBps: config.mmFeeBps,
97 | });
98 |
99 | // --------------------------------------- rest
100 |
101 | export enum OrderType {
102 | Standard = 0,
103 | Sniping = 1,
104 | }
105 |
106 | export type Frozen = {
107 | amount: BN;
108 | time: BN;
109 | };
110 |
111 | export type PoolStatsAnchor = {
112 | takerSellCount: number;
113 | takerBuyCount: number;
114 | accumulatedMmProfit: BN;
115 | };
116 |
117 | export type PoolAnchor = {
118 | version: number;
119 | bump: number[];
120 | solEscrowBump: number[];
121 | createdUnixSeconds: BN;
122 | config: PoolConfigAnchor;
123 | tswap: PublicKey;
124 | owner: PublicKey;
125 | whitelist: PublicKey;
126 | solEscrow: PublicKey;
127 | takerSellCount: number;
128 | takerBuyCount: number;
129 | nftsHeld: number;
130 | //v0.3
131 | nftAuthority: PublicKey;
132 | stats: PoolStatsAnchor;
133 | //v1.0
134 | margin: PublicKey | null;
135 | isCosigned: boolean;
136 | orderType: OrderType;
137 | frozen: Frozen | null;
138 | lastTransactedSeconds: BN;
139 | maxTakerSellCount: number;
140 | };
141 |
142 | export type SolEscrowAnchor = {};
143 | export type TSwapAnchor = {
144 | version: number;
145 | bump: number[];
146 | config: TSwapConfigAnchor;
147 | owner: PublicKey;
148 | feeVault: PublicKey;
149 | cosigner: PublicKey;
150 | };
151 |
152 | export type NftDepositReceiptAnchor = {
153 | bump: number;
154 | nftAuthority: PublicKey;
155 | nftMint: PublicKey;
156 | nftEscrow: PublicKey;
157 | };
158 |
159 | // --------------------------------------- state accounts
160 |
161 | export type NftAuthorityAnchor = {
162 | randomSeed: number[];
163 | bump: number[];
164 | pool: PublicKey;
165 | };
166 |
167 | export type MarginAccountAnchor = {
168 | owner: PublicKey;
169 | name: number[];
170 | nr: number;
171 | bump: number[];
172 | poolsAttached: number;
173 | };
174 |
175 | export type SingleListingAnchor = {
176 | owner: PublicKey;
177 | nftMint: PublicKey;
178 | price: BN;
179 | bump: number[];
180 | };
181 |
182 | // ----------- together
183 |
184 | export type TensorSwapPdaAnchor =
185 | | PoolAnchor
186 | | SolEscrowAnchor
187 | | TSwapAnchor
188 | | NftDepositReceiptAnchor
189 | | NftAuthorityAnchor
190 | | MarginAccountAnchor
191 | | SingleListingAnchor;
192 |
193 | export type TaggedTensorSwapPdaAnchor =
194 | | {
195 | name: "pool";
196 | account: PoolAnchor;
197 | }
198 | | {
199 | name: "solEscrow";
200 | account: SolEscrowAnchor;
201 | }
202 | | {
203 | name: "tSwap";
204 | account: TSwapAnchor;
205 | }
206 | | {
207 | name: "nftDepositReceipt";
208 | account: NftDepositReceiptAnchor;
209 | }
210 | | {
211 | name: "nftAuthority";
212 | account: NftAuthorityAnchor;
213 | }
214 | | {
215 | name: "marginAccount";
216 | account: MarginAccountAnchor;
217 | }
218 | | {
219 | name: "singleListing";
220 | account: SingleListingAnchor;
221 | };
222 |
--------------------------------------------------------------------------------
/src/token2022.ts:
--------------------------------------------------------------------------------
1 | import {
2 | getExtraAccountMetaAddress,
3 | getExtraAccountMetas,
4 | resolveExtraAccountMeta,
5 | } from "@solana/spl-token";
6 | import {
7 | AccountMeta,
8 | Commitment,
9 | Connection,
10 | PublicKey,
11 | TransactionInstruction,
12 | } from "@solana/web3.js";
13 |
14 | export async function getTransferHookExtraAccounts(
15 | connection: Connection,
16 | mint: PublicKey,
17 | instruction: TransactionInstruction,
18 | tansferHookProgramId: PublicKey,
19 | commitment?: Commitment
20 | ) {
21 | const address = getExtraAccountMetaAddress(mint, tansferHookProgramId);
22 | const account = await connection.getAccountInfo(address, commitment);
23 | const extraMetas: AccountMeta[] = [
24 | {
25 | pubkey: tansferHookProgramId,
26 | isSigner: false,
27 | isWritable: false,
28 | },
29 | ];
30 |
31 | // if we don't have the account, no extra accounts to add
32 | if (account == null) {
33 | return extraMetas;
34 | }
35 |
36 | for (const extraAccountMeta of getExtraAccountMetas(account)) {
37 | extraMetas.push(
38 | await resolveExtraAccountMeta(
39 | connection,
40 | extraAccountMeta,
41 | instruction.keys,
42 | instruction.data,
43 | instruction.programId
44 | )
45 | );
46 | }
47 |
48 | // add the extra account meta
49 | extraMetas.push({
50 | pubkey: address,
51 | isSigner: false,
52 | isWritable: false,
53 | });
54 |
55 | return extraMetas;
56 | }
57 |
58 | // ------------ WNS
59 |
60 | export const WNS_DISTRIBUTION_PROGRAM_ID = new PublicKey(
61 | "diste3nXmK7ddDTs1zb6uday6j4etCa9RChD8fJ1xay"
62 | );
63 | export const WNS_PROGRAM_ID = new PublicKey(
64 | "wns1gDLt8fgLcGhWi5MqAqgXpwEP1JftKE9eZnXS1HM"
65 | );
66 |
67 | export const getApprovalAccount = (mint: PublicKey) => {
68 | const [approvalAccount] = PublicKey.findProgramAddressSync(
69 | [Buffer.from("approve-account"), mint.toBuffer()],
70 | WNS_PROGRAM_ID
71 | );
72 |
73 | return approvalAccount;
74 | };
75 |
76 | export const getDistributionAccount = (
77 | collection: PublicKey,
78 | paymentMint: PublicKey = PublicKey.default
79 | ) => {
80 | const [distributionAccount] = PublicKey.findProgramAddressSync(
81 | [collection.toBuffer(), paymentMint.toBuffer()],
82 | WNS_DISTRIBUTION_PROGRAM_ID
83 | );
84 |
85 | return distributionAccount;
86 | };
87 |
88 | export const getApproveAccountLen = () => {
89 | // discriminator + slot
90 | return 8 + 8;
91 | };
92 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | import { PublicKey } from "@solana/web3.js";
2 | import Big from "big.js";
3 |
4 | //the side of the trade that the trader is taking
5 | export enum TakerSide {
6 | Buy = "Buy",
7 | Sell = "Sell",
8 | }
9 |
10 | export enum PoolType {
11 | NFT = "NFT",
12 | Token = "Token",
13 | Trade = "Trade",
14 | }
15 |
16 | export enum CurveType {
17 | Linear = "Linear",
18 | Exponential = "Exponential",
19 | }
20 |
21 | export type PoolConfig = {
22 | poolType: PoolType;
23 | curveType: CurveType;
24 | // TODO: THESE SHOULD BE BNs, OTHERWISE WE'LL RUN INTO ALL SORTS OF RUN TIME ERRORS.
25 | startingPrice: Big;
26 | delta: Big;
27 | mmCompoundFees: boolean;
28 | mmFeeBps: number | null; // null for non-trade pools
29 | };
30 |
31 | // Parsed account from a raw tx.
32 | export type ParsedAccount = {
33 | // See "getAccountByName" for name suffixes (these are the capitalized, space-separate names).
34 | name?: string | undefined;
35 | pubkey: PublicKey;
36 | isSigner: boolean;
37 | isWritable: boolean;
38 | };
39 |
40 | export type InstructionDisplay = {
41 | args: {
42 | name: string;
43 | type: string;
44 | data: string;
45 | }[];
46 | accounts: {
47 | name?: string;
48 | pubkey: PublicKey;
49 | isSigner: boolean;
50 | isWritable: boolean;
51 | }[];
52 | };
53 |
--------------------------------------------------------------------------------
/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["./src/**/*"],
3 | "compilerOptions": {
4 | "skipLibCheck": true,
5 | "lib": ["esnext", "dom"],
6 | "strict": true,
7 |
8 | "sourceMap": true,
9 | "declaration": true,
10 | "declarationMap": true,
11 | "allowSyntheticDefaultImports": true,
12 | "experimentalDecorators": true,
13 | "emitDecoratorMetadata": true,
14 | "noImplicitAny": false,
15 | "esModuleInterop": true,
16 | "resolveJsonModule": true,
17 | "composite": true,
18 | "baseUrl": ".",
19 | "typeRoots": ["node_modules/@types"]
20 | },
21 | }
22 |
--------------------------------------------------------------------------------
/tsconfig.cjs.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.base.json",
3 | "compilerOptions": {
4 | "module": "CommonJS",
5 | "target": "ES2022",
6 | "outDir": "dist/cjs/",
7 | "rootDir": "./src"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.base.json",
3 | "compilerOptions": {
4 | "moduleResolution": "node",
5 | "module": "ES2022",
6 | "target": "ES2022",
7 | "outDir": "dist/esm/",
8 | "rootDir": "./src"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/tsconfig.tests.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.base.json",
3 | "compilerOptions": {
4 | "module": "CommonJS",
5 | "target": "ES2022",
6 | "noEmit": true,
7 | "types": ["mocha"]
8 | },
9 | "include": ["src/**/*.ts", "tests/**/*.ts", "tests/**/*.json"]
10 | }
11 |
--------------------------------------------------------------------------------