
自助式账户创建
VP-0002
摘要
本提案创建由网络治理所有的 new.vaulta 账户,并向其部署一个账户创建合约。它为钱包、交易所和各类指引提供一个标准的账户创建入口,其安全由网络共识保障,而非依赖某个运营方的密钥。该合约是自助式、由付款驱动的:用户以网络代币支付,合约便在单一步骤中创建其账户并购买该账户的 RAM,全程无需运营方参与。实施分为两次 BP MSIG(15/21)步骤:先创建该账户,再部署已公布哈希的合约代码。
依据
网络应当拥有一个标准的账户创建入口。账户是使用这条链的先决条件,而当钱包、交易所或指引把新用户引向 new.vaulta 时,所指向的合约其行为由 15/21 BP 共识保障,而不是依赖任何运营方的善意。
该功能本身已经过验证:同一份合约自 2025 年 10 月起就在 create.gm 上投入生产运行,在 Vaulta 主网上创建账户。所缺少的是网络的所有权。create.gm 由个人密钥控制:持有密钥的人可以在任何时刻不加通知地替换已部署的代码,因此即便审计过该合约的集成方,也无法确定收取其用户付款的究竟是什么代码;单个运营方可能停止服务或丢失密钥,链的正门也随之消失;而且由于接口只是一笔带备注的代币转账,把真正的创建合约与专为收款却不交付而制作的仿冒品区分开的,只有一个可信的名称。在 new.vaulta 之下,代码只能通过在执行前公开可见的 msig 变更,账户与网络共存续,网络也就此指明了唯一真实的入口。
机制
账户与权限
new.vaulta通过 BP MSIG(15/21)创建,遵循与其他*.vaulta账户相同的模式。两项权限都在创建时的eosio::newaccount操作中设定,因此其后不再需要updateauth。owner和active均委托给eosio.prods@active,其归结为 15/21 BP 共识。active另包含new.vaulta@eosio.code,因此合约可以以自身权限发送内联操作,即eosio::newaccount、core.vaulta::buyram、退还多余付款的core.vaulta::transfer,以及自身的logcreation。- 因此,未来所有
setcode/setabi都需要同样的 15/21 批准。不存在阈值更低的管理权限,合约也没有任何需要这类权限的管理操作。 - 所有合约参数(付款代币、字节配给量、代理合约)均为编译期常量。更改其中任何一项都是代码变更:需要新发布的提交和哈希,并由 15/21
setcode批准。不存在配置操作。
合约接口
该合约由代币转账通知驱动,而非通过直接调用的操作驱动。
| 操作 | 权限 | 用途 |
|---|---|---|
transfer 通知(*::transfer) |
代币发送方 | 收到由 core.vaulta 发行的付款代币后,解析备注、创建账户、购买其 RAM 并退还多余部分 |
parsememo(memo)(只读) |
无 | 将 accountname-PUBLICKEY 解析为账户名和单一密钥权限 |
estimatecost()(只读) |
无 | 返回创建一个账户的当前代币成本 |
logcreation(account, from, excess, ram, timestamp) |
new.vaulta |
每次创建时发出的内联日志操作,用于索引。from 是发出付款转账的账户,未必就是被创建的账户 |
备注格式为 accountname-PUBLICKEY(PUB_ 和旧版密钥格式均可)。每次创建支出 3,260 字节的 RAM 成本加上系统手续费,用户至少需发送该金额。多余部分会转入新账户。账户实际收到的 RAM 是这笔付款按市场价所能买到的数量,因此接近 3,260 字节而非恰好等于该数,网络的每账户基础配给量则在此之上另行叠加。
付款仅接受由 core.vaulta 发行的指定代币(A,4 位小数)。合约会放行而非拒绝以下转账:并非发给它的转账、由它自己发出的转账,以及来自 eosio.ram 或 core.vaulta 的转账,后者涵盖合约自身的 RAM 购买,以及 buyram 过程中 core.vaulta 解包付款时退回合约的那一段。除备注为 bypass 的情形外,转入任何其他代币都会被拒绝,发送方的交易随之失败。
备注为 bypass 的转账会被接受并留存,不创建任何账户;这正是直接向合约注资的方式。备注先于代币被检查,因此来自任何代币合约的任何代币,只要备注为 bypass 就会被留存。合约没有任何可将这些资金转出的操作,因此以此方式送入的余额会一直留在那里,直到 15/21 setcode 为其开辟出路。
若请求的账户名已存在,该笔转账失败,付款方保留其代币。
资金与运行成本
- 一次性:
new.vaulta账户本身及其部署代码所需的 RAM 由网络出资,作为创建 MSIG 的一部分,遵循新合约账户的通行做法。创建步骤以eosio为付款方购买 409,600 字节,该数值在部署所计费的约 390,000 字节之上留出了余量。合约不存储任何表,因此占用仅为代码和 ABI。 - 持续成本:运行完全由用户出资。每次创建的 CPU 和 NET 由作为交易首个签名者的付款用户承担,所创建账户的 RAM 在交易内用用户自己的付款购买。一次创建过后,合约自身的代币余额保持不变,其 RAM 占用也没有增加,因为退还多余部分所产生的余额行由
core.vaulta承担。new.vaulta无需质押,其持有的余额只会是通过bypass备注送入的部分。
限制
合约不设任何速率或规模限制,这是刻意为之:
- 没有任何补贴。用户支付全额 RAM 成本加系统手续费;不存在垃圾发送者可以耗尽的捐赠资金或额度池。
- 对任何愿意付费的人而言,账户创建在协议层面本就是无需许可的。通过
new.vaulta大规模创建付费账户,与直接用原始的newaccount+buyram相比,可能性完全相同,成本也完全相同。合约增加的只是便利。 - 规模天然固定:每次创建支出相当于 3,260 字节的成本,每笔转账一个账户,多付部分退还给新账户。
- 名称规则不变:作为创建方,
new.vaulta只能创建普通名称。所请求的名称必须恰好为 12 个字符,取自a-z与1-5,且不含点号,因为长度不足 12 的无点名称需要已中标的名称拍卖,带点名称需要后缀所有者的权限,而new.vaulta两者皆无。
使用方式
用户通过单笔代币转账创建账户:
- 调用
estimatecost(只读)获取当前价格。RAM 价格可能在估算与转账之间变动,因此惯例是按估算值再加一小笔缓冲发送:恰好等于估算值的付款可能在临界处失败,而这笔缓冲会退还给新账户,成为其初始余额。 - 附带备注
myaccount123-PUB_K1_...,将不少于该金额的网络代币转账至new.vaulta,所用名称需符合限制一节中的规则。 - 合约以给定密钥作为
myaccount123唯一的owner/active权限创建该账户,通过core.vaulta为myaccount123购买 RAM,并将多付部分退还给myaccount123。
新建的账户持有 RAM,但没有 CPU 和 NET,因此无法自行转出退还的余额或执行 updateauth。钱包应当为该账户的首批交易联合签名或提供资源。
由一个密钥同时承担两项权限是有意的设计。单独的 owner 密钥、多重签名或基于账户的恢复方式,可在创建之后通过 updateauth 设置。
范围边界
- 本提案仅涉及
new.vaulta账户及其账户创建合约。 - 无收入机制:合约仅收取 RAM 成本加系统手续费,并将任何多余部分退还给所创建的账户。
待解决问题
无。
后续步骤
合约源代码发布于 contracts/create。
部署步骤所提议的产物如下:
- 提交:
d103fdcea03bf2a6e3e0ff5ed893ec10389c29a2 - 代码哈希:
7166a16ea0d6db98fe18cde9a2d56b30f091dcbb19efcd0a0b5fa0e510f272c8,对应 38,534 字节的 WASM - ABI 哈希:
4f47d205f7cc990efb548d002f324e9c23c8a5ab65012dbab4430973e3c7ffdb,对应 610 字节的序列化 ABI - CDT:v4.1.1,以构建参数的形式固定在工具链容器中
重新构建的方法是检出该提交并运行 make build/create/production,编译在上述容器内进行。shasum -a 256 contracts/create/build/create.wasm 得到代码哈希;将 contracts/create/build/create.abi 序列化为二进制形式得到 610 字节,其哈希即 ABI 哈希。这两个值也正是 create.gm 在 Vaulta 主网上运行的内容,由 get_raw_abi 以 code_hash 和 abi_hash 返回。
- 作者在提出部署步骤之前,于本节记录代码哈希、其构建所依据的提交,以及可复现该哈希的 CDT 版本,以便 BP 自行构建并比对。
- 作者为每个步骤的交易加上回溯引用,并将其固定到承载本提案最终文本的提交。