2026-03-02 14:22:35 -08:00
|
|
|
// 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";
|
2026-03-04 02:00:09 -08:00
|
|
|
import "../../contracts/dbis/DBIS_EIP712Helper.sol";
|
2026-03-02 14:22:35 -08:00
|
|
|
import "../../contracts/dbis/DBIS_SettlementRouter.sol";
|
|
|
|
|
import "../../contracts/dbis/StablecoinReferenceRegistry.sol";
|
2026-03-04 02:00:09 -08:00
|
|
|
import "./MockMintableToken.sol";
|
2026-03-02 14:22:35 -08:00
|
|
|
|
|
|
|
|
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;
|
2026-03-04 02:00:09 -08:00
|
|
|
MockMintableToken public token;
|
2026-03-02 14:22:35 -08:00
|
|
|
|
|
|
|
|
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));
|
2026-03-04 02:00:09 -08:00
|
|
|
router = new DBIS_SettlementRouter(admin, address(root), address(new DBIS_EIP712Helper()));
|
2026-03-02 14:22:35 -08:00
|
|
|
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));
|
|
|
|
|
|
2026-03-04 02:00:09 -08:00
|
|
|
token = new MockMintableToken("Test GRU", "tGRU", 6, admin);
|
2026-03-02 14:22:35 -08:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|