// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
interface IERC20 {
function mint(address to, uint256 amount) external;
function burn(address from, uint256 amount) external;
function transfer(address to, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
}
contract RoutingWallet {
address public factory;
address public owner;
event WalletTransfer(address indexed token, address indexed to, uint256 amount);
modifier onlyOwner() {
require(msg.sender == owner, "not owner");
_;
}
modifier onlyOwnerOrFactory() {
require(msg.sender == owner || msg.sender == factory, "not authorized");
_;
}
constructor(address _owner) {
factory = msg.sender;
owner = _owner;
}
function transfer(address token, address to, uint256 amount) external onlyOwnerOrFactory {
require(IERC20(token).transfer(to, amount), "transfer failed");
emit WalletTransfer(token, to, amount);
}
function balance(address token) external view returns (uint256) {
return IERC20(token).balanceOf(address(this));
}
}
contract XFTRoutingFactoryV5 {
IERC20 public immutable token;
address public operator;
mapping(bytes32 => address) private routingToWallet;
mapping(bytes32 => address) private ibanToWallet;
mapping(bytes32 => address) private swiftToWallet;
mapping(bytes32 => address) public cikToWallet;
mapping(bytes32 => address) public leiToWallet;
mapping(bytes32 => address) private fedNowToWallet;
mapping(bytes32 => address) private fedwireToWallet;
mapping(bytes32 => address) private chipsToWallet;
mapping(bytes32 => address) private visaToWallet;
mapping(bytes32 => address) private sunpassToWallet;
mapping(bytes32 => address) private actcardToWallet;
struct WalletRec { string rail; string id; address wallet; }
WalletRec[] private _allWallets;
mapping(bytes32 => WalletRec[]) private _walletsByRail;
function _record(string memory rail, string memory id, address wallet) internal {
_allWallets.push(WalletRec(rail, id, wallet));
_walletsByRail[keccak256(bytes(rail))].push(WalletRec(rail, id, wallet));
}
event DeployWallet(string idType, string idValue, address wallet);
event Mint(string idType, string idValue, address wallet, uint256 amount);
event Burn(string idType, string idValue, address wallet, uint256 amount);
event Transfer(string idType, string fromId, string toId, address fromWallet, address toWallet, uint256 amount);
error NotOperator();
error WalletExists();
error WalletNotFound();
modifier onlyOp() {
if (msg.sender != operator) revert NotOperator();
_;
}
constructor(address _token) {
token = IERC20(_token);
operator = msg.sender;
}
function _salt(string memory id) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(id));
}
function _predict(bytes32 salt) internal view returns (address) {
return address(uint160(uint256(keccak256(
abi.encodePacked(
bytes1(0xff),
address(this),
salt,
keccak256(abi.encodePacked(type(RoutingWallet).creationCode, abi.encode(operator)))
)
))));
}
// ---------------- ROUTING ----------------
function predictRouting(string memory routing) public view returns (address) {
return _predict(_salt(routing));
}
function deployRoutingWallet(string memory routing) public onlyOp returns (address wallet) {
bytes32 salt = _salt(routing);
if (routingToWallet[salt] != address(0)) revert WalletExists();
wallet = address(new RoutingWallet{salt: salt}(operator));
routingToWallet[salt] = wallet;
_record("ROUTING", routing, wallet);
emit DeployWallet("ROUTING", routing, wallet);
}
function mintToRouting(string memory routing, uint256 amt) external onlyOp {
bytes32 salt = _salt(routing);
address wallet = routingToWallet[salt];
if (wallet == address(0)) wallet = deployRoutingWallet(routing);
token.mint(wallet, amt);
emit Mint("ROUTING", routing, wallet, amt);
}
function burnFromRouting(string memory routing, uint256 amt) external onlyOp {
address wallet = routingToWallet[_salt(routing)];
if (wallet == address(0)) revert WalletNotFound();
token.burn(wallet, amt);
emit Burn("ROUTING", routing, wallet, amt);
}
function getRoutingWallet(string memory routing) external view returns (address) {
return routingToWallet[_salt(routing)];
}
// ---------------- IBAN ----------------
function predictIBAN(string memory iban) public view returns (address) {
return _predict(_salt(iban));
}
function deployIBANWallet(string memory iban) public onlyOp returns (address wallet) {
bytes32 salt = _salt(iban);
if (ibanToWallet[salt] != address(0)) revert WalletExists();
wallet = address(new RoutingWallet{salt: salt}(operator));
ibanToWallet[salt] = wallet;
_record("IBAN", iban, wallet);
emit DeployWallet("IBAN", iban, wallet);
}
function mintToIBAN(string memory iban, uint256 amt) external onlyOp {
bytes32 salt = _salt(iban);
address wallet = ibanToWallet[salt];
if (wallet == address(0)) wallet = deployIBANWallet(iban);
token.mint(wallet, amt);
emit Mint("IBAN", iban, wallet, amt);
}
function burnFromIBAN(string memory iban, uint256 amt) external onlyOp {
address wallet = ibanToWallet[_salt(iban)];
if (wallet == address(0)) revert WalletNotFound();
token.burn(wallet, amt);
emit Burn("IBAN", iban, wallet, amt);
}
function getIBANWallet(string memory iban) external view returns (address) {
return ibanToWallet[_salt(iban)];
}
// ---------------- SWIFT/BIC ----------------
function predictSWIFT(string memory swift) public view returns (address) {
return _predict(_salt(swift));
}
function deploySWIFTWallet(string memory swift) public onlyOp returns (address wallet) {
bytes32 salt = _salt(swift);
if (swiftToWallet[salt] != address(0)) revert WalletExists();
wallet = address(new RoutingWallet{salt: salt}(operator));
swiftToWallet[salt] = wallet;
_record("SWIFT", swift, wallet);
emit DeployWallet("SWIFT", swift, wallet);
}
function mintToSWIFT(string memory swift, uint256 amt) external onlyOp {
bytes32 salt = _salt(swift);
address wallet = swiftToWallet[salt];
if (wallet == address(0)) wallet = deploySWIFTWallet(swift);
token.mint(wallet, amt);
emit Mint("SWIFT", swift, wallet, amt);
}
function burnFromSWIFT(string memory swift, uint256 amt) external onlyOp {
address wallet = swiftToWallet[_salt(swift)];
if (wallet == address(0)) revert WalletNotFound();
token.burn(wallet, amt);
emit Burn("SWIFT", swift, wallet, amt);
}
function getSWIFTWallet(string memory swift) external view returns (address) {
return swiftToWallet[_salt(swift)];
}
// ---------------- CIK ----------------
function predictCIK(string memory cik) public view returns (address) {
return _predict(_salt(cik));
}
function deployCIKWallet(string memory cik) public onlyOp returns (address wallet) {
bytes32 salt = _salt(cik);
if (cikToWallet[salt] != address(0)) revert WalletExists();
wallet = address(new RoutingWallet{salt: salt}(operator));
cikToWallet[salt] = wallet;
_record("CIK", cik, wallet);
emit DeployWallet("CIK", cik, wallet);
}
function mintToCIK(string memory cik, uint256 amt) external onlyOp {
bytes32 salt = _salt(cik);
address wallet = cikToWallet[salt];
if (wallet == address(0)) wallet = deployCIKWallet(cik);
token.mint(wallet, amt);
emit Mint("CIK", cik, wallet, amt);
}
function burnFromCIK(string memory cik, uint256 amt) external onlyOp {
address wallet = cikToWallet[_salt(cik)];
if (wallet == address(0)) revert WalletNotFound();
token.burn(wallet, amt);
emit Burn("CIK", cik, wallet, amt);
}
function getCIKWallet(string memory cik) external view returns (address) {
return cikToWallet[_salt(cik)];
}
// ---------------- LEI ----------------
function predictLEI(string memory lei) public view returns (address) {
return _predict(_salt(lei));
}
function deployLEIWallet(string memory lei) public onlyOp returns (address wallet) {
bytes32 salt = _salt(lei);
if (leiToWallet[salt] != address(0)) revert WalletExists();
wallet = address(new RoutingWallet{salt: salt}(operator));
leiToWallet[salt] = wallet;
_record("LEI", lei, wallet);
emit DeployWallet("LEI", lei, wallet);
}
function mintToLEI(string memory lei, uint256 amt) external onlyOp {
bytes32 salt = _salt(lei);
address wallet = leiToWallet[salt];
if (wallet == address(0)) wallet = deployLEIWallet(lei);
token.mint(wallet, amt);
emit Mint("LEI", lei, wallet, amt);
}
function burnFromLEI(string memory lei, uint256 amt) external onlyOp {
address wallet = leiToWallet[_salt(lei)];
if (wallet == address(0)) revert WalletNotFound();
token.burn(wallet, amt);
emit Burn("LEI", lei, wallet, amt);
}
function getLEIWallet(string memory lei) external view returns (address) {
return leiToWallet[_salt(lei)];
}
// ---------------- FEDNOW ----------------
function predictFEDNOW(string memory fednow) public view returns (address) {
return _predict(_salt(fednow));
}
function deployFEDNOWWallet(string memory fednow) public onlyOp returns (address wallet) {
bytes32 salt = _salt(fednow);
if (fedNowToWallet[salt] != address(0)) revert WalletExists();
wallet = address(new RoutingWallet{salt: salt}(operator));
fedNowToWallet[salt] = wallet;
_record("FEDNOW", fednow, wallet);
emit DeployWallet("FEDNOW", fednow, wallet);
}
function mintToFEDNOW(string memory fednow, uint256 amt) external onlyOp {
bytes32 salt = _salt(fednow);
address wallet = fedNowToWallet[salt];
if (wallet == address(0)) wallet = deployFEDNOWWallet(fednow);
token.mint(wallet, amt);
emit Mint("FEDNOW", fednow, wallet, amt);
}
function burnFromFEDNOW(string memory fednow, uint256 amt) external onlyOp {
address wallet = fedNowToWallet[_salt(fednow)];
if (wallet == address(0)) revert WalletNotFound();
token.burn(wallet, amt);
emit Burn("FEDNOW", fednow, wallet, amt);
}
function getFEDNOWWallet(string memory fednow) external view returns (address) {
return fedNowToWallet[_salt(fednow)];
}
// ---------------- FEDWIRE ----------------
function predictFEDWIRE(string memory fedwire) public view returns (address) {
return _predict(_salt(fedwire));
}
function deployFEDWIREWallet(string memory fedwire) public onlyOp returns (address wallet) {
bytes32 salt = _salt(fedwire);
if (fedwireToWallet[salt] != address(0)) revert WalletExists();
wallet = address(new RoutingWallet{salt: salt}(operator));
fedwireToWallet[salt] = wallet;
_record("FEDWIRE", fedwire, wallet);
emit DeployWallet("FEDWIRE", fedwire, wallet);
}
function mintToFEDWIRE(string memory fedwire, uint256 amt) external onlyOp {
bytes32 salt = _salt(fedwire);
address wallet = fedwireToWallet[salt];
if (wallet == address(0)) wallet = deployFEDWIREWallet(fedwire);
token.mint(wallet, amt);
emit Mint("FEDWIRE", fedwire, wallet, amt);
}
function burnFromFEDWIRE(string memory fedwire, uint256 amt) external onlyOp {
address wallet = fedwireToWallet[_salt(fedwire)];
if (wallet == address(0)) revert WalletNotFound();
token.burn(wallet, amt);
emit Burn("FEDWIRE", fedwire, wallet, amt);
}
function getFEDWIREWallet(string memory fedwire) external view returns (address) {
return fedwireToWallet[_salt(fedwire)];
}
// ---------------- CHIPS ----------------
function predictCHIPS(string memory chips) public view returns (address) {
return _predict(_salt(chips));
}
function deployCHIPSWallet(string memory chips) public onlyOp returns (address wallet) {
bytes32 salt = _salt(chips);
if (chipsToWallet[salt] != address(0)) revert WalletExists();
wallet = address(new RoutingWallet{salt: salt}(operator));
chipsToWallet[salt] = wallet;
_record("CHIPS", chips, wallet);
emit DeployWallet("CHIPS", chips, wallet);
}
function mintToCHIPS(string memory chips, uint256 amt) external onlyOp {
bytes32 salt = _salt(chips);
address wallet = chipsToWallet[salt];
if (wallet == address(0)) wallet = deployCHIPSWallet(chips);
token.mint(wallet, amt);
emit Mint("CHIPS", chips, wallet, amt);
}
function burnFromCHIPS(string memory chips, uint256 amt) external onlyOp {
address wallet = chipsToWallet[_salt(chips)];
if (wallet == address(0)) revert WalletNotFound();
token.burn(wallet, amt);
emit Burn("CHIPS", chips, wallet, amt);
}
function getCHIPSWallet(string memory chips) external view returns (address) {
return chipsToWallet[_salt(chips)];
}
// ---------------- VISA ----------------
function predictVISA(string memory visa) public view returns (address) {
return _predict(_salt(visa));
}
function deployVISAWallet(string memory pan) public onlyOp returns (address wallet) {
bytes32 salt = _salt(pan);
if (visaToWallet[salt] != address(0)) revert WalletExists();
wallet = address(new RoutingWallet{salt: salt}(operator));
visaToWallet[salt] = wallet;
_record("VISA", pan, wallet);
emit DeployWallet("VISA", pan, wallet);
}
function mintToVISA(string memory visa, uint256 amt) external onlyOp {
bytes32 salt = _salt(visa);
address wallet = visaToWallet[salt];
if (wallet == address(0)) wallet = deployVISAWallet(visa);
token.mint(wallet, amt);
emit Mint("VISA", visa, wallet, amt);
}
function burnFromVISA(string memory visa, uint256 amt) external onlyOp {
address wallet = visaToWallet[_salt(visa)];
if (wallet == address(0)) revert WalletNotFound();
token.burn(wallet, amt);
emit Burn("VISA", visa, wallet, amt);
}
function getVISAWallet(string memory visa) external view returns (address) {
return visaToWallet[_salt(visa)];
}
function transferVISA(
string memory fromVisa,
string memory toVisa,
uint256 amount
) external onlyOp {
address fromWallet = visaToWallet[_salt(fromVisa)];
address toWallet = visaToWallet[_salt(toVisa)];
if (fromWallet == address(0)) revert WalletNotFound();
if (toWallet == address(0)) revert WalletNotFound();
RoutingWallet(fromWallet).transfer(address(token), toWallet, amount);
emit Transfer("VISA", fromVisa, toVisa, fromWallet, toWallet, amount);
}
// ---------------- SUNPASS ----------------
function predictSUNPASS(string memory sunpass) public view returns (address) {
return _predict(_salt(sunpass));
}
function deploySUNPASSWallet(string memory pan) public onlyOp returns (address wallet) {
bytes32 salt = _salt(pan);
if (sunpassToWallet[salt] != address(0)) revert WalletExists();
wallet = address(new RoutingWallet{salt: salt}(operator));
sunpassToWallet[salt] = wallet;
_record("SUNPASS", pan, wallet);
emit DeployWallet("SUNPASS", pan, wallet);
}
function mintToSUNPASS(string memory sunpass, uint256 amt) external onlyOp {
bytes32 salt = _salt(sunpass);
address wallet = sunpassToWallet[salt];
if (wallet == address(0)) wallet = deploySUNPASSWallet(sunpass);
token.mint(wallet, amt);
emit Mint("SUNPASS", sunpass, wallet, amt);
}
function burnFromSUNPASS(string memory sunpass, uint256 amt) external onlyOp {
address wallet = sunpassToWallet[_salt(sunpass)];
if (wallet == address(0)) revert WalletNotFound();
token.burn(wallet, amt);
emit Burn("SUNPASS", sunpass, wallet, amt);
}
function getSUNPASSWallet(string memory sunpass) external view returns (address) {
return sunpassToWallet[_salt(sunpass)];
}
function transferSUNPASS(
string memory fromSunPass,
string memory toSunPass,
uint256 amount
) external onlyOp {
address fromWallet = sunpassToWallet[_salt(fromSunPass)];
address toWallet = sunpassToWallet[_salt(toSunPass)];
if (fromWallet == address(0)) revert WalletNotFound();
if (toWallet == address(0)) revert WalletNotFound();
RoutingWallet(fromWallet).transfer(address(token), toWallet, amount);
emit Transfer("SUNPASS", fromSunPass, toSunPass, fromWallet, toWallet, amount);
}
// ---------------- ACT CARD ----------------
function predictACTCARD(string memory actcard) public view returns (address) {
return _predict(_salt(actcard));
}
function deployACTCARDWallet(string memory pan) public onlyOp returns (address wallet) {
bytes32 salt = _salt(pan);
if (actcardToWallet[salt] != address(0)) revert WalletExists();
wallet = address(new RoutingWallet{salt: salt}(operator));
actcardToWallet[salt] = wallet;
_record("ACTCARD", pan, wallet);
emit DeployWallet("ACTCARD", pan, wallet);
}
function mintToACTCARD(string memory actcard, uint256 amt) external onlyOp {
bytes32 salt = _salt(actcard);
address wallet = actcardToWallet[salt];
if (wallet == address(0)) wallet = deployACTCARDWallet(actcard);
token.mint(wallet, amt);
emit Mint("ACTCARD", actcard, wallet, amt);
}
function burnFromACTCARD(string memory actcard, uint256 amt) external onlyOp {
address wallet = actcardToWallet[_salt(actcard)];
if (wallet == address(0)) revert WalletNotFound();
token.burn(wallet, amt);
emit Burn("ACTCARD", actcard, wallet, amt);
}
function getACTCARDWallet(string memory actcard) external view returns (address) {
return actcardToWallet[_salt(actcard)];
}
function transferACTCARD(
string memory fromACTCard,
string memory toACTCard,
uint256 amount
) external onlyOp {
address fromWallet = actcardToWallet[_salt(fromACTCard)];
address toWallet = actcardToWallet[_salt(toACTCard)];
if (fromWallet == address(0)) revert WalletNotFound();
if (toWallet == address(0)) revert WalletNotFound();
RoutingWallet(fromWallet).transfer(address(token), toWallet, amount);
emit Transfer("ACTCARD", fromACTCard, toACTCard, fromWallet, toWallet, amount);
}
// ---------------- WALLETS ----------------
function getAllWallets() external view returns (WalletRec[] memory) {
return _allWallets;
}
function getWalletsByRail(string memory rail) external view returns (WalletRec[] memory) {
return _walletsByRail[keccak256(bytes(rail))];
}
function getVisaWallets() external view returns (WalletRec[] memory) {
return _walletsByRail[keccak256(bytes("VISA"))];
}
function getSunPassWallets() external view returns (WalletRec[] memory) {
return _walletsByRail[keccak256(bytes("SUNPASS"))];
}
function getACTCardWallets() external view returns (WalletRec[] memory) {
return _walletsByRail[keccak256(bytes("ACTCARD"))];
}
}