DN404(ERC404)のテスト
概要
前回の記事でDN404のローカル環境の構築と簡単な動作テストをしたので今回はテストコードを実装してDN404の動作確認を行う。
テストは独自クラスではなくインストールされた際に含まれている「MockDN404.sol」を使用して確認を行う。
手順
(1) SoladyTestをベースにテストクラスを作成する
前回DN404のインストールを行った際「SoladyTest」も含めてダウンロードできているはずなので、継承してテストクラスを作成する。
登場パラメータ:
dn: ERC20トークンクラス(以下、「トークン」)
mirror: ERC721クラス(以下、「NFT」)
alice: ユーザー1
bob: ユーザー2
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "./utils/SoladyTest.sol";
import {DN404, MockDN404} from "./utils/mocks/MockDN404.sol";
import {DN404Mirror} from "../src/DN404Mirror.sol";
contract MNFTDN404Test is SoladyTest {
MockDN404 dn;
DN404Mirror mirror;
uint256 private constant _WAD = 1000000000000000000;
uint256 initialSupply = 1000;
address alice = address(111);
address bob = address(222);
function setUp() public {
dn = new MockDN404();
mirror = new DN404Mirror(address(this));
}
:
:
}
(2) nameとsymbolのテスト
試しにnameとsymbolのテストコードを作成。
function testNameAndSymbol(string memory name, string memory symbol) public {
dn.initializeDN404(1000 * _WAD, address(this), address(mirror));
dn.setNameAndSymbol(name, symbol);
assertEq(mirror.name(), name);
assertEq(mirror.symbol(), symbol);
}
実行結果↓↓
% forge test --match-path test/MNFTDN404.t.sol -vvv
[⠊] Compiling...
[⠔] Compiling 1 files with 0.8.24
[⠒] Solc 0.8.24 finished in 463.95ms
Compiler run successful!
Ran 2 tests for test/MNFTDN404.t.sol:MNFTDN404Test
[PASS] testNameAndSymbol(string,string) (runs: 256, μ: 207165, ~: 207775)
[PASS] test__codesize() (gas: 21503)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 29.07ms (28.51ms CPU time)
Ran 1 test suite in 136.02ms (29.07ms CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests)
(2)DN404のテストコード
ファンクション:testMint
DN404が思った通りに動作しているかどうか、鋳造や移動を行ってERC20とERC721の数を確認する。
最大1000トークンのDN404を初期化。
4トークン分aliceに対して生成。
NFTのTokenID1、2がaliceにも生成されていることを確認(実際は4つNFTを持っている)。
bobのトークン数は0。
↓↓
dn.initializeDN404(1000 * _WAD, address(this), address(mirror));
dn.mint(alice, 4 * _WAD);
//nftは4つ
assertEq(mirror.balanceOf(alice), 4);
assertEq(mirror.ownerOf(1), alice);
assertEq(mirror.ownerOf(2), alice);
//bobのerc20は0
assertEq(dn.balanceOf(bob), 0);
aliceがbobにTokenID:2のNFTを移動。
bobがTokenID:2のNFTを所持していることを確認。
aliceのトークン数が3になってるはず。
bobのトークン数が1になっているはず。
↓↓
//id:2をbobに移動
vm.prank(alice);
mirror.transferFrom(alice, bob, 2);
assertEq(mirror.ownerOf(2), bob);
//aliceのerc20が3になってるはず
assertEq(dn.balanceOf(alice), 3 * _WAD);
//bobのerc20が1になってるはず
assertEq(dn.balanceOf(bob), 1 * _WAD);
bobからaliceに0.5トークン移動。
bobのトークンは0.5になっているはず。
bobのNFTは0になっているはず。(TokenID:2のNFTがバーンされた)
aliceのトークン数は3.5になっているはず。
aliceのNFTは3のまま。
↓↓
vm.prank(bob);
//bobから資金移動
dn.transfer(alice, _WAD/2);
//bobのerc20半分
assertEq(dn.balanceOf(bob), _WAD/2);
//bobのnft0
assertEq(mirror.balanceOf(bob), 0);
//aliceのerc20 = 3.5 * _WAD
assertEq(dn.balanceOf(alice), (3 * _WAD)+(_WAD/2));
//aliceのnft 3
assertEq(mirror.balanceOf(alice), 3);
aliceからbobに0.5トークンを再度移動。
bobのNFTが1になる。
bobが所持するNFTのTokenIDは5。
↓↓
//aliceからbobに0.5おくる。
vm.prank(alice);
dn.transfer(bob, _WAD/2);
//bobのnft = 1になる
assertEq(mirror.balanceOf(bob), 1);
//idは4まで。id:2がburnされ、新しいidが生成される。id:5になる。
assertEq(mirror.ownerOf(5), bob);
TokenID:2のNFTは存在しないことを確認。
↓↓
//id2はbobでは無くなってるはず
vm.expectRevert(DN404.TokenDoesNotExist.selector);
mirror.ownerOf(2);
ファンクション:testBurn
トークンの焼却に関してテスト。
トークン焼却できるが、NFTに焼却機能はついていなかった。
自分で実装する必要がある。
aliceの初期保有トークン数を4として初期化。
↓↓
dn.initializeDN404(1000 * _WAD, address(this), address(mirror));
dn.mint(alice, 4 * _WAD);
//nftは4つ
assertEq(mirror.balanceOf(alice), 4);
aliceのトークンを1つ焼却。
aliceの所持トークン数が3であること。
aliceの所持NFT数が3であること。
↓↓
//erc20 1バーン
dn.burn(alice, 1 * _WAD);
//ERC20: 3*_WAD, NFT: 3
assertEq(dn.balanceOf(alice), 3 * _WAD);
assertEq(mirror.balanceOf(alice), 3);
テストコード全体
下記にテスト全体を記載。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "./utils/SoladyTest.sol";
import {DN404, MockDN404} from "./utils/mocks/MockDN404.sol";
import {DN404Mirror} from "../src/DN404Mirror.sol";
contract MNFTDN404Test is SoladyTest {
MockDN404 dn;
DN404Mirror mirror;
uint256 private constant _WAD = 1000000000000000000;
uint256 initialSupply = 1000;
address alice = address(111);
address bob = address(222);
function setUp() public {
dn = new MockDN404();
mirror = new DN404Mirror(address(this));
}
function testNameAndSymbol(string memory name, string memory symbol) public {
dn.initializeDN404(1000 * _WAD, address(this), address(mirror));
dn.setNameAndSymbol(name, symbol);
assertEq(mirror.name(), name);
assertEq(mirror.symbol(), symbol);
}
function testTokenURI(string memory baseURI, uint256 id) public {
dn.initializeDN404(1000 * _WAD, address(this), address(mirror));
dn.setBaseURI(baseURI);
assertEq(mirror.tokenURI(id), string(abi.encodePacked(baseURI, id)));
}
function testMint() public {
dn.initializeDN404(1000 * _WAD, address(this), address(mirror));
dn.mint(alice, 4 * _WAD);
//nftは4つ
assertEq(mirror.balanceOf(alice), 4);
assertEq(mirror.ownerOf(1), alice);
assertEq(mirror.ownerOf(2), alice);
//bobのerc20は0
assertEq(dn.balanceOf(bob), 0);
//id:2をbobに移動
vm.prank(alice);
mirror.transferFrom(alice, bob, 2);
assertEq(mirror.ownerOf(2), bob);
//aliceのerc20が3になってるはず
assertEq(dn.balanceOf(alice), 3 * _WAD);
//bobのerc20が1になってるはず
assertEq(dn.balanceOf(bob), 1 * _WAD);
vm.prank(bob);
//bobから資金移動
dn.transfer(alice, _WAD/2);
//bobのerc20半分
assertEq(dn.balanceOf(bob), _WAD/2);
//bobのnft0
assertEq(mirror.balanceOf(bob), 0);
//aliceのerc20 = 3.5 * _WAD
assertEq(dn.balanceOf(alice), (3 * _WAD)+(_WAD/2));
//aliceのnft 3
assertEq(mirror.balanceOf(alice), 3);
//aliceからbobに0.5おくる。
vm.prank(alice);
dn.transfer(bob, _WAD/2);
//bobのnft = 1になる
assertEq(mirror.balanceOf(bob), 1);
//idは4まで。id:2がburnされ、新しいidが生成される。id:5になる。
assertEq(mirror.ownerOf(5), bob);
//id2はbobでは無くなってるはず
vm.expectRevert(DN404.TokenDoesNotExist.selector);
mirror.ownerOf(2);
}
function testBurn() public {
dn.initializeDN404(1000 * _WAD, address(this), address(mirror));
dn.mint(alice, 4 * _WAD);
//nftは4つ
assertEq(mirror.balanceOf(alice), 4);
//erc20 1バーン
dn.burn(alice, 1 * _WAD);
//ERC20: 3*_WAD, NFT: 3
assertEq(dn.balanceOf(alice), 3 * _WAD);
assertEq(mirror.balanceOf(alice), 3);
}
}
結論
トークンやNFTの移動に関しては想定通り動作している。
想定外だったのがNFTの焼却ができないこと。
トークンの焼却で対処するしかないが、結論としては同じ事なので特段問題はない。
NFTが生成できる分トークン移動すればNFTも移動される(今回の場合1:1)。
NFTが生成できない程度のトークン移動ではNFTは焼却される(今回の場合0.5のみ移動してみた)。
(2)の後生成されるNFTのTokenIDは新しくなる。
トークン焼却はできるがNFT焼却はできない(自分で実装する)。
残念なことに、複数のERC721が登録できない。
例えば一定数NFTミントしたら次から新しいNFTをミントする、と言うことができない。
複雑さの増加やガス代が高くなるため回避していると思われる。
その為、キャラクター毎のバージョン販売等といったNFTプロジェクトにDN404を使用することは不可能。
ただし、キャラクターNFTとの交換チケットとして作成することは可能。
ユーザーがDN404NFTを発行元に送信
DN404NFTを受領発行元はユーザーが指定したキャラクターNFTを送信
発行元はNFTと同価値のトークンを受領
もしくは
ユーザーが一定量のDN404トークンを発行元に送金
DN404トークンを受領した発行元はユーザーが指定したキャラクターNFTを送信
発行元は同価値のNFTを受領
推奨としては前者。
トークン数をチェックする必要がない。
次回はチケット交換コントラクトの実装を行う。
この記事が気に入ったらサポートをしてみませんか?