Conflux 引入了一些内置的外部合约,以便更好地进行系统维护和链上治理。而本文档将介绍如何应用这些内置合约。
id | title | custom_edit_url | keywords |
---|---|---|---|
internal_contract | Internal Contract | https://github.com/Conflux-Ch… | conflux ,contract |
(重要:在 Tethys 主网中该接口会被变更。该文档已与最新版本同步。)
Conflux 引入了一些内嵌的合约,以便更好的进行系统维护及链上治理。目前 Conflux 含有三种内置合约:AdminControl
合约,SponsorWhitelistControl
合约以及 Staking
合约。
这些合约提供的 solidity 功能性 api 在此处定义。
这些性能只能通过调用 CALL
或 STATICCALL
操作进行调用。应用操作 CALLCODE
或 DELEGATECALL
会触发谬误。
这三个内置合约的地址如下所示:
- AdminControl:
0x0888000000000000000000000000000000000000
- SponsorWhitelistControl:
0x0888000000000000000000000000000000000001
- Staking:
0x0888000000000000000000000000000000000002
本文中的所有样例代码会应用 js-conflux-sdk 进行开发。
AdminControl 合约
总览
AdminControl
合约是一款用于合约开发的调试工具。在交易过程中创立合约时,以后交易的发送者会主动成为合约的管理者(admin)。
管理者 admin
的地址能够通过调用接口 setAdmin(address contractAddr, address newAdmin)
将治理权利转交给其余的 普通用户地址 或零地址。而一个合约不可成为管理者。
合约的管理者具备多个管理权限。管理者能够调用 destroy(address contractAddr)
接口以销毁合约,该操作就像通过合约调用 suicide()
函数那样。而 SponsorWhitelist 外部合约提供了一些管理员专用的函数。这些函数能够更新赞助者机制中的白名单。咱们将在随后进行介绍。
留神:对于所有和管理者权限相干的接口,不管调用胜利与否都不会在执行时触发任何谬误或异样。 例如,如果一个非管理者地址尝试将管理者地址转移给其本人,该交易会胜利但不会造成任何改变。
如果合约领有非零的管理者地址,ConfluxScan 会将合约标记为调试模式。因而请记住,如果你认为你的合约曾经筹备好进入理论生产环境,你该当将管理者地址设置为零地址。
AdminControl
合约同时也提供了一个能够被任何人调用的查问接口 getAdmin(address contractAddr)
。
须要留神的细节:
- 默认管理者(交易发送者)是在合约开始创立时设置的。因而,如果发送者
A
创立合约B
并在合约构建时设置管理者为C
,在合约部署后合约的管理者为C
。 - 然而,如果发送者
A
调用合约B
,随后合约B
创立合约C
并在合约创立时将管理者设置为D
,则该设置会失败,起因是:C
合约的管理者是A
,但创立合约C
的发起者是B
。 - Conflux 会引入一种特地的规定。在案例 2 中,如果
D
是零地址,则管理者设置胜利。这意味着合约创立时能够显示地申明其不须要管理者。
样例
思考到您能够曾经部署了一个地址为 contract_addr
的合约。管理者能够通过调用AdminControl.setAdmin(contract_addr, new_admin)
以变更管理员以及通过调用AdminControl.destroy(contract_addr)
来销毁合约。
const PRIVATE_KEY = '0xxxxxxx';
const cfx = new Conflux({
url: 'http://test.confluxrpc.org',
logger: console,
});
const account = cfx.wallet.addPrivateKey(PRIVATE_KEY); // create account instance
const admin_contract = cfx.InternalContract('AdminControl')
// to change administrator
admin_contract.setAdmin(contract_addr, new_admin).sendTransaction({from: account,}).confirmed();
// to kill the contract
admin_contract.destroy(contract_addr).sendTransaction({from: account,}).confirmed();
SponsorWhitelistControl 合约
总览
Conflux 实现了一种资助机制来补贴用户对智能合约的应用。因而,只有对合约调用的交易被资助(通常由 Dapps 的运营商资助),应用余额为零的新帐户也可能调用智能合约。通过引入内置的 SponsorControl 合约可能记录和治理智能合约的资助信息。
在进行子调用(Message Call)时,Conflux 不会再次查看资助状况。例如,如果一个普通用户地址 A
调用合约 B
,而后合约 B
调用合约 C
, Conflux 仅仅会查看 A
是否被合约 B
资助。如果 A
被资助,B
会在交易执行过程中累赘全副的燃料费用和 / 或存储抵押费用,蕴含 B
调用 C
的费用。换句话说,只有交易的发送者能力被资助,B
不可能被资助。
SponsorControl合约为每一个用户建设的合约保留了如下的信息:
sponsor_for_gas
:是提供燃料补贴的账户;sponsor_for_collateral
:是提供存储抵押补贴的账户;sponsor_balance_for_gas
:可用于燃料补贴的余额;sponsor_balance_for_collateral
:可用于提供存储抵押补贴的余额;sponsor_limit_for_gas_fee
:是向每笔交易资助燃料费用的上界;whitelist
:有资格取得补贴的普通用户地址列表,全零地址则代表所有用户地址。只有合约本身和管理员有权限改变该列表。
有两种资源可能被资助:燃料费用和存储抵押物。
- * 对于燃料费用: 如果一笔交易应用非空的
sponsor_for_gas
调用智能合约且交易发送者处于合约的whitelist
列表内,且交易指定的燃料费用在sponsor_limit_for_gas_fee
范畴内,交易的燃料耗费将从合约的sponsor_balance_for_gas
中领取(如果足够的话),而不是由交易发送者的账户余额领取,如果sponsor_balance_for_gas
无奈承当燃料耗费,则交易失败。否则,交易发送者应领取燃料费用。 - 对于存储抵押物: 如果一笔交易应用非空的
sponsor_balance_for_collateral
调用智能合约且交易发送者处于合约的whitelist
列表内,在执行交易的过程中存储抵押物将从智能合约的sponsor_balance_for_collateral
中扣除,并将这些批改后的存储条目所有者相应设置为合约地址。否则,交易发送方应在执行过程中领取存储抵押物。
当一个合约被创立的时候,它的 sponsor_for_gas
和 sponsor_for_collateral
会被置为零地址,相应的燃料补贴余额也是零。提供燃料补贴的账户和存储押金补贴的账户都能够通过与 SponsorControl 合约交互实现。合约以后的资助账户能够间接追加补贴余额,也能够在满足肯定条件下进步 sponsor_limit_for_gas_fee
。其余普通用户账户如果提供高于以后余额的资金,能够将原先的赞助者取而代之。更具体的细节如下。
赞助者替换
为了替换合约的 sponsor_for_gas
,新的赞助者须要调用函数 setSponsorForGas(address contractAddr, uint upperBound)
并向内置合约转移一笔资金。只有在满足下述条件时能力实现燃料费用赞助者的替换:
- 转移的资金该当比合约以后的
sponsor_balance_for_gas
高。 sponsor_limit_for_gas_fee
的新值(被指定为upperBound
参数)该当不小于原赞助者的限度,除非本来的sponsor_balance_for_gas
无奈累赘原赞助者的限度。- 转移的资金应为新限额的 1000 倍以上,以便足以补贴至多
1000
次调用合约的交易。
如果上述条件满足,残余的 sponsor_balance_for_gas
会返还给原赞助人 sponsor_for_gas
,随后转给内置合约的资金被注入 sponsor_balance_for_gas
。sponsor_for_gas
以及 sponsor_limit_for_gas_fee
会依据新赞助人指定的值进行更新。如果上述条件没有满足,会触发异样。
对 sponsor_for_collateral
的替换与替换燃料赞助者的形式相似,解决没有燃料限额的逻辑。办法是 setSponsorForCollateral(address contractAddr)
。新的赞助者须要向合约转移一笔比目前余额更多的资金。随后以后 sponsor_for_collateral
资助的金额会被全副退还,即 sponsor_balance_for_collateral
和合约以后所有存储抵押金之和,两个与存储押金资助相干的字段将依照新赞助者的要求进行相应的变更。
一个合约账户也被容许成为赞助者。
进步资助额度
赞助者能够在无需更换赞助者的状况下提供额定的资助资金。在该状况下,赞助人须要调用函数 setSponsorForGas(address contractAddr, uint upperBound)
或 setSponsorForCollateral(address contractAddr)
并满足赞助者替换的三条要求中的条件 2,3. 如果满足相干需要,交易的资助资金会被退出资助余额中且 sponsor_limit_for_gas_fee
也会相应地被更新。
白名单列表保护
只有合约自身或合约管理者能够更新合约白名单列表。赞助者则无权变更白名单列表。
合约能够通过调用 addPrivilege(address[] memory)
将任何地址退出到白名单列表中。这意味着如果 sponsor_for_gas
被设置,合约会为白名单列表中的账户领取存储押金。全零地址是一个非凡的地址 0x0000000000000000000000000000000000000000
。如果该地址被退出白名单列表,则所有调用该合约的交易都会被资助。合约还能够调用办法 removePrivilege(address[] memory)
将局部失常账户从白名单列表中移除。移除一个不存在的地址不会导致谬误或异样。
须要留神的细节:
- 一个合约地址也可被退出到白名单列表中,但该操作无任何意义,因为只有交易的发送者能够被资助。
合约的管理者能够应用 addPrivilegeByAdmin(address contractAddr, address[] memory addresses)
及 removePrivilegeByAdmin(address contractAddr, address[] memory addresses)
以保护白名单列表。
样例
假设你有一个如下所示的简略合约。
pragma solidity >=0.4.15;
import "https://github.com/Conflux-Chain/conflux-rust/blob/master/internal_contract/contracts/SponsorWhitelistControl.sol";
contract CommissionPrivilegeTest {mapping(uint => uint) public ss;
function add(address account) public payable {SponsorWhitelistControl cpc = SponsorWhitelistControl(0x0888000000000000000000000000000000000001);
address[] memory a = new address[](1);
a[0] = account;
cpc.addPrivilege(a);
}
function remove(address account) public payable {SponsorWhitelistControl cpc = SponsorWhitelistControl(0x0888000000000000000000000000000000000001);
address[] memory a = new address[](1);
a[0] = account;
cpc.removePrivilege(a);
}
function foo() public payable {}
function par_add(uint start, uint end) public payable {for (uint i = start; i < end; i++) {ss[i] = 1;
}
}
}
部署合约且地址为 contract_addr
如果有些人心愿资助燃料费用,他 / 她能够发送如下的交易:
const PRIVATE_KEY = '0xxxxxxx';
const cfx = new Conflux({
url: 'http://test.confluxrpc.org',
logger: console,
});
const account = cfx.wallet.addPrivateKey(PRIVATE_KEY); // create account instance
const sponsor_contract = cfx.InternalContract('SponsorWhitelistControl');
sponsor_contract.setSponsorForGas(contract_addr, your_upper_bound).sendTransaction({
from: account,
value: your_sponsor_value
}).confirmed();
如果须要资助存储抵押物,能够简略的将 setSponsorForGas(contract_addr, your_upper_bound)
代替为 setSponsorForCollateral(contract_addr)
即可。
之后你能够应用 addPrivilege
和 removePrivilege
为你的合约保护 whitelist
。非凡的全零地址 0x0000000000000000000000000000000000000000
则示意 所有人都处于 whitelist
中。须要审慎应用。
you_contract.add(white_list_addr).sendTransaction({from: account,})
you_contract.remove(white_list_addr).sendTransaction({from: account,})
随后在 whitelist
中的账户在调用 you_contract.foo()
或 you_contract.par_add(1, 10)
时,不会领取任何费用。
Staking 合约
总览
Conflux 引入权利质押机制的起因有两个:一、权利机制提供了一种对占用存储空间更好的免费形式(相比于“一次付费,永恒占用”)。二、该机制还有助于定义扩散治理中的投票权。
在顶层,Conflux 实现了一个内置的 Staking 合约,以记录所有账户的权利信息。通过向该合约发送交易,用户(包含内部用户和智能合约)能够存入 / 提取资金,也被称为合约内的权利。质押资金的利息在提款时发放,其数量取决于提款金额和质押时长。
用户能够通过调用 deposit(uint amount)
来存入用于抵押的金额,随后 amount
数量的资金将从其 balance
移至 stakingBalance
. 须要留神的是该函数不是 payable
的,用户只须要指定抵押的金额而无需将资金转入到外部合约中。
用户还能够通过调用 withdraw(uint amount)
来提取余额。调用者能够调用该函数从 Conflux 内嵌质押合约提取局部代币。这会触发利息结算。抵押资金和利息将会及时的转入用户余额中。所有提款的程序将依照先到先服务的形式进行解决。
利率
目前的年化利率为 4.08%。复利是以区块的颗粒度来实现的。
如果在执行区块 B
中的交易时,尝试提取 价值 v
并且在区块 B'
中抵押的资金,其利息计算公式如下:
interest issued = v * (1 + 4% / 63072000)^T - v
其中 T = BlockNo(B)−BlockNo(B')
是以区块数目掂量的质押时长,而 63072000
是在区块生成工夫为 0.5
秒前提下 365
天生成区块数目的期望值。
锁定与投票权
通过锁定质押余额,用户可获取 投票权 以进一步进行链上治理。通过调用 voteLock(uint amount, uint unlock_block_number)
函数,一个帐户能够做出如下承诺:“我的 stakingBalance
在将来 unlock_block_number
中将始终具备至多 amount
的资金”。单个账户能够做出多个承诺,比如说“往年我将至多持有 10CFX,并且在明年至多持有 5CFX。”一旦做出承诺,无奈勾销!然而该账户可通过锁定更多的金额笼罩原有的承诺。每当账户尝试提取 stakingBalance
时,外部合约会查看残余余额是否与锁定承诺吻合。
在此处咱们将通过几个样例介绍锁定余额的逻辑细节。假如 Conflux 在往年的剩余时间将产生 x
个区块,在明年的产生 y
个区块。因为 Conflux 网络每秒能生成两个区块,因而 y
近似等于 2 * 60 * 60 * 24 * 365
。而 x
取决于您何时浏览到该文章。
- 假如一个账户的
stakingBalance
中有 10CFX,如果其调用voteLock(100 * 10^18, x)
,阐明账户尝试锁定 100CFX. 但因为其短少足够的stakingBalance
,交易执行失败。 - 然而,如果该账户调用
voteLock(8 * 10^18, x)
,则交易会胜利。 - 随后,如果该账户调用
voteLock(6 * 10^18, x+y)
,交易同样会胜利。这意味着交易执行后 2CFX 在往年完结时解锁,而另外 6CFX 会被锁定直到明年完结。 - 如果账户再调用
voteLock(0, x)
,没有任何事件会产生。在交易执行过程中交易不会触发谬误。内置合约会将此调用视为无意义的承诺:该帐户在步骤 2、3 做出的旧承诺无效的前提下,再次承诺在往年完结前至多锁定 0 CFX. - 如果该账户调用
voteLock(9 * 10^18, x+y)
,则两个较老的承诺将会因为“锁定 9CFX 直到明年完结是一个更强的承诺”被笼罩。
锁定对利息无任何影响。当账户胜利取出抵押余额时,利息会将照常计算。
在任何工夫,每一个锁定的金额将依据其解锁工夫被调配 0 到 1 的 表决权。锁定期超过 1 年的局部将领有全额的投票权力。查看 Conflux Protocol Specification 章节 8.3.2 获取更多信息。
样例
const PRIVATE_KEY = '0xxxxxxx';
const cfx = new Conflux({
url: 'http://test.confluxrpc.org',
logger: console,
});
const account = cfx.wallet.addPrivateKey(PRIVATE_KEY); // create account instance
const staking_contract = cfx.InternalContract('Staking');
// deposit some amount of tokens
staking_contract.deposit(your_number_of_tokens).sendTransaction({from: account,}).confirmed();
// withdraw some amount of tokens
staking_contract.withdraw(your_number_of_tokens).sendTransaction({from: account,}).confirmed();
// lock some tokens until some block number
staking_contract.voteLock(your_number_of_tokens, your_unlock_block_number).sendTransaction({from: account,}).confirmed();
相干资料库:
- Conflux 开发材料包
- conflux-chain github
- conflux-fans github