// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "forge-std/Test.sol"; import "../../contracts/dbis/IDBISTypes.sol"; import "../../contracts/dbis/DBIS_RootRegistry.sol"; import "../../contracts/dbis/DBIS_ParticipantRegistry.sol"; import "../../contracts/dbis/DBIS_SignerRegistry.sol"; import "../../contracts/dbis/DBIS_GRU_MintController.sol"; import "../../contracts/dbis/DBIS_EIP712Helper.sol"; import "../../contracts/dbis/DBIS_SettlementRouter.sol"; import "../../contracts/dbis/StablecoinReferenceRegistry.sol"; import "./MockMintableToken.sol"; contract DBIS_RailTest is Test, IDBISTypes { DBIS_RootRegistry public root; DBIS_ParticipantRegistry public participantReg; DBIS_SignerRegistry public signerReg; DBIS_GRU_MintController public mintController; DBIS_SettlementRouter public router; StablecoinReferenceRegistry public stableReg; MockMintableToken public token; address public admin; address public alice; address public signer1; address public signer2; address public signer3; uint256 signer1Key; uint256 signer2Key; uint256 signer3Key; function setUp() public { admin = makeAddr("admin"); alice = makeAddr("alice"); (signer1, signer1Key) = makeAddrAndKey("signer1"); (signer2, signer2Key) = makeAddrAndKey("signer2"); (signer3, signer3Key) = makeAddrAndKey("signer3"); vm.startPrank(admin); root = new DBIS_RootRegistry(admin, "v1"); participantReg = new DBIS_ParticipantRegistry(admin); signerReg = new DBIS_SignerRegistry(admin); mintController = new DBIS_GRU_MintController(admin, address(0)); router = new DBIS_SettlementRouter(admin, address(root), address(new DBIS_EIP712Helper())); stableReg = new StablecoinReferenceRegistry(admin); root.setComponent(keccak256("ParticipantRegistry"), address(participantReg)); root.setComponent(keccak256("SignerRegistry"), address(signerReg)); root.setComponent(keccak256("GRUMintController"), address(mintController)); mintController.setSettlementRouter(address(router)); token = new MockMintableToken("Test GRU", "tGRU", 6, admin); mintController.setGruToken(address(token)); token.grantRole(token.MINTER_ROLE(), address(mintController)); signerReg.addSigner(signer1, 0); signerReg.addSigner(signer2, 1); signerReg.addSigner(signer3, 2); address[] memory wallets = new address[](1); wallets[0] = alice; bytes32[] memory tags; participantReg.registerParticipant(DBIS_ParticipantRegistry.Participant({ participantId: keccak256("participant1"), legalName: "Test FI", jurisdiction: "US", entityType: DBIS_ParticipantRegistry.EntityType.BANK, status: DBIS_ParticipantRegistry.Status.ACTIVE, policyTags: tags, operationalWallets: wallets })); vm.stopPrank(); } function test_submitMintAuth_success() public { address[] memory recipients = new address[](1); recipients[0] = alice; uint256[] memory amounts = new uint256[](1); amounts[0] = 1000 * 1e6; MintAuth memory auth = MintAuth({ messageId: keccak256("msg1"), isoType: keccak256("pacs.008"), isoHash: keccak256("isobundle"), accountingRef: keccak256("acctref"), fundsStatus: FundsStatus.ON_LEDGER_FINAL, corridor: bytes32(0), assetClass: AssetClass.GRU_M00, recipients: recipients, amounts: amounts, notBefore: uint64(block.timestamp - 1), expiresAt: uint64(block.timestamp + 300), chainId: 138, verifyingContract: address(router) }); bytes32 digest = router.getMintAuthDigest(auth); bytes memory sig1 = _sign(digest, signer1Key); bytes memory sig2 = _sign(digest, signer2Key); bytes memory sig3 = _sign(digest, signer3Key); bytes[] memory sigs = new bytes[](3); sigs[0] = sig1; sigs[1] = sig2; sigs[2] = sig3; vm.prank(alice); router.submitMintAuth(auth, sigs); assertEq(token.balanceOf(alice), 1000 * 1e6); assertTrue(router.usedMessageIds(auth.messageId)); } function test_submitMintAuth_replayReverts() public { address[] memory recipients = new address[](1); recipients[0] = alice; uint256[] memory amounts = new uint256[](1); amounts[0] = 1000 * 1e6; MintAuth memory auth = MintAuth({ messageId: keccak256("msg2"), isoType: keccak256("pacs.008"), isoHash: keccak256("isobundle"), accountingRef: keccak256("acctref"), fundsStatus: FundsStatus.ON_LEDGER_FINAL, corridor: bytes32(0), assetClass: AssetClass.GRU_M00, recipients: recipients, amounts: amounts, notBefore: uint64(block.timestamp - 1), expiresAt: uint64(block.timestamp + 300), chainId: 138, verifyingContract: address(router) }); bytes32 digest = router.getMintAuthDigest(auth); bytes[] memory sigs = new bytes[](3); sigs[0] = _sign(digest, signer1Key); sigs[1] = _sign(digest, signer2Key); sigs[2] = _sign(digest, signer3Key); vm.prank(alice); router.submitMintAuth(auth, sigs); vm.expectRevert("DBIS: replay"); vm.prank(alice); router.submitMintAuth(auth, sigs); } function test_signerActiveAtBlock_afterRevoke() public { vm.prank(admin); signerReg.revokeSignerAtBlock(signer1); address[] memory recipients = new address[](1); recipients[0] = alice; uint256[] memory amounts = new uint256[](1); amounts[0] = 1000 * 1e6; MintAuth memory auth = MintAuth({ messageId: keccak256("msg3"), isoType: keccak256("pacs.008"), isoHash: keccak256("isobundle"), accountingRef: keccak256("acctref"), fundsStatus: FundsStatus.ON_LEDGER_FINAL, corridor: bytes32(0), assetClass: AssetClass.GRU_M00, recipients: recipients, amounts: amounts, notBefore: uint64(block.timestamp - 1), expiresAt: uint64(block.timestamp + 300), chainId: 138, verifyingContract: address(router) }); bytes32 digest = router.getMintAuthDigest(auth); bytes[] memory sigs = new bytes[](3); sigs[0] = _sign(digest, signer1Key); sigs[1] = _sign(digest, signer2Key); sigs[2] = _sign(digest, signer3Key); vm.prank(alice); vm.expectRevert("DBIS: signer not active at block"); router.submitMintAuth(auth, sigs); } function _sign(bytes32 digest, uint256 pk) internal pure returns (bytes memory) { (uint8 v, bytes32 r, bytes32 s) = vm.sign(pk, digest); return abi.encodePacked(r, s, v); } }