-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
Merge pull request #260 from JoinColony/feat/releasing-staged-payments
Feat: Releasing staged payments
Showing
26 changed files
with
4,235 additions
and
5,247 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,80 +1,85 @@ | ||
import { mutate, query } from '~amplifyClient'; | ||
import { utils } from 'ethers'; | ||
import { ExtensionEventListener } from '~eventListeners'; | ||
import { ColonyActionType } from '~graphql'; | ||
import { getInterfaceByListener } from '~interfaces'; | ||
import provider from '~provider'; | ||
import { ContractEventsSignatures, EventHandler } from '~types'; | ||
import { | ||
GetExpenditureMetadataDocument, | ||
GetExpenditureMetadataQuery, | ||
GetExpenditureMetadataQueryVariables, | ||
UpdateExpenditureMetadataDocument, | ||
UpdateExpenditureMetadataMutation, | ||
UpdateExpenditureMetadataMutationVariables, | ||
} from '~graphql'; | ||
import { EventHandler } from '~types'; | ||
import { | ||
checkActionExists, | ||
getCachedColonyClient, | ||
getExpenditureDatabaseId, | ||
insertAtIndex, | ||
output, | ||
mapLogToContractEvent, | ||
toNumber, | ||
verbose, | ||
writeActionFromEvent, | ||
} from '~utils'; | ||
|
||
export const handleStagedPaymentReleased: EventHandler = async ( | ||
event, | ||
listener, | ||
) => { | ||
const { expenditureId, slot } = event.args; | ||
/** | ||
* @NOTE: The UI uses multicall to potentially release multiple slots in one transaction | ||
* Since we only want to create a single action, we will get all slot release events | ||
* the first time we see this event and skip the subsequent ones | ||
* | ||
* Something to refactor once https://github.com/JoinColony/colonyCDapp/issues/2317 is implemented | ||
*/ | ||
const { transactionHash, blockNumber } = event; | ||
const actionExists = await checkActionExists(transactionHash); | ||
if (actionExists) { | ||
return; | ||
} | ||
|
||
const { expenditureId, agent: initiatorAddress } = event.args; | ||
const convertedExpenditureId = toNumber(expenditureId); | ||
const convertedSlot = toNumber(slot); | ||
|
||
const { colonyAddress } = listener as ExtensionEventListener; | ||
const colonyClient = await getCachedColonyClient(colonyAddress); | ||
if (!colonyClient) { | ||
return; | ||
} | ||
|
||
const databaseId = getExpenditureDatabaseId( | ||
colonyAddress, | ||
convertedExpenditureId, | ||
); | ||
const releasedSlotIds = []; | ||
|
||
const response = await query< | ||
GetExpenditureMetadataQuery, | ||
GetExpenditureMetadataQueryVariables | ||
>(GetExpenditureMetadataDocument, { | ||
id: databaseId, | ||
const logs = await provider.getLogs({ | ||
fromBlock: blockNumber, | ||
toBlock: blockNumber, | ||
topics: [utils.id(ContractEventsSignatures.StagedPaymentReleased)], | ||
}); | ||
const metadata = response?.data?.getExpenditureMetadata; | ||
|
||
if (!metadata?.stages) { | ||
output( | ||
`Could not find stages data for expenditure with ID: ${databaseId}. This is a bug and needs investigating.`, | ||
); | ||
const iface = getInterfaceByListener(listener); | ||
if (!iface) { | ||
return; | ||
} | ||
|
||
const existingStageIndex = metadata.stages.findIndex( | ||
(stage) => stage.slotId === convertedSlot, | ||
); | ||
const existingStage = metadata.stages[existingStageIndex]; | ||
for (const log of logs) { | ||
const mappedEvent = await mapLogToContractEvent(log, iface); | ||
|
||
// If the stage doesn't exist or it's been already set to released, we don't need to do anything | ||
if (!existingStage || existingStage.isReleased) { | ||
return; | ||
if (!mappedEvent) { | ||
continue; | ||
} | ||
|
||
// Check the expenditure ID matches the one in the first event | ||
const eventExpenditureId = toNumber(mappedEvent.args.expenditureId); | ||
|
||
if (eventExpenditureId === convertedExpenditureId) { | ||
releasedSlotIds.push(toNumber(mappedEvent.args.slot)); | ||
} | ||
} | ||
|
||
const updatedStage = { | ||
...existingStage, | ||
isReleased: true, | ||
}; | ||
const updatedStages = insertAtIndex( | ||
metadata.stages, | ||
existingStageIndex, | ||
updatedStage, | ||
const databaseId = getExpenditureDatabaseId( | ||
colonyAddress, | ||
convertedExpenditureId, | ||
); | ||
|
||
verbose(`Stage released in expenditure with ID: ${databaseId}`); | ||
if (!releasedSlotIds.length) { | ||
return; | ||
} | ||
|
||
await mutate< | ||
UpdateExpenditureMetadataMutation, | ||
UpdateExpenditureMetadataMutationVariables | ||
>(UpdateExpenditureMetadataDocument, { | ||
input: { | ||
id: databaseId, | ||
stages: updatedStages, | ||
}, | ||
await writeActionFromEvent(event, colonyAddress, { | ||
type: ColonyActionType.ReleaseStagedPayments, | ||
initiatorAddress, | ||
expenditureId: databaseId, | ||
expenditureSlotIds: releasedSlotIds, | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 47 additions & 0 deletions
47
...motions/motionCreated/handlers/multicall/multicallHandlers/releaseStagedPaymentsMotion.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { ColonyActionType } from '~graphql'; | ||
import { createMotionInDB } from '~handlers/motions/motionCreated/helpers'; | ||
import { ContractMethodSignatures } from '~types'; | ||
import { | ||
getDomainDatabaseId, | ||
getExpenditureDatabaseId, | ||
toNumber, | ||
} from '~utils'; | ||
import { MulticallHandler, MulticallValidator } from './types'; | ||
|
||
export const isReleaseStagedPaymentsMotion: MulticallValidator = ({ | ||
decodedFunctions, | ||
}) => { | ||
return decodedFunctions.every( | ||
(decodedFunction) => | ||
decodedFunction.signature === | ||
ContractMethodSignatures.ReleaseStagedPaymentViaArbitration, | ||
); | ||
}; | ||
|
||
export const releaseStagedPaymentsMotionHandler: MulticallHandler = async ({ | ||
colonyAddress, | ||
event, | ||
decodedFunctions, | ||
gasEstimate, | ||
}) => { | ||
const [, , domainId] = event.args; | ||
|
||
// @NOTE: This handler assumes the multicall is releasing stages of a single expenditure | ||
const expenditureId = decodedFunctions[0]?.args[4]; | ||
const slotIds = decodedFunctions.map((decodedFunction) => | ||
toNumber(decodedFunction.args[5]), | ||
); | ||
|
||
await createMotionInDB(colonyAddress, event, { | ||
type: ColonyActionType.ReleaseStagedPaymentsMotion, | ||
fromDomainId: colonyAddress | ||
? getDomainDatabaseId(colonyAddress, domainId) | ||
: undefined, | ||
gasEstimate: gasEstimate.toString(), | ||
expenditureId: getExpenditureDatabaseId( | ||
colonyAddress, | ||
toNumber(expenditureId), | ||
), | ||
expenditureSlotIds: slotIds, | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { query } from '~amplifyClient'; | ||
import { | ||
GetActionByIdDocument, | ||
GetActionByIdQuery, | ||
GetActionByIdQueryVariables, | ||
} from '~graphql'; | ||
|
||
export const checkActionExists = async ( | ||
transactionHash: string, | ||
): Promise<boolean> => { | ||
const existingActionQuery = await query< | ||
GetActionByIdQuery, | ||
GetActionByIdQueryVariables | ||
>(GetActionByIdDocument, { | ||
id: transactionHash, | ||
}); | ||
|
||
return !!existingActionQuery?.data?.getColonyAction; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { utils } from 'ethers'; | ||
import { TransactionDescription } from 'ethers/lib/utils'; | ||
|
||
/** | ||
* Helper attempting to decode function data by trying to parse it with different contract ABIs | ||
* until it finds one that does not throw an error | ||
* @TODO: This should be refactored to use ABIs from @colony/abis | ||
*/ | ||
export const parseFunctionData = ( | ||
functionData: string, | ||
interfaces: utils.Interface[], | ||
): TransactionDescription | null => { | ||
for (const iface of interfaces) { | ||
try { | ||
const parsed = iface.parseTransaction({ data: functionData }); | ||
return parsed; | ||
} catch (e) { | ||
// Ignore | ||
} | ||
} | ||
return null; | ||
}; |