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 );
}
}
}