Solidity
July 29, 2023

Создаем свою АБС на блокчейне 

Всем привет. Сидел я тут как-то и думал, а почему бы не сделать свою автоматизированную банковскую систему на блокчейне и соблюсти почти все правила учета и отчетности от ЦБ РФ. Мне показалось это интересно и возможным сделать. Разумеется ЦБ не примет эту систему, но как для практики программирования идея думаю не плохая.

Буду создавать все в лайв режиме, поэтому не знаю получиться сделать все что задумал.

Теория

Начну с теории и что нам нужно будет сделать.

АБС (автоматизированная банковская система) это совокупность аппаратных средств и программ для создания информационной среды, которая выполняет управленческие и финансовые задачи в условии реального времени у банка.

Добавлю также, что не надо путать слова автоматизированные и автоматические. Автоматизированные, где человек принимает окончательное решение, а в автоматических машина.

То, что я делаю не может быть принято ЦБ, потому что есть нарушения с моей стороны ряда статей ФЗ "О ЦБ РФ". Поэтому рассматриваем этот проект как просто тренировка и закрепление знаний.

Также нужно понимать, что я не создаю банк. Я создаю вендор, который можно интегрировать (в теории) в свой банк на блокчейне, если вы его создадите.

Модули АБС:

Немного о модулях АБС.

Фрон-офис - это взаимодействие с клиентом
Бэк-офис - это функционал системы
Мидл-офис - это функционал проводки или бух учет.

Секретная картинка модулей АБС

На данной картинке выделены основные модули, которые нам нужно будет по идее реализовать. Но как я уже говорил, я делаю все в лайв режиме и не знаю на данный момент что получиться сделать, а что нет.

Бухгалтерский учет будет является транзакциями в блокчейне, поэтому как такового бух учета у нас не будет. Но называть я буду его все равно так.

Еще один из недостатков: у нас не будет наличного расчета :(.

Расписывать функционал каждого модуля не буду, потому что долго и не нужно. Когда будем подходить к реализации модуля, тогда и буду его объяснять.

Начало:

Начнем с реализации ядра.

Модуль является функциональным ядром интегрированной банковской системы

Что я реализую в ядре:

1. Тип клиента
2. Ведение счетов
3. Платежный документооборот
4. Безналичные расчеты
5. Картотеки (дебетовые и кредитные карты)
6. Валютный учет(основой сделаем доллар)
7. Хранение всей информации о платежах
8. Учет и отчетность

В данной статье я реализую все кроме:
Валютный учет
Безналичные расчеты
Учет и отчетность

Код

И так для начала кину весь код, который есть на данный момент:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;
contract Core {    
    address owner;    
    constructor(){        
        owner = msg.sender;    
    }    
    struct client{        
        string name;        
        address clientOwner;         
        uint clientType;         
        bool accountDebet;        
        bool accountCredit;        
        bytes32[] paymentInfo;        
        document[] documents;    
    }    
    struct document{        
        uint typeDocument;        
        string documentInfo;        
        address document;    
    }    
    mapping(address=>mapping(uint=>bool)) initSubscribe;    
    mapping(address=>client) clients;    
    modifier onlyOwner() {        
        require(owner == msg.sender, "faild");        
        _;    
    }    
    function initializeClient(string calldata _name) public payable{        
        client storage newClientInfo = clients[msg.sender];        
        newClientInfo.name = _name;        
        newClientInfo.clientOwner = msg.sender;               
        newClientInfo.accountDebet = false;        
        newClientInfo.accountCredit = false;
        uint size;        
        address addr = msg.sender;        
        assembly {            
            size := extcodesize(addr)        
        }        
        if (size > 0) {            
            bytes4 expectedFunctionSignature = bytes4(keccak256("myFunction(uint256,address)"));            
            (bool success, ) = addr.call(abi.encodeWithSelector(expectedFunctionSignature, 1, addr)); 
            require(success, "faild");           
            if(success == true){                
                newClientInfo.clientType = 1;                
                newClientInfo.documents.push(document(0, "entity person", msg.sender));            
            }         
        }else{                
            newClientInfo.clientType = 0;                
            newClientInfo.documents.push(document(0, "natural person", msg.sender));            
        }         
        newClientInfo.paymentInfo.push(bytes32(keccak256(bytes("Create client"))));        
    }    
    function addAccountDebet() public{          
        require(msg.sender == clients[msg.sender].clientOwner);        
        initSubscribe[msg.sender][0] = true;    
    }    
    function addAccountCredit() public{          
        require(msg.sender == clients[msg.sender].clientOwner);        
        initSubscribe[msg.sender][1] = true;    
    }    
    function complitesubscribersAccount(address _client, uint typeAccount) public onlyOwner{        
        require(initSubscribe[_client][typeAccount] == true, "not complite");        
        require(typeAccount == 0 || typeAccount == 1, "error");        
        if(typeAccount == 0){            
            clients[_client].accountDebet = true;            
            initSubscribe[_client][typeAccount] = false;        
        }        
        if(typeAccount == 1){            
            clients[_client].accountCredit = true;            
            initSubscribe[_client][typeAccount] = false;        
        }    
    }    
    function deletAccountDebet() public{        
        require(msg.sender == clients[msg.sender].clientOwner);        
        require(clients[msg.sender].accountDebet == true, "not open");        
        initSubscribe[msg.sender][0] = true;    
    }    
    function deletAccountCredit() public{        
        require(msg.sender == clients[msg.sender].clientOwner);        
        require(clients[msg.sender].accountCredit == true, "not open");        
        initSubscribe[msg.sender][1] = true;    
    }    
    function comliteDeletAccount(address _client, uint typeAccount) public onlyOwner{        
        require(initSubscribe[_client][typeAccount] == true, "not complite");        
        require(typeAccount == 0 || typeAccount == 1, "error");        
        if(typeAccount == 0){            
            clients[_client].accountDebet = false;            
            initSubscribe[_client][typeAccount] = false;        
        }        
        if(typeAccount == 1){                      
            clients[_client].accountCredit = false;            
            initSubscribe[_client][typeAccount] = false;        
        }    
    }       
}

Сразу говорю, что не буду размусоливать его. Буду показывать основные моменты и общую концепцию. Это не конечная версия кода, будет еще все меняться и добавляться.

Итак:

Для начала я создаю структуру client которая будет содержать все данные клиента. И структуру document, которая должна содержать информацию о ценных бумагах или важных расписках например.

Дальше создаю маппинг, который будет нужен для подтверждения открытия счета initSubscribe. И маппинг для общего сбора всех клиентов clients.

Функции

Функция initializeClient будет нужна для регистрации клиента в банке. Будут ставиться дефолтные значения, которые в будущем можно менять открывая новые привилегии. Из интересного, я решил разделять юрлицо и физлицо на два типа: если клиент, это обычный адрес кошелька, он будет физлицом, если же смарт-контракт то юрлицо. Проверку делаю через ассемблер функцией проверки на наличие кода extcodesize. Но так как это не гарантирует, что вызов идет от смарт-контракта, я добавил условие, что каждый контракт должен иметь функцию check, которая вызывается при проверке низкоуровневым вызовом, чтоб убедиться в том, что данный вызов идет из стороннего смарт-контракта.

Функции addAccountDebet, addAccountCredit, deletAccountDebet, deletAccountCredit нужны для запроса открытия/закрытия счета в банке.

complitesubscribersAccount и comliteDeletAccount нужны для владельца который будет решать можно ли открыть данному лицу счет или нет

Я создал смарт-контракт, который хочет открыть счет в банке, чтоб удостовериться в работе моей концепции.

contract OtherContract {    
    address public coreAddress;     
    Core public coreInstance; 
    constructor(address _coreAddress) {        
        coreAddress = _coreAddress;        
        coreInstance = Core(coreAddress);    
    }    
    function check(uint num, address addr) public pure{
    }    
    function callInitializeClient(string calldata _name) external {        
        coreInstance.initializeClient(_name);    
    }
}

Тут все просто. Передаю адрес Core и после создаю экземпляр смарт-контракта.

Дальше обязательно добавляем функцию check и функцию callInitializeClient, которая вызывает initializeClient

Чтоб все проверить, я создал hardhat проект, где будут все тесты.

На гитхабе будет все, кто хочет посмотреть, то ссылка в конце статьи.

Если вы хотите проверить, или что то добавить пишите мне в тг. Его можно найти в профиле моего канала.

На этом я закончу эту статью. Пока что нет ни чего особенного и интересного, но в голове строится необычная картина. На данный момент я рассказал все, что хотел. Дальше больше. Вроде как таких похожих проектов не видел ни где, поэтому надеюсь зайдет кому-то.

Мой тг для связи в шапке канала
Мой тг канал
Этот проект на гитхабе