Equal-share partnership pool by Jamie Hale, deployed three weeks before the Ethereum hard fork to co-invest in The DAO. Still holds 12 ETH.
Historical Significance
An early example of a fully-templated, open-source DAO/multisig pattern on Ethereum, designed and used during the very heat of the DAO crisis. Predates Gnosis Multisig (Dec 2016), Aragon (2017), and most of the DAO tooling ecosystem. The same template was used by multiple groups during 2016 to pool ETH for The DAO and other early ICOs. Jamie Hale's repo (and the related celeduc fork) is one of the cleanest period-accurate references for how on-chain partnership/multisig logic was written in pure Solidity v0.3.x before any of the standard libraries existed.
Context
Deployed 27 June 2016, in the brief window between the DAO hack (17 June 2016) and the Ethereum hard fork (20 July 2016). Solidity was at v0.3.2, optimizer was on by default in truffle, and modifiers used the single-underscore syntax (_) rather than the modern _;. The throw keyword was still the standard error mechanism; revert would not exist until Solidity v0.4.10. The contract pre-dates Gnosis MultiSigWallet, OpenZeppelin's roles, and the Consensys MultiSig template.
Key Facts
Description
Partnership is an equal-share investment pool from Jamie Hale's open-source dao_partnership project. Each partner buys in by paying sharePrice wei into the contract; once every pre-registered partner has paid, funded flips true and the partnership goes live. From that point, partners propose transactions with proposeTransaction(to, value, data, description) and every other partner must call confirmTransaction(id) before executeTransaction(id) will dispatch the call. The contract supports proportional dividends via distribute(addr, amount) and distributeEvenly(amount), partner loans repayable via repayLoan(addr, value), and a pull-based withdraw pattern through withdrawableAmounts(addr) and withdraw(uint). dissolve(addr) lets the last living partner shut the contract down and forward the remainder.
Deployed 27 June 2016 at block 1,781,342, four days after the DAO recursive-call exploit drained 3.6M ETH and three weeks before the Ethereum community executed the hard fork at block 1,920,000 that restored those funds. The deployer 0xd69104404a21cf359985c21988b959ace3880c83 subsequently sent transactions to The DAO at 0xbb9bc244d798123fde783fcc1c72d3bb8c189413, confirming this Partnership was used to pool ETH from multiple investors for a co-investment in The DAO.
The 12 ETH balance has been stuck in the contract since the hard fork: dissolution requires unanimous partner consent, and at least one of the participating keys is presumably lost or unreachable.
Source matches commit 8d6b374ec5 of jamiehale/dao_partnership (the last commit before deployment). Compiled with Solidity v0.3.2+commit.81ae2a78 (optimizer ON), which produces a byte-for-byte runtime match.
Source Verified
Source from jamiehale/dao_partnership at commit 8d6b374ec5 (the last commit before the 27 June 2016 deployment). soljson-v0.3.2 reproduces a byte-for-byte runtime match (SHA-256 8a2bdb0db955f942eb60d75d0dd36f5a3180431c55dbdde895bb968f27f9f5cb). v0.3.0/v0.3.1 also match; v0.3.3 onward diverged.
Heuristic Analysis
The following characteristics were detected through bytecode analysis and may not be accurate.
Homestead Era
The first planned hard fork. Removed the canary contract, adjusted gas costs.
Bytecode Overview
Verified Source Available
Source verified through compiler archaeology and exact bytecode matching.
View Verification ProofShow source code (Solidity)
/// Partnership
/// Requires all pre-defined partners agreement on transactions and operations.
contract Partnership
{
event Funded();
event Deposit(address _from, uint _value);
event TransactionProposed(bytes32 _id, address _initiator, string _description);
event TransactionCanceled(bytes32 _id, address _actor);
event TransactionPassed(bytes32 _id, address _finalSigner, string _description);
event TransactionSent(bytes32 _transaction, address _executor, string _description);
event Withdrawal(address _partner, uint _amount);
/// Price in wei of each equal share of the partnership
uint public sharePrice;
/// Flag indicating whether or not all partners have paid for their share
bool public funded;
/// Array of partner addresses
address [] public partners;
/// Collection of partner records
mapping(address => Partner) public partnerRecords;
/// Count of partners
uint public partnerCount;
/// Count of partners who have paid for their share
uint public paidPartnerCount;
/// Collection of pending transactions (ie send X ETH to Y with Z data)
mapping(bytes32 => Transaction) public transactions;
/// Count of transactions awaiting confirmation, execution, or cancelation
uint public activeTransactionCount;
/// Available withdrawals
mapping(address => uint) public withdrawableAmounts;
struct Partner {
/// Flag indicating that this record has been initialized
bool isPartner;
/// Flag indicating that the partner has paid for their share
bool paid;
/// Total amount loaned to the partnership by the partner
uint loanBalance;
}
struct Transaction {
/// Flag indicating that this record has been initialized
bool valid;
/// Proposed recipient for the transaction (0 indicates a new contract will be created)
address to;
/// Proposed amount to send (0 indicates no wei)
uint value;
/// Optional array of data to send with the transaction
bytes data;
/// Optional description of proposed transaction
string description;
/// Account that created the transaction
address creator;
/// Total number of partners that have confirmed/voted
uint voteCount;
/// Collection of partners that have confirmed/voted
mapping(address => uint) votes;
/// Flag indicating that all partners have confirmed
bool passed;
/// Flag indicatint that the transaction has been sent
bool sent;
}
modifier onlyWhenFunded {
if (!funded)
throw;
_
}
modifier onlyByPartner {
if (!isPartner(msg.sender))
throw;
_
}
modifier onlyByDao {
if (msg.sender != address(this))
throw;
_
}
modifier onlyValidTransaction(bytes32 _id) {
if (!transactions[_id].valid)
throw;
_
}
modifier onlyPassedTransaction(bytes32 _id) {
if (!transactions[_id].passed)
throw;
_
}
modifier onlyUnpassedTransaction(bytes32 _id) {
if (transactions[_id].passed)
throw;
_
}
modifier onlyTransactionCreator(bytes32 _id) {
if (transactions[_id].creator != msg.sender)
throw;
_
}
modifier onlyUnconfirmedBySender(bytes32 _id) {
if (transactions[_id].votes[msg.sender] == 1)
throw;
_
}
modifier onlyUnsentTransaction(bytes32 _id) {
if (transactions[_id].sent)
throw;
_
}
modifier mustBePartner(address _recipient) {
if (!isPartner(_recipient))
throw;
_
}
modifier noMoreThanLoan(address _recipient, uint _amount) {
if (_amount > partnerRecords[_recipient].loanBalance)
throw;
_
}
modifier cannotExceedWithdrawableAmount(uint _amount) {
if (_amount > withdrawableAmounts[msg.sender])
throw;
_
}
modifier cannotExceedContractBalance(uint _amount) {
if (_amount > this.balance)
throw;
_
}
modifier onlyValidBeneficiary(address _beneficiary) {
// ignore unset beneficiary
if (_beneficiary == 0)
throw;
// prevent lost balance
if (_beneficiary == address(this))
throw;
_
}
function Partnership(address[] _partners, uint _sharePrice) {
funded = false;
partners = _partners;
sharePrice = _sharePrice;
for (uint i = 0; i < _partners.length; i++) {
partnerRecords[_partners[i]].isPartner = true;
}
partnerCount = _partners.length;
paidPartnerCount = 0;
}
/// This executes when funds are sent to the contract
function() {
if (msg.value > 0) {
if (funded) {
if (isPartner(msg.sender)) {
partnerRecords[msg.sender].loanBalance += msg.value;
}
}
else {
if (isPartner(msg.sender)) {
if (partnerRecords[msg.sender].paid) {
throw;
}
else {
if (msg.value == sharePrice) {
partnerRecords[msg.sender].paid = true;
paidPartnerCount = paidPartnerCount + 1;
if (paidPartnerCount == partnerCount) {
funded = true;
Funded();
}
}
else {
throw;
}
}
}
else {
throw;
}
}
Deposit(msg.sender, msg.value);
}
}
/// Adds a proposed transaction to be confirmed by other partners
function proposeTransaction(address _to, uint _value, bytes _data, string _description) onlyWhenFunded onlyByPartner external returns (bytes32) {
// generate hash for easy specification in confirm and execute
bytes32 id = sha3(msg.data, block.number);
// grab the presumably blank transaction
var transaction = transactions[id];
transaction.valid = true;
transaction.to = _to;
transaction.value = _value;
transaction.data = _data;
transaction.description = _description;
transaction.creator = msg.sender;
transaction.voteCount = 1;
transaction.votes[msg.sender] = 1;
transaction.passed = false;
transaction.sent = false;
activeTransactionCount += 1;
TransactionProposed(id, msg.sender, _description);
return id;
}
/// Cancels a transaction that has not yet passed
function cancelTransaction(bytes32 _id) onlyWhenFunded onlyByPartner onlyValidTransaction(_id) onlyUnpassedTransaction(_id) onlyTransactionCreator(_id) external {
delete transactions[_id];
activeTransactionCount -= 1;
TransactionCanceled(_id, msg.sender);
}
/// Confirms an existing proposed transaction
function confirmTransaction(bytes32 _id) onlyWhenFunded onlyByPartner onlyValidTransaction(_id) onlyUnconfirmedBySender(_id) external {
var transaction = transactions[_id];
// register the vote
transaction.voteCount += 1;
transaction.votes[msg.sender] = 1;
if (transaction.voteCount == partnerCount) {
transaction.passed = true;
TransactionPassed(_id, msg.sender, transaction.description);
}
}
/// Executes a passed transaction
function executeTransaction(bytes32 _id) onlyWhenFunded onlyByPartner onlyPassedTransaction(_id) onlyUnsentTransaction(_id) external {
var transaction = transactions[_id];
// register the sent transaction
transaction.sent = true;
activeTransactionCount -= 1;
// send the transaction
if (transaction.to.call.value(transaction.value)(transaction.data)) {
TransactionSent(_id, msg.sender, transaction.description);
// clear the transaction structure to free memory
delete transactions[_id];
}
else {
// roll back if the call failed
transaction.sent = false;
activeTransactionCount += 1;
}
}
/// Distribute ETH to a partner or external recipient
function distribute(address _recipient, uint _amount) onlyByDao external {
withdrawableAmounts[_recipient] += _amount;
}
/// Distribute ETH evenly amongst all partners
function distributeEvenly(uint _amount) onlyByDao external {
uint payout = _amount / partnerCount;
for (uint i = 0; i < partnerCount; i++) {
withdrawableAmounts[partners[i]] += payout;
}
}
/// Mark down partner's loan and make it available for withdrawal
function repayLoan(address _recipient, uint _amount) onlyByDao mustBePartner(_recipient) noMoreThanLoan(_recipient, _amount) external {
partnerRecords[_recipient].loanBalance -= _amount;
withdrawableAmounts[_recipient] += _amount;
}
/// Allow partner or external recipient to withdraw funds marked as withdrawable
function withdraw(uint _amount) onlyWhenFunded cannotExceedWithdrawableAmount(_amount) cannotExceedContractBalance(_amount) external {
// mark the withdrawal as successful
withdrawableAmounts[msg.sender] -= _amount;
// send the wei
if (msg.sender.send(_amount)) {
Withdrawal(msg.sender, _amount);
}
else {
// roll back if the send failed
withdrawableAmounts[msg.sender] += _amount;
}
}
/// Dissolve DAO and send the remaining ETH to a beneficiary
function dissolve(address _beneficiary) onlyByDao onlyValidBeneficiary(_beneficiary) external {
suicide(_beneficiary);
}
function isPartner(address _address) internal returns (bool) {
return partnerRecords[_address].isPartner;
}
}