💱

NFTどうしをSwapするライブラリ使ってみた

2022/04/16に公開

はじめに

NFTを売買するマーケットプレイスは非常に多いのですが、NFTを交換するようなツールがあまり出回っていなかったので作ってみました!

自作でのコントラクト開発も考えましたが、0x protocolを利用したSDKがあったのでこちらを利用してNFTのSwapを実装してみました。

https://github.com/trader-xyz/nft-swap-sdk

nft-swap-sdkの概要

NFTをp2pでswapするためのライブラリです。

対応チェーンはEthereum Mainnet, Polygon, Optimism, BSC, Fantom, and Celo, and Ropsten Testnetになっています。今回はRopsten Testnetを利用して実装しています。

また、公式曰くガス代も非常に削減されているようです。

交換できる規格はERC20・ERC721・ERC1155で、ERC721 <> ERC20、ERC1155 <> ERC20というように他の規格でも交換可能です。

交換用のオーダーの実装

交換するトークンの用意

本来であればMoralisOpenSeaAPI等を利用してNFTを取得したほうがいいですが、今回は事前にmintしたものを用意する形にしました。

import { UserFacingERC1155AssetDataSerializedNormalizedSingle } from "@traderxyz/nft-swap-sdk";

export const CHAIN_ID = 3;
export const NFT1: UserFacingERC1155AssetDataSerializedNormalizedSingle = {
  tokenAddress: '0x0000000000000000000000000000000000000000',
  tokenId: '0',
  type: 'ERC1155',
};

export const NFT2: UserFacingERC1155AssetDataSerializedNormalizedSingle = {
  tokenAddress: '0x0000000000000000000000000000000000000000',
  tokenId: '0',
  type: 'ERC1155',
};

export const walletAddressUserA = '0x0000000000000000000000000000000000000000';
export const walletAddressUserB = '0x0000000000000000000000000000000000000000';

初期化

事前にethers.js等を利用してprovidersignerを取得しておき、その後初期化を行います。

CHAIN_IDは先ほども述べましたがRopsten Testnetを使用しているので3になります。

const nftSwapSdk = new NftSwap(provider, signer, CHAIN_ID);

交換の承認

まず対象のNFTがSwapの承認を受けているかどうかを確認します。

const approvalStatusForUserA = await nftSwapSdk.loadApprovalStatus(
	NFT1,
	walletAddressUserA
);

もし、承認が完了していない場合、承認を行います。

if (!approvalStatusForUserA.contractApproved) {
	const approvalTx = await nftSwapSdk.approveTokenOrNftByAsset(
		NFT1,
		walletAddressUserA
	);
	const approvalTxReceipt = await approvalTx.wait();
}

署名

承認を行った後は署名を行います。

この時点で交換先のNFTの情報も必要になるので注意が必要です。

const order = nftSwapSdk.buildOrder(
	[NFT1],
	[NFT2],
	walletAddressUserA
);
const signedOrder = await nftSwapSdk.signOrder(order, walletAddressUserA);

また、このsignedOrderはSwapする際に必要なのでどこかに残しておく必要があります。

このデータの残し方については最適な方法をまだ見つけられていません(URLを発行してそこに載せるのが良さそう?それともDBに保存する?)

何か良いアイデアがあれば教えてください!!

Swapの実装

初期化

交換する側も同様に初期化を行います。

const nftSwapSdk = new NftSwap(_provider, signer, CHAIN_ID);

承認

オーダーを登録する場合と同様にNFTの承認をチェックしたのちに承認されていない場合は承認を行います。

const approvalStatusForUserB = await nftSwapSdk.loadApprovalStatus(
	NFT2,
	walletAddressUserB
)if (!approvalStatusForUserB.contractApproved) {
	const approvalTx = await nftSwapSdk.approveTokenOrNftByAsset(
		NFT2,
		walletAddressUserB
        );
	const approvalTxReceipt = await approvalTx.wait();
}

Swapの実行

最後にSwapを実行します。

const fillTx = await nftSwapSdk.fillSignedOrder(signedOrder, undefined, {gasLimit: '500000'});

const fillTxReceipt = await nftSwapSdk.awaitTransactionHash(fillTx.hash);

signedOrderはオーダー作成時の部分から取得してきました。

注意点としてはSwap時にgasLimitの制限を超えるので別途指定する必要があります。

結果はEtherscanで確認できます。

https://ropsten.etherscan.io/tx/0x9638271936a770e260b4330761bf52ab2a8aef0c281e96b4cc0bb5a37e68a734

最後にソースコードをまとめて載せておきます。

Swapのオーダー

const nftSwapSdk = new NftSwap(provider, signer, CHAIN_ID);

const approvalStatusForUserA = await nftSwapSdk.loadApprovalStatus(
	NFT1,
	walletAddressUserA
);

if (!approvalStatusForUserA.contractApproved) {
	const approvalTx = await nftSwapSdk.approveTokenOrNftByAsset(
		NFT1,
		walletAddressUserA
	);
	const approvalTxReceipt = await approvalTx.wait();
}

const order = nftSwapSdk.buildOrder(
	[NFT1],
	[NFT2],
	walletAddressUserA
);
const signedOrder = await nftSwapSdk.signOrder(order, walletAddressUserA);

Swapの実行

const nftSwapSdk = new NftSwap(_provider, signer, CHAIN_ID);

const approvalStatusForUserB = await nftSwapSdk.loadApprovalStatus(
	NFT2,
	walletAddressUserB
)if (!approvalStatusForUserB.contractApproved) {
	const approvalTx = await nftSwapSdk.approveTokenOrNftByAsset(
		NFT2,
		walletAddressUserB
	);
	const approvalTxReceipt = await approvalTx.wait();
}

const fillTx = await nftSwapSdk.fillSignedOrder(signedOrder, undefined, {gasLimit: '500000'});
const fillTxReceipt = await nftSwapSdk.awaitTransactionHash(fillTx.hash);

終わりに

SDKが非常に使いやすいため、比較的簡単に実装できました!

しかし、先に相手先の交換のNFTを選択する必要があるのでポケモンのGTS交換的なことは出来なさそうです(わかる人にはわかる)

sdkを利用して実装すると逆にcontractで実装するとどうやるんだろうという別の好奇心が湧いてきたのでまた独自コントラクトで実装してみたいです。

開発に関する質問は公式のdiscordがあるので追加で聞いてみたいことがあれば聞いてみてください!
https://discord.gg/RTvpQcxn4V

Twitterもやっていますので是非フォローしてください!
https://twitter.com/adachi_tomoki3

Discussion