-
Notifications
You must be signed in to change notification settings - Fork 1
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
Add mint tokens motion support #16
Conversation
2b6d12f
to
8e6a07a
Compare
8e6a07a
to
5940d67
Compare
src/eventQueue.ts
Outdated
this.queue.push(event); | ||
verbose( | ||
'Event added to the queue:', | ||
event?.signature || ContractEventsSignatures.UnknownEvent, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes in this file are just from prettier & correcting the "Uknown" event typo
src/utils/eventListeners.ts
Outdated
* It basically does away with all the boilerplate of setting up topics, setting | ||
* the event listener, parsing the received event | ||
*/ | ||
export const eventListenerGenerator = async ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have moved all the listeners to their own file to separate them from the helpers they use, especially now I've written a few for eventListenerGenerator
src/utils/eventListeners.ts
Outdated
* Voting Reputation Client specific event listener, | ||
* which uses `eventListenerGenerator` under the hood | ||
*/ | ||
export const addMotionEventListener = async ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the only addition, aside from refactoring event listener generator
src/utils/events.ts
Outdated
*/ | ||
export const eventListenerGenerator = async ( | ||
eventSignature: ContractEventsSignatures, | ||
export const getClientAndProviderForELG = async ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Written a few helper functions for the event listener generator, since the exact logic seems to change depending on the client type. E.g. which client / provider to use, which filter to use, which contract address to use in the event, when to add the event to the queue...
I expect splitting up the various parts of adding an event listener will make it easier to extend as we continue porting over functionality.
src/utils/events.ts
Outdated
* In the case of Voting Rep, only add the event to the queue if the colony address | ||
* associated with the event is the same as the colony address the listener is associated with. | ||
*/ | ||
const { colonyId } = await query('getColonyExtension', { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using this to get the colony the voting reputation extension is installed in. This is a guard against the motion being written to the db with the wrong colony (which is possible when the behaviour I'm seeing in development occurs, where the event fires on all colonies)
import { verbose } from './logger'; | ||
import { getColonyTokenAddress, getColonyTokenData } from './tokens'; | ||
|
||
export const getParsedActionFromMotion = async ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is how to get the underlying action associated with the motion, e.g mint tokens
const amount = actionArgs[0].toString(); | ||
const tokenAddress = await getColonyTokenAddress(colonyAddress); | ||
const tokenData = await getColonyTokenData(tokenAddress, colonyClient); | ||
await mutate('createColonyAction', { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This adds all the data the ui needs for the mint token motion
5890066
to
32f56c9
Compare
src/eventProcessor.ts
Outdated
'tokens were minted in colony:', | ||
colonyAddress, | ||
); | ||
if (amount && amount.toString() !== '0') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added this because the TokensMinted event is picked up after the MotionCreated event, twice actually. The amounts are undefined
and 0
, so guarding against that here.
Would be interested to know why this happens @rdig?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the motion set to very short timeout ? Ie: it finishes instantly ? Otherwise I can't really explain this.
The underlying action / event doesn't get triggered until the motion finishes (and that is done manually).
If that isn't the case I don't really understand what's going on. Maybe ask @area for his input ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got it to work in conjunction with CDapp#263, but I'm only commenting for now - I had a chat with Raul today and I'll need to refactor the way we handle events that comprise an action and that will probably require some changes to this PR subsequently
src/domains.ts
Outdated
export const formatDomain = (colonyAddress: string, domainId: string) => | ||
`${colonyAddress}_${domainId.toString()}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That could live in the utils
folder
src/trackExtensions.ts
Outdated
if (Extension.VotingReputation === extensionId) { | ||
addMotionEventListener(ContractEventsSignatures.MotionCreated, colony); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In eventProcessor, this listener is added on ExtensionInitialised event, and here you add it for all installed (but not necessarily initialised) extensions. Is that intentional? If not, you could move it a few lines below, there's a check for whether the extension is currently initialised
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
☝️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, have moved down
src/types.ts
Outdated
/* | ||
* Contract calls | ||
*/ | ||
export enum ColonyActions { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could the name be more indicative that these are the contract calls?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've gone for ColonyOperations
. Preferable?
src/types.ts
Outdated
export enum ColonyMotionType { | ||
MintTokensMotion = 'MINT_TOKENS_MOTION', | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed in the corresponding CDapp PR you keep those merged, should we also stick to that idea here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some comments, but otherwise seems good.
src/eventProcessor.ts
Outdated
'tokens were minted in colony:', | ||
colonyAddress, | ||
); | ||
if (amount && amount.toString() !== '0') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the motion set to very short timeout ? Ie: it finishes instantly ? Otherwise I can't really explain this.
The underlying action / event doesn't get triggered until the motion finishes (and that is done manually).
If that isn't the case I don't really understand what's going on. Maybe ask @area for his input ?
@@ -61,7 +61,7 @@ const startIngestor = async (): Promise<void> => { | |||
/* | |||
* Setup all listeners we care about | |||
*/ | |||
await blockListener(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not saying it's something wrong with this, I'm just curious on why you removed it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one's not an async function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rdig I think you asked this on another pr as well so just tagging you here in case you missed my response. Not using await since there's nothing to await.
src/trackExtensions.ts
Outdated
if (Extension.VotingReputation === extensionId) { | ||
addMotionEventListener(ContractEventsSignatures.MotionCreated, colony); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
☝️
|
||
try { | ||
return colonyClient.interface.parseTransaction({ | ||
data: motion.action, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I remember correctly the motion's data (action) is available in the event, and if that is the case we don't have to call getMotion
to get it anymore.
Please check if that's the case, I might be wrong though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rdig I don't think so, the event emits motionId
, creator
, and domainId
but not the underlying action
e042c49
to
70c25ed
Compare
c3aa3fb
to
5ea6b1d
Compare
* It basically does away with all the boilerplate of setting up topics, setting | ||
* the event listener, parsing the received event | ||
*/ | ||
export const eventListenerGenerator = async ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved to own file as I have added a few helpers
* Network Client specific event listener, | ||
* which uses `eventListenerGenerator` under the hood | ||
*/ | ||
export const addNetworkEventListener = async ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved to '~utils/eventListeners'
contractAddress, | ||
); | ||
|
||
const filter = getEventFilter(eventSignature, contractAddress, clientType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrote some helpers for this. Should make it easier to extend.
@jakubcolony Have rebased this to account for changes to event processor |
@willm30 I pulled your latest changes and I'm seeing those errors when trying to create mint tokens motion. Looks like you're trying to write some fields (
|
@jakubcolony Yep I will need to update this as I rebased the cdapp branch. |
5ea6b1d
to
43ef471
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@willm30 Nice work overall, but after pulling the latest from port/colony-motions
I can't create a colony to test this, due to the following error from GetFullColonyByName
query:
"Cannot query field \"color\" on type \"Domain\". Did you mean \"colony\" or \"colonyId\"?"
Also, we don't have a linter yet, but do you have an eslint extension installed? I'm seeing a few eslint warnings here and there, e.g. utils/motions.ts
:
src/handlers/ motions/index.ts
Outdated
@@ -0,0 +1 @@ | |||
export { default as handleMotionCreated } from './motionCreated'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import { | ||
getParsedActionFromMotion, | ||
writeMintTokensMotionToDB, | ||
} from '~utils/motions'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could export the motions stuff from ~utils
and import it together with verbose
src/utils/domains.ts
Outdated
export const formatDomain = (colonyAddress: string, domainId: string) => | ||
`${colonyAddress}_${domainId.toString()}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a heads up, there should be a getDomainDatabaseId
util in port/actions
src/utils/index.ts
Outdated
@@ -5,3 +5,4 @@ export * from './extensions'; | |||
export * from './numbers'; | |||
export * from './tokens'; | |||
export * from './actions'; | |||
export * from './eventListeners'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Matter of personal preferece I guess, but I would consider changing utils/events.ts
to events
directory and sticking all your extracted utils in there. events
and eventListeners
seem closely tied
src/utils/tokens.ts
Outdated
export const getColonyTokenData = async ( | ||
tokenAddress: string, | ||
colonyClient: AnyColonyClient, | ||
): Promise<{ name: string; decimals: number; symbol: string }> => { | ||
const tokenClient = await getTokenClient(tokenAddress, colonyClient.provider); | ||
return await tokenClient.getTokenInfo(); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this used anywhere? I can see it imported in utils/motions
but doesn't seem to be called
43ef471
to
201d984
Compare
201d984
to
6dcf4e2
Compare
@jakubcolony I have rebased the cdapp after you rebased actions and have rebased this branch too on latest actions. I'm not experiencing problems creating a colony and all seems to have been integrated correctly. But let me know if after building both branches afresh, you still have issues. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work! 💪
parsedAction: TransactionDescription, | ||
) => { | ||
const { name, args: actionArgs } = parsedAction; | ||
const amount = actionArgs[0].toString(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this element of the array be undefined? If so, consider adding optional chaining
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jakubcolony No, don't expect so. The front end should not be passing through an undefined amount of tokens to mint.
It maps to the motion params field in the current set up, for reference:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is that a safe assumption to make, that it will never be broken by anyone?
What about calling it directly from a contract?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jakubcolony I don't believe calling it directly from a contract will be a problem either. You need to pass an "encoded action" to the createMotion
call, and in order for the encode action
function to succeed, it must receive a complete set of arguments to the underlying action call (by 'complete' I mean correct length, each of which is the correct type).
@rdig Can a malformed event find its way into the block ingestor? I.e. an event without the expected set of arguments?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, not really. If that happens it'll be the event of the year on ethereum :)
While I approve of your proactive thinking, we shouldn't be worrying about this for now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good to know, thanks 👍
Description
This pr adds block-ingestor support for the MotionCreated event, specifically when it wraps the mint tokens action.
I have coupled the MotionCreated event listeners with the Voting Reputation extension, such that as soon as it is initialised, or if
trackExtensions
determines that it is initialised on block-ingestor start up, it will also listen for MotionCreated events.Testing
To test, you will need to:
colony-cdapp-env-orchestration.yaml
:npm run truffle console
Then
Testing should reveal that only one motion is created in the db, and it should be created in the colony you clicked "test mint tokens" in.
You should also terminate the block-ingestor and restart it, and you should see a listener attached to the MotionCreated event as a result of
trackExtensions
determining that the Voting Reputation Extn is installed.Notes
In my dev env, I've been noticing that the event listener attached to the
MotionCreated
event is firing for each colony, as well as often multiple times for the same colony.I have guarded against the motion being associated with the wrong colony in the db, but you may see errors in the block ingestor terminal when creating a motion in the db, because it may attempt to create the same motion twice. This should not be a problem and should not actually create more than one motion per tx hash. You can verify this in the ui as well as at localhost:20002 by ensuring only one motion is created per click.
I have also noticed that sometimes when you click the button, it redirects but the block ingestor doesn't pick up the event and so the action page doesn't load. If this happens, go back to Colony Home and refresh & try again. We suspect this may be the result of hot reloading the block ingestor and therefore only a problem in dev.
Finally, I've noticed that a couple of TokensMinted events get picked up straight after, with an amount of undefined and 0. I've added logic to guard against making a db mutatation when this is the case.
Troubleshooting
If you are experiencing an incorrent nonce error at colony create when running the block ingestor separately, it may be caused by the
await provider.getNetwork()
call on line 45 of index.ts. This is just a theory but commenting it out and thegetChainId()
call on the line below solved it for me the first time I tried it.