Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add og subgraph queries #4044

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/composables/useProposalVotes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export function useProposalVotes(proposal: Proposal, loadBy = 6) {
}

function formatProposalVotes(votes: Vote[]) {
if (!votes.length) return [];
if (!votes?.length) return [];
return votes.map(vote => {
vote.balance = vote.vp;
vote.scores = vote.vp_by_strategy;
Expand Down
7 changes: 4 additions & 3 deletions src/plugins/safeSnap/components/HandleOutcomeUma.vue
Original file line number Diff line number Diff line change
Expand Up @@ -235,14 +235,15 @@ const questionState = computed<QuestionState>(() => {
if (!activeProposal && voteResultsConfirmed) return 'waiting-for-proposal';

// Proposal has been made and is waiting for liveness period to complete.
if (!assertionEvent.isExpired) return 'waiting-for-liveness';
if (!assertionEvent?.isExpired) return 'waiting-for-liveness';

// Proposal is approved if it expires without a dispute and hasn't been settled.
if (assertionEvent.isExpired && !assertionEvent.isSettled)
if (assertionEvent?.isExpired && !assertionEvent?.isSettled)
return 'proposal-approved';

// Proposal is approved if it has been settled without a disputer and hasn't been executed.
if (assertionEvent.isSettled && !proposalExecuted) return 'proposal-approved';
if (assertionEvent?.isSettled && !proposalExecuted)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ensuring this exists

return 'proposal-approved';

return 'error';
});
Expand Down
88 changes: 70 additions & 18 deletions src/plugins/safeSnap/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,8 +395,10 @@ export type ContractData = {
deployBlockNumber?: number;
subgraph?: string;
};
// contract addresses pulled from https://github.com/UMAprotocol/protocol/tree/master/packages/core/networks
export const contractData: ContractData[] = [
{
// mainnet
network: '1',
name: 'OptimisticOracleV3',
address: '0xfb55F43fB9F48F63f9269DB7Dde3BbBe1ebDC0dE',
Expand All @@ -405,14 +407,7 @@ export const contractData: ContractData[] = [
deployBlock: 16636058
},
{
network: '1',
name: 'OptimisticGovernor',
address: '0x28CeBFE94a03DbCA9d17143e9d2Bd1155DC26D5d',
subgraph:
'https://api.thegraph.com/subgraphs/name/md0x/mainnet-optimistic-governor',
deployBlock: 16890621
},
{
// goerli
network: '5',
name: 'OptimisticOracleV3',
address: '0x9923D42eF695B5dd9911D05Ac944d4cAca3c4EAB',
Expand All @@ -421,45 +416,102 @@ export const contractData: ContractData[] = [
deployBlock: 8497481
},
{
network: '5',
name: 'OptimisticGovernor',
address: '0x07a7Be7AA4AaD42696A17e974486cb64A4daC47b',
deployBlock: 8700589
// optimism
network: '10',
name: 'OptimisticOracleV3',
address: '0x072819Bb43B50E7A251c64411e7aA362ce82803B',
subgraph:
'https://api.thegraph.com/subgraphs/name/umaprotocol/optimism-optimistic-oracle-v3'
},
{
// gnosis
network: '100',
name: 'OptimisticOracleV3',
address: '0x22A9AaAC9c3184f68C7B7C95b1300C4B1D2fB95C',
subgraph:
'https://api.thegraph.com/subgraphs/name/umaprotocol/gnosis-optimistic-oracle-v3'
},
{
// polygon
network: '137',
name: 'OptimisticOracleV3',
address: '0x5953f2538F613E05bAED8A5AeFa8e6622467AD3D',
subgraph:
'https://api.thegraph.com/subgraphs/name/umaprotocol/polygon-optimistic-oracle-v3'
},
{
//arbitrum
network: '42161',
name: 'OptimisticOracleV3',
address: '0xa6147867264374F324524E30C02C331cF28aa879',
subgraph:
'https://api.thegraph.com/subgraphs/name/umaprotocol/arbitrum-optimistic-oracle-v3'
},
{
network: '10',
// avalanche
network: '43114',
name: 'OptimisticOracleV3',
address: '0xa4199d73ae206d49c966cF16c58436851f87d47F',
subgraph:
'https://api.thegraph.com/subgraphs/name/umaprotocol/optimism-optimistic-oracle-v3'
'https://api.thegraph.com/subgraphs/name/umaprotocol/avalanche-optimistic-oracle-v3'
},
{
// mainnet
network: '1',
name: 'OptimisticGovernor',
address: '0x28CeBFE94a03DbCA9d17143e9d2Bd1155DC26D5d',
subgraph:
'https://api.thegraph.com/subgraphs/name/umaprotocol/mainnet-optimistic-governor',
deployBlock: 16890621
},
// Keep in mind, OG addresses are not the module addresses for each individual space, these addresses typically
// are not used, but are here for reference.
{
//goerli
network: '5',
name: 'OptimisticGovernor',
address: '0x07a7Be7AA4AaD42696A17e974486cb64A4daC47b',
deployBlock: 8700589,
subgraph:
'https://api.thegraph.com/subgraphs/name/md0x/goerli-optimistic-governor'
},
{
// optimism
network: '10',
name: 'OptimisticGovernor',
address: '0x357fe84E438B3150d2F68AB9167bdb8f881f3b9A',
subgraph:
'https://api.thegraph.com/subgraphs/name/umaprotocol/optimism-optimistic-governor'
},
{
// gnosis
network: '100',
name: 'OptimisticGovernor',
subgraph:
'https://api.thegraph.com/subgraphs/name/umaprotocol/gnosis-optimistic-governor'
},
{
// polygon
network: '137',
name: 'OptimisticGovernor',
address: '0x3Cc4b597E9c3f51288c6Cd0c087DC14c3FfdD966',
subgraph:
'https://api.thegraph.com/subgraphs/name/md0x/polygon-optimistic-governor'
'https://api.thegraph.com/subgraphs/name/umaprotocol/polygon-optimistic-governor'
},
{
// arbitrum
network: '42161',
name: 'OptimisticGovernor',
address: '0x30679ca4ea452d3df8a6c255a806e08810321763',
subgraph:
'https://api.thegraph.com/subgraphs/name/md0x/arbitrum-optimistic-governor'
'https://api.thegraph.com/subgraphs/name/umaprotocol/arbitrum-optimistic-governor'
},
{
network: '10',
// avalanche
network: '43114',
name: 'OptimisticGovernor',
address: '0xEF8b46765ae805537053C59f826C3aD61924Db45',
subgraph:
'https://api.thegraph.com/subgraphs/name/md0x/optimism-optimistic-governor'
'https://api.thegraph.com/subgraphs/name/umaprotocol/avalanche-optimistic-governor'
}
];
137 changes: 53 additions & 84 deletions src/plugins/safeSnap/utils/umaModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,31 @@ function getDeployBlock(network: string, name: string): number {
if (data.length === 1) return data[0].deployBlock;
return 0;
}
function getOracleV3Subgraph(network: string): string {
const results = filter(contractData, { network, name: 'OptimisticOracleV3' });
function getContractSubgraph(search: {
network: string;
name: string;
}): string {
const results = filter(contractData, search);
if (results.length > 1)
throw new Error(
`Too many results finding oracle v3 subgraph on network ${network}`
`Too many results finding ${search.name} subgraph on network ${search.network}`
);
if (results.length < 1)
throw new Error(
`No results finding oracle v3 subgraph on network ${network}`
`No results finding ${search.name} subgraph on network ${search.network}`
);
if (!results[0].subgraph)
throw new Error(
`No subgraph url defined for oracle v3 on network ${network}`
`No subgraph url defined for ${search.name} on network ${search.network}`
);
return results[0].subgraph;
}
function getOptimisticGovernorSubgraph(network: string): string {
return getContractSubgraph({ network, name: 'OptimisticGovernor' });
}
function getOracleV3Subgraph(network: string): string {
return getContractSubgraph({ network, name: 'OptimisticOracleV3' });
}

export const queryGql = async (url: string, query: string) => {
try {
Expand Down Expand Up @@ -86,6 +95,20 @@ const findAssertionsGql = async (
const result = await queryGql(oracleUrl, request);
return result?.assertions ?? [];
};
// Search optimistic governor for individual proposal
const findProposalGql = async (network: string, params: { assertionId }) => {
const subgraph = getOptimisticGovernorSubgraph(network);
const request = `
{
proposal(id:"${params.assertionId}"){
id
executed
}
}
`;
const result = await queryGql(subgraph, request);
return result?.proposal;
};

const getBondDetailsUma = async (
provider: StaticJsonRpcProvider,
Expand Down Expand Up @@ -274,26 +297,22 @@ export const getModuleDetailsUma = async (
// Get the full proposal events (with state).
const fullAssertionEvent = await Promise.all(
thisModuleAssertionEvent.map(async event => {
const assertion = oracleContract.getAssertion(
const assertion = await oracleContract.getAssertion(
event.args?.assertionId
) as Promise<{
expirationTime: BigNumber;
settled: boolean;
}>;

return assertion.then(result => {
const isExpired =
Math.floor(Date.now() / 1000) >= Number(result.expirationTime);

return {
expirationTimestamp: result.expirationTime,
isExpired: isExpired,
isSettled: result.settled,
proposalHash: proposalHash,
proposalTxHash: event.transactionHash,
logIndex: event.logIndex
};
});
);

const isExpired =
Math.floor(Date.now() / 1000) >= assertion.expirationTime.toNumber();

return {
assertionId: event?.args?.assertionId,
expirationTimestamp: assertion.expirationTime,
isExpired: isExpired,
isSettled: assertion.settled,
proposalHash: proposalHash,
proposalTxHash: event.transactionHash,
logIndex: event.logIndex
};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was just re-formatted to use async await

})
);

Expand Down Expand Up @@ -422,57 +441,15 @@ export const getModuleDetailsUmaGql = async (
assertionId !==
'0x0000000000000000000000000000000000000000000000000000000000000000';

// Search for requests with matching ancillary data
const oracleContract = new Contract(
optimisticOracle,
UMA_ORACLE_ABI,
provider
);

const latestBlock = await provider.getBlock('latest');
// modify this per chain. this should be updated with constants for all chains. start block is og deploy block.
// this needs to be optimized to reduce loading time, currently takes a long time to parse 3k blocks at a time.
const oGstartBlock = network === '1' ? 17167414 : 0;
const maxRange = network === '1' ? 3000 : 10000;

// TODO: use og subgraph to replace need for these calls
const [transactionsProposedEvents, executionEvents] = await Promise.all([
// Check if this specific proposal has already been executed.
// note usage of pageEvents, which query only based on a limit number of blocks within a broader range
// this prevents block range too large errors.
pageEvents(
oGstartBlock,
latestBlock.number,
maxRange,
({ start, end }: { start: number; end: number }) => {
return moduleContract.queryFilter(
moduleContract.filters.TransactionsProposed(),
start,
end
);
}
),
pageEvents(
oGstartBlock,
latestBlock.number,
maxRange,
({ start, end }: { start: number; end: number }) => {
return moduleContract.queryFilter(
moduleContract.filters.ProposalExecuted(proposalHash),
start,
end
);
}
)
]);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can remove all events now from the graph function call now that we have OG subgraphs

const [assertion0] = await findAssertionsGql(network, {
claim: ancillaryData,
callbackRecipient: moduleAddress
});

const assertionEvent = assertion0
? {
assertionId: assertion0.assertionId,
expirationTimestamp: assertion0.expirationTime,
expirationTimestamp: BigNumber.from(assertion0.expirationTime),
isExpired:
Math.floor(Date.now() / 1000) >= Number(assertion0.expirationTime),
isSettled: assertion0.settlementHash ? true : false,
Expand All @@ -482,20 +459,12 @@ export const getModuleDetailsUmaGql = async (
}
: undefined;

const thisProposalTransactionsProposedEvents =
transactionsProposedEvents.filter(
event => toUtf8String(event.args?.explanation) === explanation
);

const assertion = thisProposalTransactionsProposedEvents.map(
tx => tx.args?.assertionId
);

const assertionIds = executionEvents.map(tx => tx.args?.assertionId);

const proposalExecuted = assertion.some(assertionId =>
assertionIds.includes(assertionEvent.assertionId)
);
const proposal = assertionEvent?.assertionId
? await findProposalGql(network, {
assertionId: assertionEvent.assertionId
})
: undefined;
const proposalExecuted = proposal?.executed ? true : false;

return {
dao: moduleDetails[0][0],
Expand All @@ -510,9 +479,9 @@ export const getModuleDetailsUmaGql = async (
userBalance: bondDetails.currentUserBalance,
needsBondApproval: needsApproval,
noTransactions: false,
activeProposal: activeProposal,
activeProposal,
assertionEvent,
proposalExecuted: proposalExecuted,
proposalExecuted,
livenessPeriod: livenessPeriod.toString()
};
};