Financial Records
ComBoox adopts ETH and USDC as special βgeneral equivalenceβ to pay for new share subscription or share transfer consideration. It also allows companies booked in the system to transfer ETH / USDC respectively, or to pay ETH together with calling external smart contracts to pay for the cost or consideration of legal actions. To facilitate automatic bookkeeping of the financial records of these activities, the system set up special events in the smart contracts of the General Keeper, Sub-Keepers and Cashier, so that the financial records of these payment activities can be retrieved and summarized in real time automatically.
1. ETH/Fiat Currency Exchange Rate
When creating registration books in the system, the company concerned can set its book keeping currency. Currently, the following currencies are acceptable to the system: RMB, USD, JPY, EUR, GBP and some other fiat currencies of major countries. During the equity transaction, the system will automatically call the Chain Link Price Feed service to query the exchange rate between the bookkeeping currency and ETH, and then automatically calculate and deliver ETH according to the amount of the fiat currency of the transaction, delivery versus payment.
2. ETH/CBP Exchange Rate
The system offers CBP to the public through the smart contract of Fuel Tank, at the ETH/CBP exchange rate that is determined by the Owner of the Platform. Currently, the exchange rate is 1:1, which can be retrieved in real time by users.
3. CBP Inflow and Outflow Records
As an ERC-20 token, CBP's income and expenditure are booked in the Registration Center, which automatically records the sender, recipient and amount of each transaction. Users can further distinguish the CBP payments with respect to their detailed scenarios and reasons according to the identity of the payer and payee. For example, when the payer is "zero" address, it means the Platform minted coins to the payee.
event Transfer(address indexed from, address indexed to, uint256 indexed value);
4. ETH Inflow and Outflow Records
It is only General Keeper that has API for equity trading transactions to be settled in ETH. Therefore, ETH paid together with the transaction is also directly received by General Keeper. After that, General Keeper will route the relevant equity transaction command to the specific Sub-Keeper, and the Sub-Keeper will further retrieve the involving partiesβ user number and calculate their respective attributable amount of ETH, and return the relevant information back to General Keeper. Finally, General Keeper will allocate the ETH received to the specific users' deposit account or custody account according to the feedback information from Sub-Keeper. For example, when a buyer pays the consideration of a share transfer deal in ETH, General Keeper will receive and store the ETH paid with the command, and then route the calling command to the ROA Keeper, which will calculate the total amount of the share transfer transaction, call the Chain Link Price Feed service to query the ETH exchange rate at that time, and then send the ETH records booking instruction back to General Keeper. Thereafter, General Keeper will credit the amount of ETH equivalent to the total transaction price to the seller's deposit account as consideration, and credit the amount of ETH exceeding the transactionβs consideration to the buyer's deposit account as over paid balance.
5. USDC Inflow and Outflow Records
Only the USD Keeper provides API for equity trading transactions in USDC, and, Cashier holds, receives, forwards, transfers, or distributes the USDC paid in connection with these transactions, and is also responsible for recording all such USDC payment activities.
// Structure and Events of Cashier
struct TransferAuth{
address from;
address to;
uint256 value;
uint256 validAfter;
uint256 validBefore;
bytes32 nonce;
uint8 v;
bytes32 r;
bytes32 s;
}
event ReceiveUsd(address indexed from, uint indexed amt);
event ReceiveUsd(address indexed from, uint indexed amt, bytes32 indexed remark);
event ForwardUsd(address indexed from, address indexed to, uint indexed amt, bytes32 remark);
event CustodyUsd(address indexed from, uint indexed amt, bytes32 indexed remark);
event ReleaseUsd(address indexed from, address indexed to, uint indexed amt, bytes32 remark);
event TransferUsd(address indexed to, uint indexed amt, bytes32 indexed remark);
event DistributeUsd(uint indexed amt);
event PickupUsd(address indexed msgSender, uint indexed caller, uint indexed value);
6. ETH Deposit Records
(1) Save To Coffer Function
The General Keeper manages the transfer and accounting activities of ETH payments through the function of Save To Coffer. This function only records the recipientβs user number, the specific amount and the reason of payment. Among them, the reason of payment is recorded the summary description of the relevant payment reason in the format of Bytes32 in ASCII coding.
// SaveToCoffer Function
function saveToCoffer(uint acct, uint value, bytes32 reason) external {
require(msg.sender == _keepers[4] ||
msg.sender == _keepers[5] ||
msg.sender == _keepers[6] ||
msg.sender == _keepers[7] ||
msg.sender == _keepers[10],
"GK.saveToCoffer: not correct Keeper");
require(address(this).balance >= _coffers[0] + value,
"GK.saveToCoffer: insufficient Eth");
_coffers[acct] += value;
_coffers[0] += value;
emit SaveToCoffer(acct, value, reason);
}
ETH deposited in a user's deposit account can only be exclusively picked up by such user by calling the Pickup Deposit Function of the General Keeper, and cannot be misappropriated or withdrawn by any other account.
(2) Pickup Deposit Function
// Pickup Deposit Function
function pickupDeposit() external isNormal{
uint caller = _msgSender(msg.sender, 18000);
uint value = _coffers[caller];
if (value > 0) {
_coffers[caller] = 0;
_coffers[0] -= value;
Address.sendValue(payable(msg.sender), value);
emit PickupDeposit(msg.sender, caller, value);
} else revert("GK.pickupDeposit: no balance");
}
(3) Deposit Reasons List
The ETH deposit reasons, the brief description concerned, as well as the corresponding ASCII code are listed as follows:
Profits Distribution
GMMKeeper
DistributeProfits
0x4469737472696275746550726f66697473000000000000000000000000000000
Deposit Sell Orderβs Consideration
LOOKeeper
CloseBidAgainstOffer
0x436c6f7365426964416761696e73744f66666572000000000000000000000000
Deposit Buy Orderβs Overpaid Balance
LOOKeeper
DepositBalanceOfBidOrder
0x4465706f73697442616c616e63654f664269644f726465720000000000000000
Deposit Share Transfer Consideration
ROAKeeper
DepositConsiderationOfSTDeal
0x4465706f736974436f6e73696465726174696f6e4f6653544465616c00000000
Deposit Share Transfer Overpaid Balance
ROAKeeper
DepositBalanceOfOTCDeal
0x4465706f73697442616c616e63654f664f54434465616c000000000000000000
Deposit Paid In Contribution Balance
ROMKeeper
DepositBalanceOfPayInCap
0x4465706f73697442616c616e63654f66506179496e4361700000000000000000
Deposit Swap Deal Consideration
ROOKeeper
DepositConsiderationOfSwap
0x4465706f736974436f6e73696465726174696f6e4f6653776170000000000000
Deposit Swap Deal Over Paid Balance
ROOKeeper
DepositBalanceOfSwap
0x4465706f73697442616c616e63654f6653776170000000000000000000000000
Deposit Rejected Deal Consideration
ROOKeeper
DepositConsiderOfRejectedDeal
0x4465706f736974436f6e73696465724f6652656a65637465644465616c000000
Deposit Rejected Deal Overpaid Balance
ROOKeeper
DepositBalanceOfRejectedDeal
0x4465706f73697442616c616e63654f6652656a65637465644465616c00000000
7. ETH Escrow Records
(1) Escrow ETH Margin
The system allows verified investors to trade the equity shares of the registered companies in the smart contract of List of ETH Orders with six types of orders: limited buy order, market buy order, limited sell order, market sell order, limited issue order, and market issue order. When listing a limited buy order, the system needs to deposit the ETH paid by the buyer into the escrow account as margin. When depositing margin, the system still calls the function of Save To Coffer of the General Keeper, except that the buyer's user number will be duplicated twice and linked together as the account holder. This allows different users to be distinguished and prevents the margin from being taken out by the user himself or any other user.
// Example of Deposit Margin Algorithm
uint acct = bid.buyer;
acct = (acct << 40) + acct;
_gk.saveToCoffer(
acct, bid.consideration,
bytes32(0x437573746f647956616c75654f664269644f7264657200000000000000000000)
); // CustodyValueOfBidOrder
(2) Release ETH Escrow
When a sell order or issue order is listed, the system will take the lead in matching the transaction with the existing listed buy orders, and after the transaction is closed, the margin locked up with the buy order will be released to the seller's deposit account as consideration, or to the company as capital contribution. Upon releasing the margin, the Release Custody function of General Keeper will be called, through the events of which the payer, payee, amount, and reason of the payment will automatically be recorded. Similar to Save To Coffer, a summary description of the reason of payment will be saved in ASCII code via a Bytes32 fixed-length array.
//Release Custody Function
function releaseCustody(uint from, uint to, uint amt, bytes32 reason) external isNormal {
require (msg.sender == _keepers[10], "GK.releaseCustody: wrong Keeper");
uint acct = (from << 40) + from;
uint value = _coffers[acct];
require (value >= amt, "GK.releaseCusotody: insufficient");
_coffers[acct] -= amt;
if (to > 0) { _coffers[to] += amt;
} else { _coffers[0] -= amt;}
emit ReleaseCustody(from, to, amt, reason);
}
(3) Reason of ETH Escrow
The table below provides a summary of the Sub-Keeper, reasons for custody or releasing ETH margin and their ASCII codes.
Deposit Buy Order Margin
LOO Keeper
CustodyValueOfBidOrder
0x437573746f647956616c75654f664269644f7264657200000000000000000000
Release Margin to Company
LOO Keeper
CloseInitOfferAgainstBid
0x436c6f7365496e69744f66666572416761696e73744269640000000000000000
Release Margin to Seller
LOO Keeper
CloseOfferAgainstBid
0x436c6f73654f66666572416761696e7374426964000000000000000000000000
Refund Buy Order Margin
LOO Keeper
RefundValueOfBidOrder
0x526566756e6456616c75654f664269644f726465720000000000000000000000
8. ETH Inflow
For ETH paid to the company such as paid-in contributions and equity subscriptions, the Sub-Keeper will only record the reason for the payment through the event log, and the General Keeper will not allocate such ETH to the deposit account or the custody account.
ETH Inflow Events List
Pay In Contribution for Capital Increase Deal
ROA Keeper
event PayOffCIDeal(uint indexed caller, uint indexed valueOfDeal)
Pay In Capital Contribution
ROM Keeper
event PayInCapital (uint indexed seqOfShare, uint indexed amt, uint indexed valueOfDeal)
Close New Share Issue Order
LOO Keeper
event CloseBidAgainstInitOffer(uint indexed buyer, uint indexed amt);
9. ETH Outflow
Payments of the Company are all subject to the approval of the General Meeting of Members or the Board of Directors in accordance with the corporate governance process stipulated in the Shareholders Agreement, which are essentially the process of implementing the resolutions of the General Meeting of Members or the ones of the Board of Directors.
ETH Outflow Events List
Payment approved by the General Meeting of Members
GMM Keeper
event TransferFund(address indexed to, bool indexed isCBP, uint indexed amt, uint seqOfMotion, uint caller)
Execute actions approved by the General Meeting of Members
GMM Keeper
event ExecAction(address indexed targets, uint indexed values, bytes indexed params, uint seqOfMotion, uint caller)
Distribute profits
GMM Keeper
event DistributeProfits(uint256 indexed sum, uint indexed seqOfMotion, uint indexed caller);
Payment approved by the Board of Directors
BMM Keeper
event TransferFund(address indexed to, bool indexed isCBP, uint indexed amt, uint seqOfMotion, uint caller)
Execute actions approved by the Board of Directors
BMM Keeper
event ExecAction(address indexed targets, uint indexed values, bytes indexed params, uint seqOfMotion, uint caller)
10. USDC Deposit Records
(1) Distribute USD Function
As the price of USDC is deemed to be $1.00 per unit, no balance or change will be generated for payments made in USDC. Any consideration or refund will be settled directly to the relevant userβs account.
However, in the event that any USDC is distributed by a company to its Members, the corresponding amounts will be allocated under each Memberβs User Number in a private mapping called "_lockers". Thereafter, Members may retrieve their allocated deposits at their discretion at any time.
//Mapping _lockers and Distribute USD Function of Cashier
mapping(uint => uint) private _lockers;
function distributeUsd(uint amt) external {
require(msg.sender == address(_gk), "Cashier.DistrUsd: not GK");
require(balanceOfComp( ) >= amt, "Cashier.DistrUsd: insufficient amt");
IRegisterOfMembers _rom = _gk.getROM( );
uint[ ] memory members = _rom.membersList();
uint len = members.length;
uint totalPoints = _rom.ownersPoints( ).points;
uint sum = 0;
while (len > 1) {
uint member = members[len - 1];
uint pointsOfMember = _rom.pointsOfMember(member).points;
uint value = pointsOfMember * amt / totalPoints;
_lockers[member] += value;
_lockers[0] += value;
sum += value;
len--;
}
_lockers[members[0]] += (amt - sum);
_lockers[0] += (amt - sum);
emit DistributeUsd(amt);
}
(2) Pickup USD Function
USDC deposited in a user's deposit account can only be exclusively picked up by such user by calling the Pickup USD Function of the Cashier, and cannot be misappropriated or withdrawn by any other account.
//Pickup USD Function
function pickupUsd() external {
uint caller = _msgSender(msg.sender, 18000);
uint value = _lockers[caller];
if (value > 0) {
_lockers[caller] = 0;
_lockers[0] -= value;
emit PickupUsd(msg.sender, caller, value);
require(_usd().transfer(msg.sender, value), "Cashier.PickupUsd: transfer failed");
} else revert("Cashier.pickupDeposit: no balance");
}
11. USDC Escrow Records
(1) Escrow USDC Margin
The system allows verified investors to trade the equity shares of the registered companies in the smart contract of List of USD Orders with six types of orders: limited buy order, market buy order, limited sell order, market sell order, limited issue order, and market issue order. When listing a limited buy order, the system needs to deposit the USDC paid by the buyer into the escrow account as margin.
When depositing margin, the system will call the function of Custody USD of Cashier, and deposit the relevant amount into a private mapping called β_coffersβ. This allows different users to be distinguished and prevents the margin from being taken out by the user himself or any other user.
//Function Custody USD of Cashier
mapping(address => uint) private _coffers;
function _transferWithAuthorization(TransferAuth memory auth) private {
_usd().transferWithAuthorization(
auth.from,
address(this),
auth.value,
auth.validAfter,
auth.validBefore,
auth.nonce,
auth.v,
auth.r,
auth.s
);
}
function custodyUsd(TransferAuth memory auth, bytes32 remark) external anyKeeper {
_transferWithAuthorization(auth);
_coffers[auth.from] += auth.value;
_coffers[address(0)] += auth.value;
emit CustodyUsd(auth.from, auth.value, remark);
}
(2) Release USDC Escrow
When a sell order or issue order is listed, the system will take the lead in matching the transaction with the existing listed buy orders, and after the transaction is closed, the margin locked up with the buy order will be released to the seller's deposit account as consideration, or to the company as capital contribution.
Upon releasing the margin, the Release USD function of Cashier will be called, through the events of which the payer, payee, amount, and reason of the payment will automatically be recorded. Similar to Save To Coffer, a summary description of the reason of payment will be saved in ASCII code via a Bytes32 fixed-length array.
//Release USD Function
function releaseUsd(address from, address to, uint amt, bytes32 remark) external anyKeeper {
require(_coffers[from] >= amt, "Cashier.ReleaseUsd: insufficient amt");
_coffers[from] -= amt;
_coffers[address(0)] -= amt;
emit ReleaseUsd(from, to, amt, remark);
require(_usd().transfer(to, amt), "Cashier.releaseUsd: transfer failed");
}
(3) Reason USDC Escrow
The table below provides a summary of the reasons for escrow or releasing USDC margin and their ASCII codes.
Reason of Escrow
Deposit Buy Order Margin
CustodyValueOfBid
0x437573746f647956616c75654f66426964000000000000000000000000000000
Release Margin to Company
CloseInitOfferAgainstBid
0x436c6f7365496e69744f66666572416761696e73744269640000000000000000
Release Margin to Seller
CloseOfferAgainstBid
0x436c6f73654f66666572416761696e7374426964000000000000000000000000
Refund Buy Order Margin
RefundValueOfBidOrder
0x526566756e6456616c75654f664269644f726465720000000000000000000000
12. USDC Inflow
For USDC paid to the company such as paid-in contributions and equity subscriptions, the Cashier will only record the reason for the payment through the event log, and will not allocate such USDC to the deposit account or the escrow account.
//Collect USD Function
function collectUsd(TransferAuth memory auth, bytes32 remark) external anyKeeper {
_transferWithAuthorization(auth);
emit ReceiveUsd(auth.from, auth.value, remark);
}
13. USDC Outflow
In addition to the USDC Distribution scenario, any other USDC payments of a company shall also be subject to the approval of the General Meeting of Members or the Board of Directors in accordance with the corporate governance process stipulated in the Shareholders Agreement, which are essentially the process of implementing the resolutions of the General Meeting of Members or the ones of the Board of Directors.
The authorized executor needs to call the function of ExecAction or ExecActionOfGM of General Keeper, so as to further invoke the function of TransferUSD of Cashier to make the payment.
//Transfer USD Function of Cashier
function transferUsd(address to, uint amt, bytes32 remark) external anyKeeper {
require(balanceOfComp() >= amt, "Cashier.transferUsd: insufficient amt");
emit TransferUsd(to, amt, remark);
require(_usd().transfer(to, amt),"Cashier.transferUsd: transfer failed");
}
Source Codes
Last updated