# 0x v4 Swap SDK

NFT Swap SDK now supports 0x Protocol v4! Try it out on Ethereum Mainnet today.

![0x v4 Integration into Swap SDK is here!](/files/YhY3uTCxN57IiBKBRLQG)

### Overview

#### What's new?

Swapping with the Swap SDK just got even better. With the new integration of 0x v4, **NFT trades that use the Swap SDK are the cheapest and most efficient swaps available on Ethereum.** Leverage the updated Swap SDK to ensure your users have the lowest possible gas fees anywhere. Read more about the gas optimizations below.

**0x v4 NFT is available on Ethereum Mainnet, Polygon, Optimism, BSC, Fantom, and Celo, and Ropsten Testnet.** Arbitrum will be supported very soon and just awaiting final deployment.

#### About Swap SDK

Swap SDK allows developers to build NFT swap functionality into their Ethereum (or EVM-compatible chains) applications quickly and easily. Whether you're building a wallet, an NFT marketplace, or a peer-to-peer swap application, Swap SDK makes integrating NFT swap functionality easy and lightweight. Just add your UI!

### ⛽ Gas Optimizations

> **0x v4 is the cheapest way to swap an NFT on Ethereum or any EVM-compatible chain.**

![](/files/ffoQPep7kKP59eMVuUpQ)

Whether you prefer on-chain or off-chain orders, 0x v4 is the cheapest and most efficient way to swap NFTs to date.

![Gas analysis as of 1/31/2022](/files/yYZXuhcatnPCGWMrbWVp)

Since Swap SDK uses 0x v4 protocol under the hood, the Swap SDK is the the most gas-efficient and cheapest way to swap NFTs on Ethereum. Build your app confidently knowing you're offering your users the best swapping experience available.

Based on recent gas benchmarks, 0x v4 is significantly cheaper to fill orders.

* **40% cheaper than OpenSea, LooksRare, and Rarible for buying NFTs**
* **35% cheaper than Zora to fill (>60% cheaper than Zora if including both parties' gas fees)**

0x v4 supports both on-chain listings and off-chain listings so you can choose the best approach for your application.

### 📩 Order Support

0x v4 initially includes a rich set of swap functionality. More swap functionality (e.g. bundles) will be added over time

* :white\_check\_mark: ERC721 <> ERC20 swap
* :white\_check\_mark: ERC1155 <> ERC20 swap

NFT buys and sells (bids and asks) are both supported.

![Analysis of Order support of leading NFT protocols](/files/pr5PUjG2CDo8IDm9f3I6)

Note: Currently 0x v4 does not support NFT<>NFT swaps (e.g. ERC721<>ERC721). If required, you can use the 0x v3 protocol until v4 support is added.

### Installation

You can install the SDK with yarn:

`yarn add @traderxyz/nft-swap-sdk`

or npm:

`npm install @traderxyz/nft-swap-sdk`

### Configuration

To use the SDK, create a new `NftSwapV4` instance.

```tsx
import { NftSwapV4 } from '@traderxyz/nft-swap-sdk';

// Supply a provider, signer, and chain id to get started
// Signer is optional if you only need read-only methods
const nftSwapSdk = new NftSwapV4(provider, signer, chainId);
```

### Quick Start

Let's walk through the most common NFT swap case: swapping an NFT (ERC721 or ERC1155) with an ERC20.

#### Swap an NFT with an ERC20

```typescript
import { NftSwapV4 } from '@traderxyz/nft-swap-sdk';

// Scenario: User A wants to sell their CryptoPunk for 420 WETH

// Set up the assets we want to swap (CryptoPunk #69 and 420 WETH)
const CRYPTOPUNK = {
  tokenAddress: '0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb',
  tokenId: '69',
  type: 'ERC721', // 'ERC721' or 'ERC1155'
};
const FOUR_HUNDRED_TWENTY_WETH = {
  tokenAddress: '0x6b175474e89094c44da98b954eedeac495271d0f', // WETH contract address
  amount: '420000000000000000000', // 420 Wrapped-ETH (WETH is 18 digits)
  type: 'ERC20',
};

// [Part 1: Maker (owner of the Punk) creates trade]
const nftSwapSdk = new NftSwapV4(provider, signerForMaker, CHAIN_ID);
const walletAddressMaker = '0x1234...';

// Approve NFT to trade (if required)
await nftSwapSdk.approveTokenOrNftByAsset(CRYPTOPUNK, walletAddressMaker);

// Build order
const order = nftSwapSdk.buildOrder(
  CRYPTOPUNK, // Maker asset to swap
  FOUR_HUNDRED_TWENTY_WETH, // Taker asset to swap
  walletAddressMaker
);
// Sign order so order is now fillable
const signedOrder = await nftSwapSdk.signOrder(order);

// [Part 2: Taker that wants to buy the punk fills trade]
const nftSwapSdk = new NftSwap(provider, signerForTaker, CHAIN_ID);
const walletAddressTaker = '0x9876...';

// Approve USDC to trade (if required)
await nftSwapSdk.approveTokenOrNftByAsset(FOUR_HUNDRED_TWENTY_WETH, walletAddressTaker);

// Fill order :)
const fillTx = await nftSwapSdk.fillSignedOrder(signedOrder);
const fillTxReceipt = await nftSwapSdk.awaitTransactionHash(fillTx.hash);
console.log(`🎉 🥳 Order filled. TxHash: ${fillTxReceipt.transactionHash}`)
```

That's it! More examples and advanced usage can be found in the examples documentation.

Happy swapping! :tada: :handshake:

#### About Swap SDK

Swap SDK is a light, performant library built with [`ethers`](https://github.com/ethers-io/ethers.js/) to easily interact with the 0x v4 protocol. Swap SDK also offers a free, managed orderbook so you don't need to worry about building off-chain order persistance (unless you want to). Swap SDK provides all the functionality to build an NFT marketplace or peer-to-peer swap application, including building orders, approving orders, persisting orders, and filling orders. Just add a UI!

Swap SDK is a library made by developers for developers. Swap SDK is fully funded by a 0x DAO grant. We plan to support and mature this library, as well as continue to open-source more tooling, so developers can integrate the SwapSDK confidently.


# Quick Start

Get started integrating the Swap SDK into your app

### Installation

You can install the SDK with yarn:

`yarn add @traderxyz/nft-swap-sdk`

or npm:

`npm install @traderxyz/nft-swap-sdk`

### Configuration

To use the SDK, create a new `NftSwapV4` instance.

```tsx
import { NftSwapV4 } from '@traderxyz/nft-swap-sdk';

// Supply a provider, signer, and chain id to get started
// Signer is optional if you only need read-only methods
const nftSwapSdk = new NftSwapV4(provider, signer, chainId);
```

Note: 0x v4 contracts with NFT support are currently live on **Ethereum Mainnet, Polygon, Optimism, BSC, Fantom, and Celo, and Ropsten Testnet.**

### Quick Start

Let's walk through the most common NFT swap case: swapping an NFT (ERC721 or ERC1155) with an ERC20.

#### Swap an NFT with an ERC20

```typescript
import { NftSwapV4 } from '@traderxyz/nft-swap-sdk';

// Scenario: User A wants to sell their CryptoPunk for 420 WETH

// Set up the assets we want to swap (CryptoPunk #69 and 420 WETH)
const CRYPTOPUNK = {
  tokenAddress: '0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb',
  tokenId: '69',
  type: 'ERC721', // 'ERC721' or 'ERC1155'
};
const FOUR_HUNDRED_TWENTY_WETH = {
  tokenAddress: '0x6b175474e89094c44da98b954eedeac495271d0f', // WETH contract address
  amount: '420000000000000000000', // 420 Wrapped-ETH (WETH is 18 digits)
  type: 'ERC20',
};

// [Part 1: Maker (owner of the Punk) creates trade]
const nftSwapSdk = new NftSwapV4(provider, signerForMaker, CHAIN_ID);
const walletAddressMaker = '0x1234...';

// Approve NFT to trade (if required)
await nftSwapSdk.approveTokenOrNftByAsset(CRYPTOPUNK, walletAddressMaker);

// Build order
const order = nftSwapSdk.buildOrder(
  CRYPTOPUNK, // Maker asset to swap
  FOUR_HUNDRED_TWENTY_WETH, // Taker asset to swap
  walletAddressMaker
);
// Sign order so order is now fillable
const signedOrder = await nftSwapSdk.signOrder(order);

// [Part 2: Taker that wants to buy the punk fills trade]
const nftSwapSdk = new NftSwap(provider, signerForTaker, CHAIN_ID);
const walletAddressTaker = '0x9876...';

// Approve USDC to trade (if required)
await nftSwapSdk.approveTokenOrNftByAsset(FOUR_HUNDRED_TWENTY_WETH, walletAddressTaker);

// Fill order :)
const fillTx = await nftSwapSdk.fillSignedOrder(signedOrder);
const fillTxReceipt = await nftSwapSdk.awaitTransactionHash(fillTx.hash);
console.log(`🎉 🥳 Order filled. TxHash: ${fillTxReceipt.transactionHash}`);
```

That's it! More examples and advanced usage can be found in the examples documentation.

Happy swapping! :tada: :handshake:


# Swap NFT <> ERC-20 Example

End-to-end example demonstrating swap a CryptoPunk for USDC

Let's swap a NFT (an [`ERC721`](https://eips.ethereum.org/EIPS/eip-721)) for USDC (an [`ERC20`](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/)).

In this first example, we're going to do an NFT<>ERC20 swap. We're going to swap User A's CryptoPunk NFT for 69 USDC.&#x20;

This is a common scenario on NFT marketplaces -- User A lists an NFT for sale in exchange for a certain amount of a token (in this example, 69 USDC). Anyone can then fill the order (i.e. buy the NFT that is listed for sale) as long as they have enough token balance.

> **Terminology**: `maker`: Since User A will initiate the trade, we'll refer to User A as the `maker` of the trade.

> **Terminology**: `taker`: Since User B will be filling and completing the trade created by User A, we'll refer to User B as the `taker` of the trade.

```tsx
// Setup the sample data...
const CHAIN_ID = 1; // Chain 1 corresponds to Mainnet. Visit https://chainid.network/ for a complete list of chain ids

const CRYPTOPUNK_420 = {
  tokenAddress: '0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb', // CryptoPunk contract address
  tokenId: '420', // Token Id of the CryptoPunk we want to swap
  type: 'ERC721', // Must be one of 'ERC20', 'ERC721', or 'ERC1155'
};

const SIXTY_NINE_USDC = {
  tokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC contract address
  amount: '69000000', // 69 USDC (USDC is 6 digits)
  type: 'ERC20',
};

// User A Trade Data
const walletAddressUserA = '0x1eeD19957E0a81AED9a80f09a3CCEaD83Ea6D86b';
const nftToSwapUserA = CRYPTOPUNK_420;

// User B Trade Data
const walletAddressUserB = '0x44beA2b43600eE240AB6Cb90696048CeF32aBf1D';
const usdcToSwapUserB = SIXTY_NINE_USDC;

// ............................
// Part 1 of the trade -- User A (the 'maker') initiates an order
// ............................

// Initiate the SDK for User A.
// Pass the user's wallet signer (available via the user's wallet provider) to the Swap SDK
const nftSwapSdk = new NftSwap(provider, signerUserA, CHAIN_ID);

// Check if we need to approve the NFT for swapping
const approvalStatusForUserA = await nftSwapSdk.loadApprovalStatus(
  nftToSwapUserA,
  walletAddressUserA
);
// If we do need to approve User A's CryptoPunk for swapping, let's do that now
if (!approvalStatusForUserA.contractApproved) {
  const approvalTx = await nftSwapSdk.approveTokenOrNftByAsset(
    nftToSwapUserA,
    makerAddress
  );
  const approvalTxReceipt = await approvalTx.wait();
  console.log(
    `Approved ${assetsToSwapUserA[0].tokenAddress} contract to swap with 0x v4 (txHash: ${approvalTxReceipt.transactionHash})`
  );
}

// Create the order (Remember, User A initiates the trade, so User A creates the order)
const order = nftSwapSdk.buildOrder(
  nftToSwapUserA,
  usdcToSwapUserB,
  walletAddressUserA
);
// Sign the order (User A signs since they are initiating the trade)
const signedOrder = await nftSwapSdk.signOrder(order);
// Part 1 Complete. User A is now done. Now we send the `signedOrder` to User B to complete the trade.

// ............................
// Part 2 of the trade -- User B (the 'taker') accepts and fills order from User A and completes trade
// ............................
// Initiate the SDK for User B.
const nftSwapSdk = new NftSwap(provider, signerUserB, CHAIN_ID);

// Check if we need to approve the NFT for swapping
const approvalStatusForUserB = await nftSwapSdk.loadApprovalStatus(
  usdcToSwapUserB,
  walletAddressUserB
);
// If we do need to approve NFT for swapping, let's do that now
if (!approvalStatusForUserB.contractApproved) {
  const approvalTx = await nftSwapSdk.approveTokenOrNftByAsset(
    usdcToSwapUserB,
    walletAddressUserB
  );
  const approvalTxReceipt = await approvalTx.wait();
  console.log(
    `Approved ${assetsToSwapUserB[0].tokenAddress} contract to swap with 0x. TxHash: ${approvalTxReceipt.transactionHash})`
  );
}
// The final step is the taker (User B) submitting the order.
// The taker approves the trade transaction and it will be submitted on the blockchain for settlement.
// Once the transaction is confirmed, the trade will be settled and cannot be reversed.
const fillTx = await nftSwapSdk.fillSignedOrder(signedOrder);
const fillTxReceipt = await nftSwapSdk.awaitTransactionHash(fillTx);
console.log(`🎉 🥳 Order filled. TxHash: ${fillTxReceipt.transactionHash}`);
```

### Trade Lifecycle

![Diagram of a order lifecycle being filled](/files/QYnLRMXhA7rDm9cJKOkN)


# Managing Orders

Build, approve, sign, fill, cancel and save orders with the Swap SDK.

### Building Orders

There are two ways to build orders with the 0x v4 Swap SDK, **`buildOrder`** and **`buildNftAndErc20Order`**&#x20;

**`buildOrder()`** accepts the traditional order format (specifying maker and taker assets).&#x20;

```typescript
import { NftSwapV4 } from '@traderxyz/nft-swap-sdk';

const nftSwapSdk = new NftSwapV4(provider, signer, chainId);

const CRYPTOPUNK_420 = {
  tokenAddress: '0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb', // CryptoPunk contract address
  tokenId: '420', // Token Id of the CryptoPunk we want to swap
  type: 'ERC721', // Must be one of 'ERC20', 'ERC721', or 'ERC1155'
};

const SIXTY_NINE_USDC = {
  tokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC contract address
  amount: '69000000', // 69 USDC (USDC is 6 digits)
  type: 'ERC20',
};

const walletAddressUserA = '0x1eeD19957E0a81AED9a80f09a3CCEaD83Ea6D86b';

const order = nftSwapSdk.buildOrder(
  nftToSwapUserA,
  usdcToSwapUserB,
  walletAddressUserA
);

```

**`buildNftAndErc20Order()`** accepts the new order format, where you specify an nft, an erc20 and a sell direction ('sell' if the maker of the trade is selling the nft, 'buy' if the maker of the trade is buying the nft)

```typescript
const order = nftSwapSdk.buildNftAndErc20Order(
  nftToSwapUserA,
  usdcToSwapUserB,
  'sell',
  walletAddressOfUserA
);
```

### Approving Orders

Approvals are required to move tokens and NFTs to and from accounts, and in general to fill orders. Before executing a trade, both parties will need to have approved the 0x v4 Exchange Contract.

To approve an asset, call the `approveTokenOrNftByAsset` function.

Pass it the NFT or ERC20 you need to approve&#x20;

```typescript
const CRYPTOPUNK = {
  tokenAddress: '0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb',
  tokenId: '69',
  type: 'ERC721', // Must be one of 'ERC20', 'ERC721', or 'ERC1155'
};

await nftSwapSdk.approveTokenOrNftByAsset(CRYPTOPUNK, walletAddress);
```

### Signing Orders

Once you've built an order, for it to be valid (and for it to be able to be filled by someone) it needs to be signed. The maker of the order signs the order, and the taker will fill the signed order.

After building an order via `buildOrder` or `buildNftAndErc20Order` dpass the order object to the `signOrder` function to sign and confirm your order. Once signed, this order is active and can be filled as long as it is valid.

```typescript
const signedOrder = await nftSwapSdk.signOrder(order);
```

### Filling an Order

Now that we have a signed order, another wallet can fill that order (known as 'taking' the trade).

To fill order, pass the signed order to the Swap SDK to the `fillSignedOrder` function.

If the transaction succeeds, the trade went through, and the user can be notified.

```typescript
const fillTx = await nftSwapperMaker.fillSignedOrder(signedOrder);
const txReceipt = await fillTx.wait();
console.log('Filled order! 🎉', txReceipt.transactionHash);
```

### Cancelling Orders

To cancel an order, call the `cancelOrder` function on the Swap SDK and pass it the order:

```typescript
await nftSwapSdk.cancelOrder(nonce, orderType); 
// Where `orderType` is either 'ERC721' | 'ERC1155'
// And `nonce` is from the order object
```

#### Advanced Cancellations

Being able to cancel by nonce allows us to do some cool things with regard to order cancellations.

Documentation coming soon

### Saving Orders

#### Orderbook / Order Persistance&#x20;

**Swap SDK offers integrators their own free, publicly hosted orderbook to use for their application.** This allows developers to persist orders off-chain without having to manage any additional infrastructure. Leverage the power of off-chain orders without any of the work!&#x20;

#### Save Order

To save an order to the hosted orderbook:

```typescript
await nftSwapSdk.postOrder(signedOrder);
```

#### Fetch Order(s)

To fetch order(s) from the orderbook, you can use the SDK.&#x20;

```typescript
// Search the orderbook for all offers to sell this NFT (CryptoCoven #9757)
const orders = await nftSwap.getOrders({
  nftToken: "0x5180db8f5c931aae63c74266b211f580155ecac8",
  nftTokenId: "9757",
  sellOrBuyNft: "sell", // Only show asks (sells) for this NFT (excludes asks)
});
 
// Or search by unique nonce
const orders = await nftSwap.getOrders({
  nonce: "0x31f42841c2db5173425b5223809cf3a38fede360",
});ype
```

Developers can always bring their own orderbook/order persistance infrastructure if they'd prefer. There is no lock in for using the hosted orderbook.


# Hosted Orderbook

Trader offers a free publicly hosted orderbook to manage your 0x v4 orders automatically. The orderbook handles order status, order fills

### Post an order:

To post an order to the Trader orderbook, use the SDK as follows:

```typescript
	
const order = nftSdk.buildOrder(
  // I am offering an NFT (CryptoCoven #9757)
  {
    type: "ERC721",
    tokenAddress: "0x5180db8f5c931aae63c74266b211f580155ecac8",
    tokenId: "9757",
  },
  // I will receive an ERC20 (5,000 of USDC)
  {
    type: "ERC20",
    tokenAddress: "0x31f42841c2db5173425b5223809cf3a38fede360",
    amount: "500000000000000", // 5000 USDC (5000 * 6 decimals)
  },
  // My wallet address
  "0xabc23F70Df4F45dD3Df4EC6DA6827CB05853eC9b"
);
 
const signedOrder = await nftSdk.signOrder(order);
 
const postedOrder = await nftSdk.postOrder(signedOrder, CHAIN_ID);
```

### Fetching Orders

```typescript
// Search the orderbook for all offers to sell this NFT (CryptoCoven #9757)
const orders = await nftSwap.getOrders({
  nftToken: "0x5180db8f5c931aae63c74266b211f580155ecac8",
  nftTokenId: "9757",
  chainId: "3",
});
 
// Or search by unique nonce
const orders = await nftSwap.getOrders({
  nonce: "0x31f42841c2db5173425b5223809cf3a38fede360",
});
 
const foundOrder = orders[0];
// Once you find an order, you can then fill it

await nftSwap.fillSignedOrder(foundOrder.order);
```

### Monitoring Orderbook Status

The status page for the orderbook can be found here: <https://status.trader.xyz>


# ETH/Native Token support

Sell an NFT for ETH. Buy an NFT with ETH.

Swap SDK & 0x v4 NFT fully support buying NFTs using ETH (or the native token if not on mainnet).&#x20;

Since ETH (or any native token on the EVM) is not technically an ERC20 (and doesn't have a 'token address'), we work around that by using a 'fake' address for ETH.

To require filling an order with ETH, hardcode the `tokenAddress` to `0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee` (the common ERC20 representation of ETH)

```
const orderWithEth = nftSwap.buildOrder(
  // NFT is for sale for    
  { type: 'ERC721', tokenAddress: '0xa0b8...', tokenId: '401' },
  // In exchange for 1 ETH  
  { type: 'ERC20', tokenAddress: '0xeeee...', amount: '1e18' },
  MAKER_WALLET_ADDRESS,
);
```

From there, you can fill this order as usual (e.g. `nftSwap.fillSignedOrder(...)`)

### Native Tokens

Native token can vary with the EVM chain, although the underlying concept remains the same. It is the primary token that is used to pay gas fees and other transaction fees.

Mainnet: `ETH`

Ropsten: `ETH`

Polygon: `MATIC`

Optimism: `OETH`

BSC: `BNB`


# Batch Buy NFTs

Buy multiple NFTs in a single transaction.

NFT Swap SDK supports buying multiple NFTs in an a single atomic transaction using 0x v4.

This can be useful if you're building a shopping-cart feature for your users. A user can select multiple NFTs they would like to purchase, and when they are ready to purchase, call the `batchBuyNfts` method to checkout.

### Usage&#x20;

To use the batch fill feature, pass in an array of signed orders the taker would like to fill.&#x20;

```typescript
const fillTx = await nftSwap.batchBuyNfts([
  signedOrder_1,
  signedOrder_2,
])
```

That's it!

#### Available batch fill options:

`revertIfIncomplete`: (boolean) Revert the transaction if only some (but not all) of the orders are filled. When set to `true` this is the equivalent of a `fillOrKill` type of order. Defaults to false.&#x20;

* Example: If only four of five provided orders are filled (the fifth one expired or was filled by someone else previously), this allows you to revert or continue with the transaction.

### Limitation

There are a limitations to be aware of when using the `batchBuyNfts` function

* Array of signed orders must be either all ERC721s or ERC1155s. They cannot be a mix of \[ERC721, ERC1155]. This is a constraint at the smart contract level.

Also, keep in mind this is only for NFT sell orders (i.e. the taker is buying NFTs). As such, only pass the `batchBuyNfts` NFT sell orders.&#x20;


# Royalties and Fee Configuration

0x v4 supports configurable royalties and fees.

0x v4 includes extremely flexible support for fees, both royalties to creators and fees for applications and marketplaces. 0x v4 even includes multiple fee support per order, so you could split fees however you'd like! It's up to you!

NFT marketplaces can now pay royalties in real-time at a lower cost so that creators no longer have to wait days or weeks to get paid. Marketplaces also have the option to send payouts to a contract that implements custom fee disbursement logic.

Fees unlock all sorts of use cases:

* Monetize applications,
* Royalties for creators or DAOs
* Reward user**s**

🔥 **Under 120k gas for a NFT swap with one fee, the cheapest on the EVM** 🔥

### Usage&#x20;

Fees can be specified by:

```ts
interface Fee {
  recipient: string // The address to send the fee to
  amount: string // The amount (based in the same erc20Token) to charge for fee
  feeData?: string | undefined; // optional feeData callback
}
```

Important notes:

* Buyer of the NFT pays the fee(s)
* Fees are in addition to the erc20TokenAmount that the buyer is paying for the NFT itself
* Can support multiple fees

### Example Code Implementing Fees

```typescript
const MAKER_ASSET: SwappableAsset = {
  type: 'ERC721',
  tokenAddress: TEST_NFT_CONTRACT_ADDRESS,
  tokenId: '11045',
};

const TAKER_ASSET: SwappableAsset = {
  type: 'ERC20',
  tokenAddress: USDC_TOKEN_ADDRESS,
  amount: '420000000000000', // 4200 USDC
};

const v4Erc721Order = nftSwap.buildOrder(
  MAKER_ASSET,
  TAKER_ASSET,
  MAKER_WALLET_ADDRESS,
  {
    fees: [
      {
        amount: '6900000000000', // 69 USDC fee
        recipient: '0xaaa1388cD71e88Ae3D8432f16bed3c603a58aD34', // your DAO treasury 
      },
    ],
  }
);
```

Docs here: <https://0x.org/docs/guides/0x-v4-nft-features-overview#fees>

Spec: For each Fee specified in an order, the buyer of the NFT will pay the fee recipient the given amount of ETH/ERC20 tokens. This is in addition to the erc20TokenAmount that the buyer is paying for the NFT itself. There is an optional callback for each fee:


# Collection-based/Floor-based orders

NFT Swap SDK supports Collection-based orders. Allows users to bid on any NFT from a specified collection.

Makers can create bids (orders) for any NFT from a specific collection.

#### Use Case Example

Let's say a user wants to buy *any* Bored Ape and they don't care which specific Ape they get. The user simply signs a collection order (shown below) agreeing to buy an NFT from the collection for a specified amount of an ERC20 token.&#x20;

#### Example Code:

Create collection-based or floor-based orders easily:

```typescript
// Maker creates an order for any NFT from a collection (you can think of it as a 'bid')
// Specifically in this example, the maker will sell 1000 USDC for any NFT in the collection specificed
const v4Erc721Order = nftSwapperMaker.buildCollectionBasedOrder(
  // Selling ERC20
  {
    type: "ERC20",
    tokenAddress: USDC_TOKEN_ADDRESS,
    amount: "100000000000000", // 1000 USDC
  },
  // Bidding on NFT in the collection, just specify the contract address and whether its an ERC721 or ERC1155.
  {
    tokenAddress: NFT_CONTRACT_ADDDRESS,
    type: "ERC721",
  },
  makerWalletAddress // Maker wallet address
)

const signedOrder = await nftSwapperMaker.signOrder(v4Erc721Order)

// Later, taker can sell an NFT from the specified collection, filling the bid.
const fillTx = await nftSwapperMaker.fillSignedCollectionOrder(
  signedOrder,
  "11045" // Token ID from the collection to fill order with
)
```


# Other Resources

NFT Workshop from ETHDenver 2022: <https://slides.com/johnj/nft-workshop>


# 0x v3 Swap SDK

Overview of Trader.xyz's swap library

## ![](/files/J1xPykWE9yGZDAWgTvnm)

## Swap SDK

The missing peer-to-peer swap library for Ethereum and EVM compatible chains, powered by the [0x protocol](https://0x.org), written in TypeScript for web3 developers. Trade tokens (ERC20s), NFTs, and other collectibles (ERC721 and ERC1155) with just a few lines of code. Seriously, easily trade anything on Ethereum with this library.

### Overview

tl;dr: NFT Swap SDK is the easiest, most-powerful swap library available on the EVM. Supports Ethereum and EVM-compatible chains (Polygon, Avalanche, BSC, etc..). Works in both browser and node.js. Written in TypeScript, built using the 0x protocol. With this library, you can build support for NFT marketplaces, over-the-counter (OTC) exchange, and/or peer-to-peer exchange.

The NFT Swap SDK developed by [Trader.xyz](https://trader.xyz) offers swap support for ERC20s, ERC721s, and ERC1155s. Exchange NFTs for NFTs, NFTs for ERC20 tokens, or bundles of NFTs and tokens. This library provides the ultimate swap flexibility combined with a simple API surface area so you can be productive immediately and focus on building your web3 app.

This library is powered and secured by the [0x v3 protocol](https://0x.org). The 0x v3 protocol has been in production for multiple years securing billions of dollars with of trades.

#### Goals

We want to share all underlying technology trader.xyz uses with the community. While we won't be open-sourcing our frontend, as we think design and UX is our differentiator, we believe in open-sourcing and freely sharing all underlying technology.

Our end goal is every piece of tech you see trader.xyz use (protocol, swap libraries, open-source orderbook, order monitor, high-performance NFT indexer, property-based orders, specific React hooks, and NFT aggregation) end up open-source. This library is the first step to achieving our goal.

### Installation

You can install the SDK with yarn:

`yarn add @traderxyz/nft-swap-sdk`

or npm:

`npm install @traderxyz/nft-swap-sdk`

### Configuration

To use the SDK, create a new NftSwap instance.

```tsx
import { NftSwap } from '@traderxyz/nft-swap-sdk';

// From your app, provide NftSwap the web3 provider, signer for the user's wallet, and the chain id.
const nftSwapSdk = new NftSwap(provider, signer, chainId);
```

Now you're setup and ready to use the SDK in your program. Check out the examples for how to swap with the library.

### Examples

#### Example 1: NFT <> NFT swap

In this first example, we're going to do a 1:1 NFT swap. We're going to swap User A's CryptoPunk NFT for User B's Bored Ape NFT.

> **Terminology**: `maker`: Since User A will initiate the trade, we'll refer to User A as the `maker` of the trade.

> **Terminology**: `taker`: Since User B will be filling and completing the trade created by User A, we'll refer to User B as the `taker` of the trade.

```tsx
// Setup the sample data...
const CHAIN_ID = 1; // Chain 1 corresponds to Mainnet. Visit https://chainid.network/ for a complete list of chain ids

const CRYPTOPUNK_420 = {
  tokenAddress: '0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb', // CryptoPunk contract address
  tokenId: '420', // Token Id of the CryptoPunk we want to swap
  type: 'ERC721', // Must be one of 'ERC20', 'ERC721', or 'ERC1155'
};

const BORED_APE_69 = {
  tokenAddress: '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D', // BAYC contract address
  tokenId: '69', // Token Id of the BoredApe we want to swap
  type: 'ERC721',
};

// User A Trade Data
const walletAddressUserA = '0x1eeD19957E0a81AED9a80f09a3CCEaD83Ea6D86b';
const assetsToSwapUserA = [CRYPTOPUNK_420];

// User B Trade Data
const walletAddressUserB = '0x44beA2b43600eE240AB6Cb90696048CeF32aBf1D';
const assetsToSwapUserB = [BORED_APE_69];

// ............................
// Part 1 of the trade -- User A (the 'maker') initiates an order
// ............................

// Initiate the SDK for User A.
// Pass the user's wallet signer (available via the user's wallet provider) to the Swap SDK
const nftSwapSdk = new NftSwap(provider, signerUserA, CHAIN_ID);

// Check if we need to approve the NFT for swapping
const approvalStatusForUserA = await nftSwapSdk.loadApprovalStatus(
  assetsToSwapUserA[0],
  walletAddressUserA
);
// If we do need to approve User A's CryptoPunk for swapping, let's do that now
if (!approvalStatusForUserA.contractApproved) {
  const approvalTx = await nftSwapSdk.approveTokenOrNftByAsset(
    assetsToSwapUserA[0],
    makerAddress
  );
  const approvalTxReceipt = await approvalTx.wait();
  console.log(
    `Approved ${assetsToSwapUserA[0].tokenAddress} contract to swap with 0x (txHash: ${approvalTxReceipt.transactionHash})`
  );
}

// Create the order (Remember, User A initiates the trade, so User A creates the order)
const order = nftSwapSdk.buildOrder(
  assetsToSwapUserA,
  assetsToSwapUserB,
  walletAddressUserA
);
// Sign the order (User A signs since they are initiating the trade)
const signedOrder = await nftSwapSdk.signOrder(order, takerAddress);
// Part 1 Complete. User A is now done. Now we send the `signedOrder` to User B to complete the trade.

// ............................
// Part 2 of the trade -- User B (the 'taker') accepts and fills order from User A and completes trade
// ............................
// Initiate the SDK for User B.
const nftSwapSdk = new NftSwap(provider, signerUserB, CHAIN_ID);

// Check if we need to approve the NFT for swapping
const approvalStatusForUserB = await nftSwapSdk.loadApprovalStatus(
  assetsToSwapUserB[0],
  walletAddressUserB
);
// If we do need to approve NFT for swapping, let's do that now
if (!approvalStatusForUserB.contractApproved) {
  const approvalTx = await nftSwapSdk.approveTokenOrNftByAsset(
    assetsToSwapUserB[0],
    walletAddressUserB
  );
  const approvalTxReceipt = await approvalTx.wait();
  console.log(
    `Approved ${assetsToSwapUserB[0].tokenAddress} contract to swap with 0x. TxHash: ${approvalTxReceipt.transactionHash})`
  );
}
// The final step is the taker (User B) submitting the order.
// The taker approves the trade transaction and it will be submitted on the blockchain for settlement.
// Once the transaction is confirmed, the trade will be settled and cannot be reversed.
const fillTx = await nftSwapSdk.fillSignedOrder(signedOrder);
const fillTxReceipt = await nftSwapSdk.awaitTransactionHash(fillTx);
console.log(`🎉 🥳 Order filled. TxHash: ${fillTxReceipt.transactionHash}`);
```

#### Example 2: Swap bundles -- Bundle of mixed ERC721s and ERC20 <> Bundle of ERC20s

Here we show an example of what the swap library is capable of. We can even swap arbitrary ERC tokens in bundles. We call it a bundle when we have more than one item that a party will swap. Bundles can have different ERC types within the same bundle.

In other words, we can swap `[ERC721, ERC1155, ERC20] <> [ERC721, ERC1155, ERC20]`. There's really no limit to what we can swap.

More concrete example: We can swap `[2 CryptoPunks and 1,000 DAI] for [420 WETH and 694,200 USDC]`. In this case we'd be swapping an `ERC721` and an `ERC20` (Punk NFT and DAI, respectively) for `two ERC20s` (WETH and USDC).

This is just one example. In reality, you can swap as many things as you'd like, any way you'd like. The underlying 0x protocol is extremely flexible, and the NFT swap library abstracts all the complexity away so you don't have to worry about protocol nuances.

```tsx
// Setup the sample data for the swap...
const CHAIN_ID = 1; // Mainnet

const CRYPTOPUNK_420 = {
  tokenAddress: '0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb',
  tokenId: '420',
  type: 'ERC721',
};

const CRYPTOPUNK_421 = {
  tokenAddress: '0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb',
  tokenId: '421',
  type: 'ERC721',
};

const ONE_THOUSAND_DAI = {
  tokenAddress: '0x6b175474e89094c44da98b954eedeac495271d0f', // DAI contract address
  amount: '1000000000000000000000', // 1,000 DAI (DAI is 18 digits) -- amount to swap
  type: 'ERC20',
};

const SIXTY_NINE_USDC = {
  tokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC contract address
  amount: '69000000', // 69 USDC (USDC is 6 digits)
  type: 'ERC20',
};

const FOUR_THOUSAND_TWENTY_WETH = {
  tokenAddress: '0x6b175474e89094c44da98b954eedeac495271d0f', // WETH contract address
  amount: '420000000000000000000', // 420 Wrapped-ETH (WETH is 18 digits)
  type: 'ERC20',
};

// User A Trade Data
const walletAddressUserA = '0x1eeD19957E0a81AED9a80f09a3CCEaD83Ea6D86b';
const assetsToSwapUserA = [CRYPTOPUNK_420, CRYPTOPUNK_421, ONE_THOUSAND_DAI];

// User B Trade Data
const walletAddressUserB = '0x44beA2b43600eE240AB6Cb90696048CeF32aBf1D';
const assetsToSwapUserB = [SIXTY_NINE_USDC, FOUR_THOUSAND_TWENTY_WETH];

// ............................
// Part 1 of the trade -- User A (the 'maker') initiates an order
// ............................
const nftSwapSdk = new NftSwap(provider, signerUserA, CHAIN_ID);
// Note: For brevity, we assume all assets are approved for swap in this example.
// See previous example on how to approve an asset.

const order = nftSwapSdk.buildOrder(
  assetsToSwapUserA,
  assetsToSwapUserB,
  walletAddressUserA
);
const signedOrder = await nftSwapSdk.signOrder(order, takerAddress);

// ............................
// Part 2 of the trade -- User B (the 'taker') accepts and fills order from User A and completes trade
// ............................
const nftSwapSdk = new NftSwap(provider, signerUserB, CHAIN_ID);

const fillTx = await nftSwapSdk.fillSignedOrder(signedOrder);
const fillTxReceipt = await nftSwapSdk.awaitTransactionHash(fillTx);
console.log(`🎉 🥳 Order filled. TxHash: ${fillTxReceipt.transactionHash}`);

// Not so bad, right? We can arbitrarily add more assets to our swap without introducing additional complexity!
```

#### Example 3: React Hooks + Swap SDK

In this example, we'll leverage the amazing [`web3-react`](https://github.com/NoahZinsmeister/web3-react) React Hook library.

```tsx
const App = () => {
  const { library, chainId } = useWeb3React<Web3React>();

  const [swapSdk, setSwapSdk] = useState(null);
  useEffect(() => {
    const sdk = new NftSwap(library, library.getSigner(), chainId);
    setSwapSdk(sdk);
  }, [library, chainId])

  // Use the SDK however you'd like in the app...
  const handleClick = useCallback(() => {
    if (!swapSdk) {
      return;
    }
    swapSdk.buildOrder(...)
  }, [swapSdk])

  // ...
}
```

### FAQ

* Which ERCs does this library support
  * ERC20, ERC721, and ERC1155
* What EVM chains are currently supported?
  * Mainnet (1)
  * Kovan (42)
  * Rinkeby (4)
  * Polygon (137)
  * Binance Smart Chain (56)
  * Avalance (43114)
* What protocol does this library?
  * trader.xyz and trader.xyz libraries are powered by 0x v3 Protocol. This protocol is mature and lindy, and has been extremely well-audited.
  * Check out the 0x v3 spec [here](https://github.com/0xProject/0x-protocol-specification/blob/master/v3/v3-specification.md)
  * Check out the 0x v3 Consensys audit [here](https://consensys.net/diligence/audits/2019/09/0x-v3-exchange/)
* Are there any protocol fees to execute swaps?
  * No
* How do I get the user's `signer` object?
  * Generally you can get it from the user's web3 wallet provider, by something like this: `provider.getSigner()`.
  * See this [ethers guide](https://docs.ethers.io/v4/cookbook-providers.html#metamask) (control-f for `getSigner`).
  * In web3-react you can do:
    * `const { library } = useWeb3React();`
    * `const signer = library.getSigner();`
* How do I store a `SignedOrder`
  * That's up to you. This library has no opinions on how to store orders. You can throw them in a centralized SQL database, save them to localstorage, use a decentralized messaging solution -- it's really up to you and your app concerns. You can even serialize and compress an order to fit in a tweet or shareable URL! 🤯

### Roadmap

We're currently working on the following features for the next iteration of this library

* Persistent data store of orders (off-the-shelf storage in trader.xyz's public order storage server). Think of it as a public good
* Property-based orders
* Order validation
* Live order status
* Order event streaming via websockets

If you have feature requests, reach out in our [Discord](https://discord.gg/GCf5rSX6).

We want to make this library a one-stop shop for all your NFT swapping needs.


# Orderbook API

Trader.xyz hosts a free, real-time NFT orderbook that hosts buy and sell NFT orders.

### Overview

Trader.xyz hosts the official orderbook for 0x v4 NFT orders.

Trader.xyz orderbook is an open orderbook that keeps track of off-NFT chain orders and order statuses in real-time. Anyone can add orders to the orderbook as long as they are valid 0x v4 orders.

Having an open orderbook for NFT orders makes it much easier for integrators to build NFT marketplaces and swapping apps -- bring your own frontend and leverage the trader infrastructure. No lock-in and the open orderbook is completely free to use!&#x20;

### Routes:

#### Get Orders

<mark style="color:blue;">`GET`</mark> `https://api.trader.xyz/orderbook/orders`

`Fetch NFT buy and sell orders that can be filled via 0x v4`

Use query params to filter for orders

#### Query Parameters

| Name         | Type   | Description                                                                                                                                 |               |           |             |           |
| ------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | --------- | ----------- | --------- |
| nftToken     | String | <p>Contract address for the NFT</p><p>(e.g. <code>0xed5...544</code> would filter for Azuki on mainnet</p>                                  |               |           |             |           |
| nftTokenId   | String | Token ID for the NFT                                                                                                                        |               |           |             |           |
| erc20Token   | String | <p>Contract address for the ERC20</p><p>(e.g. <code>0xa</code>...<code>06eb48</code> is USDC on mainnet)</p>                                |               |           |             |           |
| chainId      | String | Chain Id (<https://chainid.network/>)                                                                                                       |               |           |             |           |
| maker        | String | Maker wallet address                                                                                                                        |               |           |             |           |
| taker        | String | Taker wallet address                                                                                                                        |               |           |             |           |
| nonce        | String | Unique nonce for order                                                                                                                      |               |           |             |           |
| sellOrBuyNft | String | <p>Filter for either buys (bids) or sells (asks) of NFTs </p><p>Accepted filter values: 'sell' or 'buy'</p>                                 |               |           |             |           |
| status       | String | <p>Filter by real-time order status</p><p>Accepted values: 'open'                                                                           | 'filled'      | 'expired' | 'cancelled' | 'all'</p> |
| visibility   | String | <p>Filter by whether an order is public or private (private meaning the order has a specific taker address)</p><p>Accepted values: 'public' | 'private'</p> |           |             |           |
| offset       | String | <p>Offset fetching orders</p><p>Defaults to <code>0</code></p>                                                                              |               |           |             |           |
| limit        | String | <p>Amount of orders to fetch</p><p>Defaults to <code>200</code>. Max is <code>1000</code></p>                                               |               |           |             |           |

{% tabs %}
{% tab title="200: OK Returns an object which includes an `orders` field containing an array of orders" %}

```javascript
{
    "orders": [
      {
        "erc20Token": "0x31f42841c2db5173425b5223809cf3a38fede360",
        "erc20TokenAmount": "100000000000",
        "nftToken": "0x080ac75de7c348ae5898d6f03b894c6b2740179f",
        "nftTokenId": "1",
        "nftTokenAmount": "5",
        "nftType": "ERC1155",
        "sellOrBuyNft": "sell",
        "chainId": "3",
        "order": {
            "direction": 0,
            "erc20Token": "0x31f42841c2db5173425b5223809cf3a38fede360",
            "erc20TokenAmount": "100000000000",
            "erc1155Token": "0x080ac75de7c348ae5898d6f03b894c6b2740179f",
            "erc1155TokenId": "1",
            "erc1155TokenAmount": "5",
            "erc1155TokenProperties": [],
            "expiry": "2524604400",
            "fees": [],
            "maker": "0xabc23f70df4f45dd3df4ec6da6827cb05853ec9b",
            "nonce": "0x95cb442a6c40447397735b97a6265507",
            "signature": {
            "r": "0x40d064b246aaa46f7fc6f0b21d11329d62aa822b9ef0a848a64e68c12c25f8ee",
            "s": "0x74dac794840285584a30c88c37aaafe113642be3133651a375672b695c362861",
            "v": 27,
            "signatureType": 2
            },
            "taker": "0x0000000000000000000000000000000000000000"
        },
        "orderStatus": {
            "status": null,
            "transactionHash": null,
            "blockNumber": null
        },
        "metadata": {}
      },
      // ...more orders
    ]
}
```

{% endtab %}
{% endtabs %}

Upon finding an order you like. use the order field as the order object to fill on 0x v4.

```typescript
const nftOrders = await fetch(
  `https://api.trader.xyz/orderbook/orders?chainId=1&nftToken=0x5Af0D9827E0c53E4799BB226655A1de152A425a5&status=open`
).then(res => res.json())

// Find the first order
const nftOrder = nftOrders[0]
// Get the actual 0x v4 order that can be filled via the ExchangeProxy
const fillableZeroExOrder = nftOrder.order

// Fill order with a) Swap SDK, or b) ethers/exchange proxy directly:

// a) Fill via Swap SdK
const swapSdk = new SwapSdkV4(provider, signer);
const tx = await swapSdk.fillSignedOrder(fillableZeroExOrder);

// b) Fill via ExchangeProxy (you will need to set up the ExchangeProxy ABI via ethers)
// The function signature looks like this:
const tx = await exchangeProxy.buyERC721(
  fillableZeroExOrder,
  fillableZeroExOrder.signature,
  '0x',
);

```

### Posting orders

<mark style="color:green;">`POST`</mark> `https://api.trader.xyz/orderbook/order`

Add a signed 0x V4 NFT order to the open orderbook

#### Request Body

| Name                                      | Type   | Description                                                                                               |
| ----------------------------------------- | ------ | --------------------------------------------------------------------------------------------------------- |
| chainId<mark style="color:red;">\*</mark> | String | <p>Chain that the order is for</p><p>(e.g. <code>1</code> for mainnet or <code>137</code> for Polygon</p> |
| order<mark style="color:red;">\*</mark>   | String | Signed, Fillable 0x v4 NFT order                                                                          |

{% tabs %}
{% tab title="200: OK Order payload" %}

```javascript
{
  "erc20Token": "0x31f42841c2db5173425b5223809cf3a38fede360",
  "erc20TokenAmount": "100000000000",
  "nftToken": "0x080ac75de7c348ae5898d6f03b894c6b2740179f",
  "nftTokenId": "1",
  "nftTokenAmount": "5",
  "nftType": "ERC1155",
  "sellOrBuyNft": "sell",
  "chainId": "3",
  "order": {
    "direction": 0,
    "erc20Token": "0x31f42841c2db5173425b5223809cf3a38fede360",
    "erc20TokenAmount": "100000000000",
    "erc1155Token": "0x080ac75de7c348ae5898d6f03b894c6b2740179f",
    "erc1155TokenId": "1",
    "erc1155TokenAmount": "5",
    "erc1155TokenProperties": [],
    "expiry": "2524604400",
    "fees": [],
    "maker": "0xabc23f70df4f45dd3df4ec6da6827cb05853ec9b",
    "nonce": "0x95cb442a6c40447397735b97a6265507",
    "signature": {
      "r": "0x40d064b246aaa46f7fc6f0b21d11329d62aa822b9ef0a848a64e68c12c25f8ee",
      "s": "0x74dac794840285584a30c88c37aaafe113642be3133651a375672b695c362861",
      "v": 27,
      "signatureType": 2
    },
    "taker": "0x0000000000000000000000000000000000000000"
  },
  "orderStatus": {
    "status": null,
    "transactionHash": null,
    "blockNumber": null
  },
  "metadata": {}
}

```

{% endtab %}
{% endtabs %}


# V3 or V4 - Which version should I use?

Currently there are two versions of the NFT Swap SDK: [0x v3](https://github.com/0xProject/0x-protocol-specification/blob/master/v3/v3-specification.md) and [0x v4](https://protocol.0x.org/en/latest/).&#x20;

### Which version?

tl;dr:

By default, **we recommend using V4.** It's the future of the 0x protocol and is hyper gas-optimized (currently the cheapest way to buy/sell NFTs on Ethereum).

**One exception:** V4 currently does not support buying and selling bundles. If you require bundle support in the short term, you will need to use V3. Eventually V4 plans to be able to support bundles, but for now use V3.


