はじめに
この記事では、Javaサーバーからweb3jを使用してユーザーの操作なしでNFTを転送する実装例を紹介します。
「ユーザーの操作なし」というのは、フロントエンド等でメタマスクの承認が不要ということを指しています。
あくまで「運営アカウントからユーザーに対してNFTを送り付ける」という一方的な形式なのでご了承ください。
自動エアドロップや報酬設計を検討している方にも参考になれば幸いです。
実装例
前提条件
・コントラクトの準備
web3jでコントラクトを操作するためにはコントラクトのjavaクラスファイルが必要です。
下記の記事を参考に用意してください。
【solidity】コントラクトのABIファイル及びjavaクラスファイルの作成方法
・秘密鍵の管理
非常に重要です。
ウォレットの秘密鍵が外部に漏れた場合、他人に残高の引き出しや不正利用を許してしまうことになるので十分な管理を徹底しましょう。
package com.example.restservice.service;
import java.math.BigInteger;
import org.springframework.stereotype.Service;
import org.web3j.crypto.Credentials;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameter;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.response.EthBlock;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.gas.StaticGasProvider;
// コントラクトのjavaクラスファイル読み込み
import ethereum.contractsontracts.SampleNftContract;
@Service
public class NftTransferService {
final static String TARGET_MAINNET_RPC = "https://rpc.target.net/";
// ※各種アドレスは仮のものです
final static String NFT_CONTRACT_ADDRESS = "0xabcdefg";
final static String OWNER_WALLET_ADDRESS = "0x123456789";
final static String PRIVATE_KEY="0123456789";
// targetNftId:コントラクト内のトークンID
// toAddress:NFT送信先となるアドレス
public void NftTransferMethod(int targetNftId, String toAddress){
try {
// Web3jインスタンスをRPCエンドポイントに接続
Web3j web3 = Web3j.build(new HttpService(TARGET_MAINNET_RPC));
// 秘密鍵から署名用のCredentialsを生成
Credentials credentials = Credentials.create(PRIVATE_KEY);
// 最新ブロック情報を取得
EthBlock.Block latestBlock = web3.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, false)
.send()
.getBlock();
// 最新ブロックのガスリミットを取得
BigInteger blockGasLimit = latestBlock.getGasLimit();
BigInteger gasLimit = blockGasLimit.subtract(BigInteger.valueOf(100000)); // 100000 gasの余裕を持たせる
BigInteger gasPrice = web3.ethGasPrice().send().getGasPrice(); // 最新のガス価格を取得
// NFTコントラクトのロード
SampleNftContract contract = SampleNftContract.load(
NFT_CONTRACT_ADDRESS,
web3,
credentials,
new StaticGasProvider(gasPrice, gasLimit)
);
// safeTransferFromトランザクションを実行
TransactionReceipt receipt = contract.safeTransferFrom(
OWNER_WALLET_ADDRESS,
toAddress,
BigInteger.valueOf(targetNftId)
).send();
// 成功・失敗を確認
if ("0x1".equals(receipt.getStatus())) {
System.out.println("NFT転送成功 トランザクションハッシュ: " + receipt.getTransactionHash());
}
else {
System.out.println("NFT転送失敗 ステータス: " + receipt.getStatus());
}
} catch (Exception e) {
System.out.println("NFT転送失敗 エラー:"+e);
}
}
}
簡単な解説
通常、NFTを第三者に転送するには、ユーザーがapproveやsetApprovalForAllを事前に行う必要があります。
しかしこのコードでは、NFTの所有者自身の秘密鍵を使って署名することで、承認プロセスを省略しています。
Credentials credentials = Credentials.create(PRIVATE_KEY);
このcredentialsを使ってコントラクトのsafeTransferFromを呼び出すことで、所有者本人が直接トランザクションを発行している扱いになります。
そのためユーザーのUI操作や署名は不要となり、サーバー側で自動的にNFTを送信可能というわけです。
注意点
- 本コードでは秘密鍵を直接使用しています。実運用では環境変数などで安全に管理してください。
- ユーザーの同意なしにNFTを移動させる設計は、ユースケースによっては規約違反やトラブルの原因となる可能性があるため、実装される環境を考慮してください。
- ガス価格は変動するため、適切な上限設定やリトライ処理を検討してください。