Skip to content

Commit

Permalink
exec results (#316)
Browse files Browse the repository at this point in the history
* exec results

* add proposal

* goerli manifest

* update script

* rollback

* rollback

* rollback

* address comments
  • Loading branch information
novaknole authored Mar 6, 2023
1 parent abf2e26 commit 7c363eb
Show file tree
Hide file tree
Showing 5 changed files with 365 additions and 89 deletions.
42 changes: 31 additions & 11 deletions packages/subgraph/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ interface TokenTransfer {
from: Bytes!
to: Bytes!
type: TransferType!
proposal: Proposal
proposal: IProposal
txHash: Bytes!
createdAt: BigInt!
}
Expand All @@ -77,7 +77,7 @@ type ERC20Transfer implements TokenTransfer @entity(immutable: true) {
amount: BigInt!
from: Bytes!
to: Bytes!
proposal: Proposal
proposal: IProposal
type: TransferType!
txHash: Bytes!
createdAt: BigInt!
Expand All @@ -90,7 +90,7 @@ type ERC721Transfer implements TokenTransfer @entity(immutable: true) {
tokenId: BigInt!
from: Bytes!
to: Bytes!
proposal: Proposal
proposal: IProposal
type: TransferType!
txHash: Bytes!
createdAt: BigInt!
Expand All @@ -103,7 +103,7 @@ type NativeTransfer implements TokenTransfer @entity(immutable: true) {
from: Bytes!
to: Bytes!
reference: String!
proposal: Proposal
proposal: IProposal
type: TransferType!
txHash: Bytes!
createdAt: BigInt!
Expand Down Expand Up @@ -137,7 +137,7 @@ type Action @entity {
value: BigInt!
data: Bytes!
dao: Dao!
proposal: Proposal!
proposal: IProposal!
execResult: Bytes
}

Expand Down Expand Up @@ -165,7 +165,7 @@ type Dao @entity {
permissions: [Permission!]! @derivedFrom(field: "dao")
pluginInstallations: [PluginInstallation!]! @derivedFrom(field: "dao")
plugins: [IPluginInstallation!]! @derivedFrom(field: "dao") # TODO: Delete or refactor after the plugins left the Aragon OSx subgraph
proposals: [Proposal!] @derivedFrom(field: "dao")
proposals: [IProposal!] @derivedFrom(field: "dao")
trustedForwarder: Bytes
signatureValidator: Bytes
standardCallbacks: [StandardCallback!] @derivedFrom(field: "dao")
Expand Down Expand Up @@ -273,13 +273,29 @@ interface IPluginInstallation {
### Entities below should move to there respective subgraph once plugin is seperated from the Aragon OSx
# Proposal

interface Proposal {
interface IProposal {
id: ID! # package + proposalId
dao: Dao!
creator: Bytes!
metadata: String
actions: [Action!]! @derivedFrom(field: "proposal")
allowFailureMap: BigInt!
failureMap: BigInt
executed: Boolean!
createdAt: BigInt!
startDate: BigInt!
endDate: BigInt!
executionTxHash: Bytes
}

type TransactionActionsProposal implements IProposal @entity {
id: ID!
dao: Dao!
creator: Bytes!
metadata: String
actions: [Action!]! @derivedFrom(field: "proposal")
allowFailureMap: BigInt!
failureMap: BigInt
executed: Boolean!
createdAt: BigInt!
startDate: BigInt!
Expand Down Expand Up @@ -348,11 +364,12 @@ type TokenVotingVote @entity {
updatedAt: BigInt!
}

type TokenVotingProposal implements Proposal @entity {
type TokenVotingProposal implements IProposal @entity {
id: ID! # package + proposalId
dao: Dao!
actions: [Action!]! @derivedFrom(field: "proposal")
allowFailureMap: BigInt!
failureMap: BigInt
plugin: TokenVotingPlugin!
proposalId: BigInt!
creator: Bytes!
Expand Down Expand Up @@ -419,11 +436,12 @@ type AddresslistVotingVote @entity {
updatedAt: BigInt!
}

type AddresslistVotingProposal implements Proposal @entity {
type AddresslistVotingProposal implements IProposal @entity {
id: ID! # package + proposalId
dao: Dao!
actions: [Action!]! @derivedFrom(field: "proposal")
allowFailureMap: BigInt!
failureMap: BigInt
plugin: AddresslistVotingPlugin!
proposalId: BigInt!
creator: Bytes!
Expand Down Expand Up @@ -479,13 +497,14 @@ type AdministratorAdminPlugin @entity {
plugin: AdminPlugin!
}

type AdminProposal implements Proposal @entity {
type AdminProposal implements IProposal @entity {
id: ID! # plugin + proposalId
dao: Dao!
creator: Bytes! # Administrator address
metadata: String
actions: [Action!]! @derivedFrom(field: "proposal")
allowFailureMap: BigInt!
failureMap: BigInt
executed: Boolean!
createdAt: BigInt!
startDate: BigInt!
Expand Down Expand Up @@ -528,11 +547,12 @@ type MultisigProposalApprover @entity(immutable: true) {
createdAt: BigInt!
}

type MultisigProposal implements Proposal @entity {
type MultisigProposal implements IProposal @entity {
id: ID! # plugin + proposalId
dao: Dao!
actions: [Action!]! @derivedFrom(field: "proposal")
allowFailureMap: BigInt!
failureMap: BigInt
plugin: MultisigPlugin!
proposalId: BigInt!
creator: Bytes!
Expand Down
76 changes: 69 additions & 7 deletions packages/subgraph/src/dao/dao.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Bytes, store} from '@graphprotocol/graph-ts';
import {BigInt, Bytes, store} from '@graphprotocol/graph-ts';

import {
MetadataSet,
Expand All @@ -16,7 +16,9 @@ import {
Dao,
ContractPermissionId,
Permission,
StandardCallback
StandardCallback,
Action,
TransactionActionsProposal
} from '../../generated/schema';

import {ADDRESS_ZERO} from '../utils/constants';
Expand All @@ -32,6 +34,7 @@ import {
ERC721_transferFrom,
onERC721Received
} from '../utils/tokens/common';
import {updateProposalWithFailureMap} from './utils';

export function handleMetadataSet(event: MetadataSet): void {
let daoId = event.address.toHexString();
Expand Down Expand Up @@ -61,16 +64,75 @@ export function handleCallbackReceived(event: CallbackReceived): void {
}

export function handleExecuted(event: Executed): void {
let proposalId = event.params.actor
.toHexString()
.concat('_')
.concat(event.params.callId.toHexString());

// Not an effective solution, until each plugin has
// its own subgraph separately.
// If proposal found, update its failureMap.
let wasUpdated = updateProposalWithFailureMap(
proposalId,
event.params.failureMap
);

// If not updated, proposal wasn't found which means,
// it was called by the address that
// Subgraph doesn't index, in which case, we still create
// proposal entity in order to group its actions together.
if (!wasUpdated) {
// The id generation must include hash + logindex
// This is important to not allow overwriting. since
// the uniqueness of callId isn't checked in DAO, there
// might be a case when 2 different tx might end up having the same
// proposal ID which will cause overwrite. In case of plugins,
// It's plugin's responsibility to pass unique callId per execute.
proposalId = proposalId
.concat('_')
.concat(event.transaction.hash.toHexString())
.concat('_')
.concat(event.transactionLogIndex.toHexString());
let proposal = new TransactionActionsProposal(proposalId);
proposal.dao = event.address.toHexString();
proposal.createdAt = event.block.timestamp;
proposal.endDate = event.block.timestamp;
proposal.startDate = event.block.timestamp;
proposal.creator = event.params.actor;
proposal.executionTxHash = event.transaction.hash;
// Since DAO doesn't emit allowFailureMap by mistake, we got no choice now.
// In such case, `allowFailureMap` shouldn't be fully trusted.
proposal.allowFailureMap = BigInt.zero();
proposal.executed = true;
proposal.failureMap = event.params.failureMap;
proposal.save();
}

let actions = event.params.actions;

for (let index = 0; index < actions.length; index++) {
const action = actions[index];
let proposalId = event.params.actor
.toHexString()
.concat('_')
.concat(event.params.callId.toHexString());

if (action.data.toHexString() == '0x') {
let actionId = proposalId.concat('_').concat(index.toString());
let actionEntity = Action.load(actionId);

// In case the execute on the dao is called by the address
// That we don't currently index for the actions in the subgraph,
// we fallback and still create an action.
// NOTE that it's important to generate action id differently to not allow overwriting.
if (!actionEntity) {
actionEntity = new Action(actionId);
actionEntity.to = action.to;
actionEntity.value = action.value;
actionEntity.data = action.data;
actionEntity.proposal = proposalId;
actionEntity.dao = event.address.toHexString();
}

actionEntity.execResult = event.params.execResults[index];
actionEntity.save();

if (action.data.toHexString() == '0x' && action.value.gt(BigInt.zero())) {
handleNativeAction(
event.address,
action.to,
Expand Down
89 changes: 37 additions & 52 deletions packages/subgraph/src/dao/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,65 +5,50 @@ import {
Bytes,
ethereum
} from '@graphprotocol/graph-ts';
import {
AddresslistVotingProposal,
AdminProposal,
MultisigProposal,
TokenVotingProposal,
TransactionActionsProposal
} from '../../generated/schema';
import {Admin, TokenVoting} from '../../generated/templates';

import {ADDRESS_ZERO} from '../utils/constants';

class WithdrawParams {
token: Address = Address.fromString(ADDRESS_ZERO);
to: Address = Address.fromString(ADDRESS_ZERO);
amount: BigInt = BigInt.zero();
// eslint-disable-next-line @typescript-eslint/no-inferrable-types
reference: string = '';
}

/**
*
* @param data is ethereum function call data without the function signiture for dao's Withdraw function
* @returns WithdrawParams
*/
export function decodeWithdrawParams(data: ByteArray): WithdrawParams {
let tokenSubArray = data.subarray(12, 32);
let toSubArray = data.subarray(44, 64);
let amountSubArray = data.subarray(64, 96);
// skip next 32 Bytes as it is just an indicator that the next batch is string
let referenceLengthSubArray = data.subarray(128, 160);
let referenceSubArray = data.subarray(160);

let tokenAddress = Address.fromString(
Address.fromUint8Array(tokenSubArray).toHexString()
);
// AssemblyScript struggles having mutliple return types. Due to this,
// The below seems most effective way.
export function updateProposalWithFailureMap(
proposalId: string,
failureMap: BigInt
): boolean {
let tokenVotingProposal = TokenVotingProposal.load(proposalId);
if (tokenVotingProposal) {
tokenVotingProposal.failureMap = failureMap;
tokenVotingProposal.save();
return true;
}

let toAddress = Address.fromString(
Address.fromUint8Array(toSubArray).toHexString()
);
let multisigProposal = MultisigProposal.load(proposalId);
if (multisigProposal) {
multisigProposal.failureMap = failureMap;
multisigProposal.save();
return true;
}

let amountDecoded = ethereum.decode(
'uint256',
changetype<Bytes>(amountSubArray)
);
let amountBigInt = BigInt.zero();
if (amountDecoded) {
amountBigInt = amountDecoded.toBigInt();
let addresslistProposal = AddresslistVotingProposal.load(proposalId);
if (addresslistProposal) {
addresslistProposal.failureMap = failureMap;
addresslistProposal.save();
return true;
}

let referenceLengthDecoded = ethereum.decode(
'uint256',
changetype<Bytes>(referenceLengthSubArray)
);
let referenceLength: i32 = 0;
if (referenceLengthDecoded) {
referenceLength = referenceLengthDecoded.toI32();
let adminProposal = AdminProposal.load(proposalId);
if (adminProposal) {
adminProposal.failureMap = failureMap;
adminProposal.save();
return true;
}

// @dev perhaps a length limmit is need such as no more than 288 char
let refrenceStringArray = referenceSubArray.subarray(0, referenceLength);
let referenceBytes = Bytes.fromByteArray(
changetype<ByteArray>(refrenceStringArray)
);
let withdrawParams = new WithdrawParams();
withdrawParams.token = tokenAddress;
withdrawParams.to = toAddress;
withdrawParams.amount = amountBigInt;
withdrawParams.reference = referenceBytes.toString();
return withdrawParams;
return false;
}
Loading

0 comments on commit 7c363eb

Please sign in to comment.