财务记录
系统允许使用ETH和USDC作为价值载体,支付新股认购款或股转对价;同时,也允许系统中簿记的公司对外支付ETH/USDC,或者在调用外部智能合约时,携带ETH以支付特定法律行为的费用或对价。
为方便用户自动簿记这些价值收支活动的财务记录,系统在总管理人合约、相关的分项管理人合约以及收银台合约中设置了事件日志,以便可以实时、自动检索、汇总这些价值支付活动的财务记录。
(2)CBP收支记录
作为ERC-20标准化代币,CBP的收支记录由注册中心合约统一簿记,自动记录每一笔转账记录的支付方、收款方和支付金额。用户可根据CBP的支付方、收款方角色进一步区分CBP支付的具体场景、支付理由。例如,当支付方为“零地址”时,代表平台向收款方铸币;当支付方为燃料库合约地址时,为充值购入CBP。
// CBP支付行为的事件日志接口
event Transfer(address indexed from, address indexed to, uint256 indexed value);
(3)ETH收支记录
所有以ETH作为结算方式的股权交易行为的调用指令,只能由总管理人合约统一接收。因此,随交易行为一并支付的ETH也直接由总管理人合约统一收取并存储。之后,总管理人合约将会把相关股权交易指令分别路由至特定的分项管理人合约,由分项管理人合约逐一计算ETH款项的收款用户编号和具体收款金额,并将相关信息反馈给总管理人合约。最后,总管理人合约再根据分项管理人合约反馈信息,将ETH款项划归特定用户的暂存款账户或保证金账户。
例如,当买方通过ETH支付协议股转交易的对价时,总管理人合约会接收并存储随指令支付的ETH,然后将调用指令路由至投资协议管理人合约,投资协议管理人会计算股转交易的成交总金额,调用Chain Link Price Feed服务查询当时的ETH兑换汇率,然后向总管理人合约反馈ETH记账指令,将成交总价对应的ETH记入卖方暂存款账户、将超出成交总价的ETH余额记入买方暂存款账户。
1. 公司的ETH收入
对于股东实缴出资款、股权认购款这种支付给公司的ETH,分项管理人合约只会通过事件日志记录下来款项支付理由,总管理人合约也不会将这些ETH划归暂存款账户或保证金账户。
公司ETH收款事件日志表
支付增资交易款
PayOffCIDeal
支付人用户编号、ETH金额
支付实缴出资
PayInCapital
股权证书编号、出资金额、ETH金额
摘牌新股发行订单
CloseBidAgainstInitOffer
买方、ETH金额
2. 公司的ETH支付
公司的对外支付的ETH,均需要按照股东协议合约规定的公司治理流程,经过股东会决议或董事会决议的批准才能对外支付,实质上是股东会决议或董事会决议的执行过程。
公司对外支付事件日志表
股东会批准的支付
TransferFund
收款用户编号、币种(CBP/ETH)、支付金额、动议编号、执行用户编号
股东会批准的行为
ExecAction
目标合约地址、ETH金额、调用指令内容、动议编号、执行用户编号
利润分配
DistributeProfits
分配ETH总额、动议编号、执行用户编号
董事会批准的支付
TransferFund
收款用户编号、币种(CBP/ETH)、支付金额、动议编号、执行用户编号
董事会批准的行为
ExecAction
目标合约地址、ETH金额、调用指令内容、动议编号、执行用户编号
3. ETH暂存款
(1) 存入保管箱函数
总管理人合约通过存入保管箱函数统一管理ETH款项的划拨、记账活动。该函数仅记录收款用户编号、具体金额和款项用途。其中,款项用途通过Bytes32定长数组以ASCII编码格式简单记录相关用途的摘要信息。
// 存入保管箱函数
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,仅能由特定用户通过总管理人合约的取出存款函数一次性取出,任何其他账户均无法挪用或取出。
(2) 取出暂存款函数
// 取出暂存款函数
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) 暂存款用途编码表
ETH暂存款收支的分项管理人合约、款项用途摘要及其ASCII编码,请见下表。
暂存款用途编码表
利润分配
GMMKeeper
DistributeProfits
0x4469737472696275746550726f66697473000000000000000000000000000000
存入卖单对价
LOOKeeper
CloseBidAgainstOffer
0x436c6f7365426964416761696e73744f66666572000000000000000000000000
存回买单余额
LOOKeeper
DepositBalanceOfBidOrder
0x4465706f73697442616c616e63654f664269644f726465720000000000000000
存入协议股转对价
ROAKeeper
DepositConsiderationOfSTDeal
0x4465706f736974436f6e73696465726174696f6e4f6653544465616c00000000
存回协议交易余额
ROAKeeper
DepositBalanceOfOTCDeal
0x4465706f73697442616c616e63654f664f54434465616c000000000000000000
存回实缴出资余额
ROMKeeper
DepositBalanceOfPayInCap
0x4465706f73697442616c616e63654f66506179496e4361700000000000000000
存入互换交易对价
ROOKeeper
DepositConsiderationOfSwap
0x4465706f736974436f6e73696465726174696f6e4f6653776170000000000000
存回互换交易余额
ROOKeeper
DepositBalanceOfSwap
0x4465706f73697442616c616e63654f6653776170000000000000000000000000
存入否决交易对价
ROOKeeper
DepositConsiderOfRejectedDeal
0x4465706f736974436f6e73696465724f6652656a65637465644465616c000000
存回否决交易余额
ROOKeeper
DepositBalanceOfRejectedDeal
0x4465706f73697442616c616e63654f6652656a65637465644465616c00000000
4. ETH保证金
(1) 存入ETH保证金
系统允许用户在挂牌交易(ETH)登记簿以定价买单、市价买单、定价卖单、市价卖单、定价发行订单五种订单挂牌交易标的公司的股份。挂牌定价买单时,系统需要将买方随单支付的ETH作为保证金存入买方用户的保证金账户。
存入保证金时,系统仍旧调用总管理人合约的存入保管箱函数,只是将买方用户编号复写两次并连接在一起作为账户持有人。这样,既可以甄别不同用户,又可以防止用户本人或任何其他用户取出保证金。
// 存入保证金算法举例
uint acct = bid.buyer;
acct = (acct << 40) + acct;
_gk.saveToCoffer(
acct, bid.consideration,
bytes32(0x437573746f647956616c75654f664269644f7264657200000000000000000000)
); // CustodyValueOfBidOrder
(2) 释放ETH保证金
挂牌卖单或发行订单时,系统会率先与已有买单进行交易撮合,成交后会将随买单锁定的保证金作为股转对价释放给卖方的暂存账户,或者作为增资对价释放给公司。
释放保证金时,会调用总管理人合约的释放保证金函数,通过事件日志自动记录付款方、收款方、金额和款项用途。与暂存款相似,也是通过Bytes32定长字符数组以ASCII编码保存款项用途的摘要说明。
// 释放保证金函数
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) ETH保证金用途说明
存入或释放ETH保证金的分项管理人合约、用途摘要及其ASCII编码,请见下表。
保证金用途编码表
存入买单保证金
LOOKeeper
CustodyValueOfBidOrder
0x437573746f647956616c75654f664269644f7264657200000000000000000000
释放保证金给公司
LOOKeeper
CloseInitOfferAgainstBid
0x436c6f7365496e69744f66666572416761696e73744269640000000000000000
释放保证金给卖方
LOOKeeper
CloseOfferAgainstBid
0x436c6f73654f66666572416761696e7374426964000000000000000000000000
返还买单保证金
LOOKeeper
RefundValueOfBidOrder
0x526566756e6456616c75654f664269644f726465720000000000000000000000
(4)USDC收支记录
所有以USDC作为结算方式的股权交易行为的调用指令,只能由美元登记簿管理员合约接收。收银台合约持有、接收、转发、转让和分配并且随交易行为一并支付的USDC,并记录所有此类USDC支付活动。
1. 公司的USDC收入
对于股东实缴出资款、股权认购款这种支付给公司的USDC,收银台合约只会通过事件日志记录支付原因,不会将这些USDC分配到USDC暂存账户或保证金账户。
// USDC收入函数
function collectUsd(TransferAuth memory auth, bytes32 remark) external anyKeeper {
_transferWithAuthorization(auth);
emit ReceiveUsd(auth.from, auth.value, remark);
}
2. 公司的USDC支付
除前述的USDC分配场景外,公司的任何其他 USDC支付也应按照股东协议合约中规定的公司治理程流程,经过股东会决议或董事会决议的批准才能对外支付,实质上是股东会决议或董事会决议的执行过程。
被授权的执行者需要调用总管理人合约的董事会批准的行为函数或股东会批准的行为函数,进一步调用收银台合约的USDC支付函数来执行相关支付行为。
// 收银台合约的USDC支付函数
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");
}
// 收银台合约的事件和数据结构
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);
3. USDC暂存款
(1) 美元利润分配函数
由于USDC的价格等于每单位1.00美元,因此以 USDC 进行的支付不会产生余额或零钱,任何对价或退款将直接结算到相关用户的账户中。
然而,如果公司向其成员分配任何 USDC,则相应金额将在名为“_lockers”的私有映射中按每个成员的用户编号进行分配。此后,成员可以根据自己的意愿随时提取分配的款项。
// Some收银台合约中的“_lockers”映射和美元利润分配函数
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) 取出美元函数
存入暂存款账户的USDC,仅能由特定用户通过调用收银台合约的取出美元函数来提取,任何其他账户均无法挪用或取出。
// 取出美元函数
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");
}
4. USDC保证金
(1) 存入USDC保证金
系统允许经过验证的投资者在挂牌交易(USDC)登记簿的智能合约中以定价买单、市价买单、定价卖单、市价卖单、定价发行单和市价发行单六种订单挂牌交易标的公司的股份。挂牌定价买单时,系统需要将买方随单支付的USDC存入买方的保证金账户。
在存入保证金时,系统将调用收银台合约的美元托管函数,并将相关金额存入一个名为 “_coffers” 的私有映射中。这样,既可以甄别不同用户,又可以防止用户本人或任何其他用户取出保证金。
// 收银台合约的美元托管函数
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) 释放USDC保证金
释放保证金时,会调用收银台合约的释放美元函数,通过事件日志自动记录付款人、收款人、金额和付款原因。与暂存款相似,也是通过Bytes32定长字符数组以ASCII编码保存款项用途的摘要说明。
// 释放美元函数
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) USDC保证金用途说明
下表总结了存入或释放USDC保证金的原因及其ASCII编码,请见下表:
USDC保证金用途说明
存入买单保证金
CustodyValueOfBid
0x437573746f647956616c75654f66426964000000000000000000000000000000
释放保证金给公司
CloseInitOfferAgainstBid
0x436c6f7365496e69744f66666572416761696e73744269640000000000000000
释放保证金给卖方
CloseOfferAgainstBid
0x436c6f73654f66666572416761696e7374426964000000000000000000000000
返还买单保证金
RefundValueOfBidOrder
0x526566756e6456616c75654f664269644f726465720000000000000000000000
相关源代码
Last updated