Skip to content

Commit

Permalink
fix issue with transfer events wrongly updating delegate values
Browse files Browse the repository at this point in the history
  • Loading branch information
pedropregueiro committed Dec 2, 2023
1 parent f32274b commit df26975
Show file tree
Hide file tree
Showing 3 changed files with 292 additions and 3 deletions.
6 changes: 3 additions & 3 deletions packages/nouns-subgraph/src/nouns-erc-721.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,6 @@ export function handleTransfer(event: Transfer): void {
if (fromHolder.tokenBalanceRaw == BIGINT_ZERO && fromHolderPreviousBalance > BIGINT_ZERO) {
governance.currentTokenHolders = governance.currentTokenHolders.minus(BIGINT_ONE);
governance.save();

fromHolder.delegate = null;
} else if (
fromHolder.tokenBalanceRaw > BIGINT_ZERO &&
fromHolderPreviousBalance == BIGINT_ZERO
Expand Down Expand Up @@ -198,7 +196,9 @@ export function handleTransfer(event: Transfer): void {
governance.currentTokenHolders = governance.currentTokenHolders.plus(BIGINT_ONE);
governance.save();

toHolder.delegate = toHolder.id;
if (!toHolder.delegate) {
toHolder.delegate = toHolder.id;
}
}

let noun = Noun.load(transferredNounId);
Expand Down
198 changes: 198 additions & 0 deletions packages/nouns-subgraph/tests/nouns-erc-721.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import {
assert,
clearStore,
test,
describe,
beforeAll,
afterAll,
afterEach,
beforeEach,
} from 'matchstick-as/assembly/index';
import { Address, BigInt, Bytes } from '@graphprotocol/graph-ts';
import {
createTransferEvent,
createDelegateChangedEvent,
createDelegateVotesChangedEvent,
} from './utils';
import { BIGINT_ONE, BIGINT_ZERO } from '../src/utils/constants';
import { getOrCreateDelegate, getOrCreateAccount } from '../src/utils/helpers';
import {
handleDelegateChanged,
handleDelegateVotesChanged,
handleTransfer,
} from '../src/nouns-erc-721';

const someAddress = Address.fromString('0x0000000000000000000000000000000000000001');
const popularDelegate = Address.fromString('0x0000000000000000000000000000000000000002');
const BLACKHOLE_ADDRESS = Address.fromString('0x0000000000000000000000000000000000000000');

const txHash = Bytes.fromI32(11);
const logIndex = BigInt.fromI32(3);
const updateBlockTimestamp = BigInt.fromI32(946684800);
const updateBlockNumber = BigInt.fromI32(15537394);

afterEach(() => {
clearStore();
});

describe('nouns-erc-721', () => {
describe('Delegate changes', () => {
beforeEach(() => {
const delegate = getOrCreateDelegate(someAddress.toHexString());
delegate.tokenHoldersRepresentedAmount = 1;
delegate.delegatedVotes = BIGINT_ONE;
delegate.delegatedVotesRaw = BIGINT_ONE;
delegate.save();

const account = getOrCreateAccount(someAddress.toHexString());
account.tokenBalance = BIGINT_ONE;
account.tokenBalanceRaw = BIGINT_ONE;
account.nouns = ['1'];
account.save();
});

afterAll(() => {
clearStore();
});

test('after having delegated votes, transferring all nouns avoids nulling account delegate', () => {
const delegateChangeEvent = createDelegateChangedEvent(
txHash,
logIndex,
updateBlockTimestamp,
updateBlockNumber,
someAddress,
someAddress,
popularDelegate,
);

handleDelegateChanged(delegateChangeEvent);

assert.fieldEquals(
'Account',
someAddress.toHexString(),
'delegate',
popularDelegate.toHexString(),
);
assert.fieldEquals('Delegate', popularDelegate.toHexString(), 'nounsRepresented', '[1]');

const randomDelegateIncreateVoteEvent = createDelegateVotesChangedEvent(
txHash,
logIndex,
updateBlockTimestamp,
updateBlockNumber,
popularDelegate,
BIGINT_ZERO,
BIGINT_ONE,
);

const accountDelegateDecreaseVoteEvent = createDelegateVotesChangedEvent(
txHash,
logIndex,
updateBlockTimestamp,
updateBlockNumber,
someAddress,
BIGINT_ONE,
BIGINT_ZERO,
);

handleDelegateVotesChanged(randomDelegateIncreateVoteEvent);
handleDelegateVotesChanged(accountDelegateDecreaseVoteEvent);

assert.fieldEquals('Delegate', popularDelegate.toHexString(), 'delegatedVotes', '1');
assert.fieldEquals('Delegate', someAddress.toHexString(), 'delegatedVotes', '0');

const transferEvent = createTransferEvent(
txHash,
logIndex,
updateBlockTimestamp,
updateBlockNumber,
someAddress,
BLACKHOLE_ADDRESS,
BIGINT_ONE,
);

const randomDelegateDecreateVoteEvent = createDelegateVotesChangedEvent(
txHash,
logIndex,
updateBlockTimestamp,
updateBlockNumber,
popularDelegate,
BIGINT_ONE,
BIGINT_ZERO,
);

handleTransfer(transferEvent);
handleDelegateVotesChanged(randomDelegateDecreateVoteEvent);

assert.fieldEquals('Account', someAddress.toHexString(), 'tokenBalance', '0');
assert.fieldEquals(
'Account',
someAddress.toHexString(),
'delegate',
popularDelegate.toHexString(),
);
assert.fieldEquals('Delegate', popularDelegate.toHexString(), 'nounsRepresented', '[]');
assert.fieldEquals('Delegate', popularDelegate.toHexString(), 'delegatedVotes', '0');
});

test('transfer events ignore delegate changes', () => {
const delegateChangeEvent = createDelegateChangedEvent(
txHash,
logIndex,
updateBlockTimestamp,
updateBlockNumber,
someAddress,
someAddress,
popularDelegate,
);

handleDelegateChanged(delegateChangeEvent);

assert.fieldEquals(
'Account',
someAddress.toHexString(),
'delegate',
popularDelegate.toHexString(),
);

const selloutEvent = createTransferEvent(
txHash,
logIndex,
updateBlockTimestamp,
updateBlockNumber,
someAddress,
BLACKHOLE_ADDRESS,
BIGINT_ONE,
);

handleTransfer(selloutEvent);

assert.fieldEquals(
'Account',
someAddress.toHexString(),
'delegate',
popularDelegate.toHexString(),
);

const buybackEvent = createTransferEvent(
txHash,
logIndex,
updateBlockTimestamp,
updateBlockNumber,
BLACKHOLE_ADDRESS,
someAddress,
BIGINT_ONE,
);

handleTransfer(buybackEvent);

assert.fieldEquals(
'Account',
someAddress.toHexString(),
'delegate',
popularDelegate.toHexString(),
);
});
});
});
91 changes: 91 additions & 0 deletions packages/nouns-subgraph/tests/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ import {
import { Address, ethereum, Bytes, BigInt, ByteArray } from '@graphprotocol/graph-ts';
import { BIGINT_ONE, BIGINT_ZERO } from '../src/utils/constants';
import { ProposalCandidateCreated, SignatureAdded } from '../src/types/NounsDAOData/NounsDAOData';
import {
DelegateChanged,
DelegateVotesChanged,
Transfer,
} from '../src/types/NounsToken/NounsToken';

export function createProposalCreatedWithRequirementsEventV3(
input: ProposalCreatedWithRequirementsEvent,
Expand Down Expand Up @@ -661,3 +666,89 @@ export function createProposalQueuedEvent(

return newEvent;
}

export function createTransferEvent(
txHash: Bytes,
logIndex: BigInt,
blockTimestamp: BigInt,
blockNumber: BigInt,
from: Address,
to: Address,
tokenId: BigInt,
): Transfer {
let newEvent = changetype<Transfer>(newMockEvent());

newEvent.transaction.hash = txHash;
newEvent.logIndex = logIndex;
newEvent.block.timestamp = blockTimestamp;
newEvent.block.number = blockNumber;

newEvent.parameters = new Array();
newEvent.parameters.push(new ethereum.EventParam('from', ethereum.Value.fromAddress(from)));
newEvent.parameters.push(new ethereum.EventParam('to', ethereum.Value.fromAddress(to)));
newEvent.parameters.push(
new ethereum.EventParam('tokenId', ethereum.Value.fromUnsignedBigInt(tokenId)),
);

return newEvent;
}

export function createDelegateChangedEvent(
txHash: Bytes,
logIndex: BigInt,
blockTimestamp: BigInt,
blockNumber: BigInt,
delegator: Address,
previousDelegate: Address,
newDelegate: Address,
): DelegateChanged {
let newEvent = changetype<DelegateChanged>(newMockEvent());

newEvent.transaction.hash = txHash;
newEvent.logIndex = logIndex;
newEvent.block.timestamp = blockTimestamp;
newEvent.block.number = blockNumber;

newEvent.parameters = new Array();
newEvent.parameters.push(
new ethereum.EventParam('delegator', ethereum.Value.fromAddress(delegator)),
);
newEvent.parameters.push(
new ethereum.EventParam('previousDelegate', ethereum.Value.fromAddress(previousDelegate)),
);
newEvent.parameters.push(
new ethereum.EventParam('newDelegate', ethereum.Value.fromAddress(newDelegate)),
);

return newEvent;
}

export function createDelegateVotesChangedEvent(
txHash: Bytes,
logIndex: BigInt,
blockTimestamp: BigInt,
blockNumber: BigInt,
delegate: Address,
previousBalance: BigInt,
newBalance: BigInt,
): DelegateVotesChanged {
let newEvent = changetype<DelegateVotesChanged>(newMockEvent());

newEvent.transaction.hash = txHash;
newEvent.logIndex = logIndex;
newEvent.block.timestamp = blockTimestamp;
newEvent.block.number = blockNumber;

newEvent.parameters = new Array();
newEvent.parameters.push(
new ethereum.EventParam('delegate', ethereum.Value.fromAddress(delegate)),
);
newEvent.parameters.push(
new ethereum.EventParam('previousBalance', ethereum.Value.fromUnsignedBigInt(previousBalance)),
);
newEvent.parameters.push(
new ethereum.EventParam('newBalance', ethereum.Value.fromUnsignedBigInt(newBalance)),
);

return newEvent;
}

0 comments on commit df26975

Please sign in to comment.