1.基本原理
奖励是激励机制的最简单形式:为人们提供完成任务的令牌。以太坊区块链提供了许多好处来支持以下激励机制:
- 与世界各地的个人进行廉价交易的能力
- 能够将资金锁定在代管合同(赏金)中的功能,当接受任务完成或可交付证明时,便可以支配资金
- 以开放且可互操作的方式托管这些赏金的能力,因此可以使用许多不同类型的应用程序从共享的流动资金池(没有人控制)中创建,探索和完成赏金。通过这种方式,StandardBounties使团队能够通过一个应用程序(例如其DAO)创建赏金,并立即将赏金立即在多个赏金市场上列出,以最大程度地扩大赏金的覆盖面并提高市场效率。
2.实施
赏金中有几种关键类型的用户:
Bounty Issuers
是有权删除赏金并编辑与赏金相关的详细信息的地址列表。Bounty Approvers
是有权接收赏金提交地址的地址列表。(注意:发布者不被认为也是批准者,但可以根据需要添加自己)Bounty Contributors
是对给定赏金有贡献的任何地址Bounty Fulfillers
是任何对给定赏金提交内容的贡献者地址Bounty Submitters
是代表自己或他人提交成就的任何地址
这些参与者共同努力,通过激励的力量来部署资金并塑造人类行为。
赏金生命周期中有几个核心动作,某些用户可以执行:
- 任何人都可以
issue
赏金,指定赏金的详细信息,并将关联的IPFS哈希锚定在StandardBounties智能合约内的链上 - 任何人都可以
contribute
悬赏,指定他们想要添加到端口的令牌数量。 - 任何人都可以
fulfill
悬赏,提交贡献者列表以及详细信息和可交付成果的IPFS哈希。 - 赏金的任何批准者都可以
accept
兑现,提交他们希望每个贡献者获得的代币数量。
这些行动构成了赏金的核心生命周期,支持资金流入各种赏金,并随着任务完成而流出。
各种用户可以执行一些其他操作:
- 只要赏金的期限已过且未接受任何提交,任何贡献者都可以将其捐款退还给赏金。
- 任何发行人都可以根据需要退还其他用户的捐款(即使还没有截止日期或赏金已经支付了一部分资金)
- 任何发行人都可以耗尽赏金中一部分资金的赏金
- 任何人都可以执行概括化的操作
action
,提交IPFS哈希表,该哈希表存储其操作的详细信息(例如,评论,提交其完成赏金的意图等) - 任何提交者都可以更新其提交内容,并对提交数据或贡献者列表进行更改
- 任何批准者都可以同时提交并接受链下履行,一成不变地记录交换,同时省去了先行提交链上履行的需求
- 任何发行人都可以更改赏金的任何细节,但与赏金相关的代币合约不可更改。
合约写法
pragma solidity 0.5.12;
pragma experimental ABIEncoderV2;
import "./StandardBounties.sol";
contract BountiesMetaTxRelayer {
// This contract serves as a relayer for meta txns being sent to the Bounties contract
StandardBounties public bountiesContract;
mapping(address => uint) public replayNonce;
constructor(address _contract) public {
bountiesContract = StandardBounties(_contract);
}
function metaIssueBounty(
bytes memory signature,
address payable[] memory _issuers,
address[] memory _approvers,
string memory _data,
uint _deadline,
address _token,
uint _tokenVersion,
uint _nonce)
public
returns (uint)
{
bytes32 metaHash = keccak256(abi.encode(address(this),
"metaIssueBounty",
_issuers,
_approvers,
_data,
_deadline,
_token,
_tokenVersion,
_nonce));
address signer = getSigner(metaHash, signature);
//make sure signer doesn't come back as 0x0
require(signer != address(0));
require(_nonce == replayNonce[signer]);
//increase the nonce to prevent replay attacks
replayNonce[signer]++;
return bountiesContract.issueBounty(address(uint160(signer)),
_issuers,
_approvers,
_data,
_deadline,
_token,
_tokenVersion);
}
function metaIssueAndContribute(
bytes memory signature,
address payable[] memory _issuers,
address[] memory _approvers,
string memory _data,
uint _deadline,
address _token,
uint _tokenVersion,
uint _depositAmount,
uint _nonce)
public
payable
returns (uint)
{
bytes32 metaHash = keccak256(abi.encode(address(this),
"metaIssueAndContribute",
_issuers,
_approvers,
_data,
_deadline,
_token,
_tokenVersion,
_depositAmount,
_nonce));
address signer = getSigner(metaHash, signature);
//make sure signer doesn't come back as 0x0
require(signer != address(0));
require(_nonce == replayNonce[signer]);
//increase the nonce to prevent replay attacks
replayNonce[signer]++;
if (msg.value > 0){
return bountiesContract.issueAndContribute.value(msg.value)(address(uint160(signer)),
_issuers,
_approvers,
_data,
_deadline,
_token,
_tokenVersion,
_depositAmount);
} else {
return bountiesContract.issueAndContribute(address(uint160(signer)),
_issuers,
_approvers,
_data,
_deadline,
_token,
_tokenVersion,
_depositAmount);
}
}
function metaContribute(
bytes memory _signature,
uint _bountyId,
uint _amount,
uint _nonce)
public
payable
{
bytes32 metaHash = keccak256(abi.encode(address(this),
"metaContribute",
_bountyId,
_amount,
_nonce));
address signer = getSigner(metaHash, _signature);
//make sure signer doesn't come back as 0x0
require(signer != address(0));
require(_nonce == replayNonce[signer]);
//increase the nonce to prevent replay attacks
replayNonce[signer]++;
if (msg.value > 0){
bountiesContract.contribute.value(msg.value)(address(uint160(signer)), _bountyId, _amount);
} else {
bountiesContract.contribute(address(uint160(signer)), _bountyId, _amount);
}
}
function metaRefundContribution(
bytes memory _signature,
uint _bountyId,
uint _contributionId,
uint _nonce)
public
{
bytes32 metaHash = keccak256(abi.encode(address(this),
"metaRefundContribution",
_bountyId,
_contributionId,
_nonce));
address signer = getSigner(metaHash, _signature);
//make sure signer doesn't come back as 0x0
require(signer != address(0));
require(_nonce == replayNonce[signer]);
//increase the nonce to prevent replay attacks
replayNonce[signer]++;
bountiesContract.refundContribution(signer, _bountyId, _contributionId);
}
function metaRefundMyContributions(
bytes memory _signature,
uint _bountyId,
uint[] memory _contributionIds,
uint _nonce)
public
{
bytes32 metaHash = keccak256(abi.encode(address(this),
"metaRefundMyContributions",
_bountyId,
_contributionIds,
_nonce));
address signer = getSigner(metaHash, _signature);
//make sure signer doesn't come back as 0x0
require(signer != address(0));
require(_nonce == replayNonce[signer]);
//increase the nonce to prevent replay attacks
replayNonce[signer]++;
bountiesContract.refundMyContributions(signer, _bountyId, _contributionIds);
}
function metaRefundContributions(
bytes memory _signature,
uint _bountyId,
uint _issuerId,
uint[] memory _contributionIds,
uint _nonce)
public
{
bytes32 metaHash = keccak256(abi.encode(address(this),
"metaRefundContributions",
_bountyId,
_issuerId,
_contributionIds,
_nonce));
address signer = getSigner(metaHash, _signature);
//make sure signer doesn't come back as 0x0
require(signer != address(0));
require(_nonce == replayNonce[signer]);
//increase the nonce to prevent replay attacks
replayNonce[signer]++;
bountiesContract.refundContributions(signer, _bountyId, _issuerId, _contributionIds);
}
function metaDrainBounty(
bytes memory _signature,
uint _bountyId,
uint _issuerId,
uint[] memory _amounts,
uint _nonce)
public
{
bytes32 metaHash = keccak256(abi.encode(address(this),
"metaDrainBounty",
_bountyId,
_issuerId,
_amounts,
_nonce));
address payable signer = address(uint160(getSigner(metaHash, _signature)));
//make sure signer doesn't come back as 0x0
require(signer != address(0));
require(_nonce == replayNonce[signer]);
//increase the nonce to prevent replay attacks
replayNonce[signer]++;
bountiesContract.drainBounty(signer, _bountyId, _issuerId, _amounts);
}
function metaPerformAction(
bytes memory _signature,
uint _bountyId,
string memory _data,
uint256 _nonce)
public
{
bytes32 metaHash = keccak256(abi.encode(address(this),
"metaPerformAction",
_bountyId,
_data,
_nonce));
address signer = getSigner(metaHash, _signature);
//make sure signer doesn't come back as 0x0
require(signer != address(0));
require(_nonce == replayNonce[signer]);
//increase the nonce to prevent replay attacks
replayNonce[signer]++;
bountiesContract.performAction(signer, _bountyId, _data);
}
function metaFulfillBounty(
bytes memory _signature,
uint _bountyId,
address payable[] memory _fulfillers,
string memory _data,
uint256 _nonce)
public
{
bytes32 metaHash = keccak256(abi.encode(address(this),
"metaFulfillBounty",
_bountyId,
_fulfillers,
_data,
_nonce));
address signer = getSigner(metaHash, _signature);
//make sure signer doesn't come back as 0x0
require(signer != address(0));
require(_nonce == replayNonce[signer]);
//increase the nonce to prevent replay attacks
replayNonce[signer]++;
bountiesContract.fulfillBounty(signer, _bountyId, _fulfillers, _data);
}
function metaUpdateFulfillment(
bytes memory _signature,
uint _bountyId,
uint _fulfillmentId,
address payable[] memory _fulfillers,
string memory _data,
uint256 _nonce)
public
{
bytes32 metaHash = keccak256(abi.encode(address(this),
"metaUpdateFulfillment",
_bountyId,
_fulfillmentId,
_fulfillers,
_data,
_nonce));
address signer = getSigner(metaHash, _signature);
//make sure signer doesn't come back as 0x0
require(signer != address(0));
require(_nonce == replayNonce[signer]);
//increase the nonce to prevent replay attacks
replayNonce[signer]++;
bountiesContract.updateFulfillment(signer, _bountyId, _fulfillmentId, _fulfillers, _data);
}
function metaAcceptFulfillment(
bytes memory _signature,
uint _bountyId,
uint _fulfillmentId,
uint _approverId,
uint[] memory _tokenAmounts,
uint256 _nonce)
public
{
bytes32 metaHash = keccak256(abi.encode(address(this),
"metaAcceptFulfillment",
_bountyId,
_fulfillmentId,
_approverId,
_tokenAmounts,
_nonce));
address signer = getSigner(metaHash, _signature);
//make sure signer doesn't come back as 0x0
require(signer != address(0));
require(_nonce == replayNonce[signer]);
//increase the nonce to prevent replay attacks
replayNonce[signer]++;
bountiesContract.acceptFulfillment(signer,
_bountyId,
_fulfillmentId,
_approverId,
_tokenAmounts);
}
function metaFulfillAndAccept(
bytes memory _signature,
uint _bountyId,
address payable[] memory _fulfillers,
string memory _data,
uint _approverId,
uint[] memory _tokenAmounts,
uint256 _nonce)
public
{
bytes32 metaHash = keccak256(abi.encode(address(this),
"metaFulfillAndAccept",
_bountyId,
_fulfillers,
_data,
_approverId,
_tokenAmounts,
_nonce));
address signer = getSigner(metaHash, _signature);
//make sure signer doesn't come back as 0x0
require(signer != address(0));
require(_nonce == replayNonce[signer]);
//increase the nonce to prevent replay attacks
replayNonce[signer]++;
bountiesContract.fulfillAndAccept(signer,
_bountyId,
_fulfillers,
_data,
_approverId,
_tokenAmounts);
}
function metaChangeBounty(
bytes memory _signature,
uint _bountyId,
uint _issuerId,
address payable[] memory _issuers,
address payable[] memory _approvers,
string memory _data,
uint _deadline,
uint256 _nonce)
public
{
bytes32 metaHash = keccak256(abi.encode(address(this),
"metaChangeBounty",
_bountyId,
_issuerId,
_issuers,
_approvers,
_data,
_deadline,
_nonce));
address signer = getSigner(metaHash, _signature);
//make sure signer doesn't come back as 0x0
require(signer != address(0));
require(_nonce == replayNonce[signer]);
//increase the nonce to prevent replay attacks
replayNonce[signer]++;
bountiesContract.changeBounty(signer,
_bountyId,
_issuerId,
_issuers,
_approvers,
_data,
_deadline);
}
function metaChangeIssuer(
bytes memory _signature,
uint _bountyId,
uint _issuerId,
uint _issuerIdToChange,
address payable _newIssuer,
uint256 _nonce)
public
{
bytes32 metaHash = keccak256(abi.encode(address(this),
"metaChangeIssuer",
_bountyId,
_issuerId,
_issuerIdToChange,
_newIssuer,
_nonce));
address signer = getSigner(metaHash, _signature);
//make sure signer doesn't come back as 0x0
require(signer != address(0));
require(_nonce == replayNonce[signer]);
//increase the nonce to prevent replay attacks
replayNonce[signer]++;
bountiesContract.changeIssuer(signer,
_bountyId,
_issuerId,
_issuerIdToChange,
_newIssuer);
}
function metaChangeApprover(
bytes memory _signature,
uint _bountyId,
uint _issuerId,
uint _approverId,
address payable _approver,
uint256 _nonce)
public
{
bytes32 metaHash = keccak256(abi.encode(address(this),
"metaChangeApprover",
_bountyId,
_issuerId,
_approverId,
_approver,
_nonce));
address signer = getSigner(metaHash, _signature);
//make sure signer doesn't come back as 0x0
require(signer != address(0));
require(_nonce == replayNonce[signer]);
//increase the nonce to prevent replay attacks
replayNonce[signer]++;
bountiesContract.changeApprover(signer,
_bountyId,
_issuerId,
_approverId,
_approver);
}
function metaChangeData(
bytes memory _signature,
uint _bountyId,
uint _issuerId,
string memory _data,
uint256 _nonce)
public
{
bytes32 metaHash = keccak256(abi.encode(address(this),
"metaChangeData",
_bountyId,
_issuerId,
_data,
_nonce));
address signer = getSigner(metaHash, _signature);
//make sure signer doesn't come back as 0x0
require(signer != address(0));
require(_nonce == replayNonce[signer]);
//increase the nonce to prevent replay attacks
replayNonce[signer]++;
bountiesContract.changeData(signer,
_bountyId,
_issuerId,
_data);
}
function metaChangeDeadline(
bytes memory _signature,
uint _bountyId,
uint _issuerId,
uint _deadline,
uint256 _nonce)
public
{
bytes32 metaHash = keccak256(abi.encode(address(this),
"metaChangeDeadline",
_bountyId,
_issuerId,
_deadline,
_nonce));
address signer = getSigner(metaHash, _signature);
//make sure signer doesn't come back as 0x0
require(signer != address(0));
require(_nonce == replayNonce[signer]);
//increase the nonce to prevent replay attacks
replayNonce[signer]++;
bountiesContract.changeDeadline(signer,
_bountyId,
_issuerId,
_deadline);
}
function metaAddIssuers(
bytes memory _signature,
uint _bountyId,
uint _issuerId,
address payable[] memory _issuers,
uint256 _nonce)
public
{
bytes32 metaHash = keccak256(abi.encode(address(this),
"metaAddIssuers",
_bountyId,
_issuerId,
_issuers,
_nonce));
address signer = getSigner(metaHash, _signature);
//make sure signer doesn't come back as 0x0
require(signer != address(0));
require(_nonce == replayNonce[signer]);
//increase the nonce to prevent replay attacks
replayNonce[signer]++;
bountiesContract.addIssuers(signer,
_bountyId,
_issuerId,
_issuers);
}
function metaAddApprovers(
bytes memory _signature,
uint _bountyId,
uint _issuerId,
address[] memory _approvers,
uint256 _nonce)
public
{
bytes32 metaHash = keccak256(abi.encode(address(this),
"metaAddApprovers",
_bountyId,
_issuerId,
_approvers,
_nonce));
address signer = getSigner(metaHash, _signature);
//make sure signer doesn't come back as 0x0
require(signer != address(0));
require(_nonce == replayNonce[signer]);
//increase the nonce to prevent replay attacks
replayNonce[signer]++;
bountiesContract.addApprovers(signer,
_bountyId,
_issuerId,
_approvers);
}
function getSigner(
bytes32 _hash,
bytes memory _signature)
internal
pure
returns (address)
{
bytes32 r;
bytes32 s;
uint8 v;
if (_signature.length != 65){
return address(0);
}
assembly {
r := mload(add(_signature, 32))
s := mload(add(_signature, 64))
v := byte(0, mload(add(_signature, 96)))
}
if (v < 27){
v += 27;
}
if (v != 27 && v != 28){
return address(0);
} else {
return ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _hash)), v, r, s );
}
}
}