-
Notifications
You must be signed in to change notification settings - Fork 0
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: implement oracle rpc calls #39
Conversation
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.
Cool! Some changes needed, leaving a short summary below.
- Double check that the smart contract function signatures that we are using correspond to our
viem
calls (the Linear task might be helpful here, as it has the respective link to each of those function signatures) - Create a custom error for a receipt status not being
=== success
and apply it into everyProtocolProvider
method - Add some more testing cases
address: this.oracleContract.address, | ||
abi: oracleAbi, | ||
functionName: "proposeResponse", | ||
args: [requestId, { epoch, chainId, block: blockNumber }], |
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.
The old function signature might have been a little bit deprecated.
Based on the Oracle function we might be ok using Request["prophetData"]
and Response["prophetData"]
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.
yep, I double checked the interfaces and had to update a couple of their protocolProvider interfaces and implementations
}); | ||
|
||
if (receipt.status !== "success") { | ||
throw new Error("Transaction failed"); |
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.
We'll also need to handle this scenario too when processing the event that triggered this action.
Let's add a named error for this (probably TransactionExecutionError
) as it needs to be handled by the EboProcessor
by retrying the transaction.
Also, given that this error is most likely not going to be a child class of ContractFunctionRevertedError
we'll (probably) end up modifying the method we have/adding a new method to the ErrorFactory
class:
// ErrorFactory.ts
static async createTransactionError(error) {
...
}
static async createContractRevertedError(error) {
...
}
For these last ErrorFactory
changes mentioned, let's wait until we have a solid design for the error handling and we'll come back to it after that.
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.
Ok, for now I added a TransactionExecutionError w/test cases
address: this.oracleContract.address, | ||
abi: oracleAbi, | ||
functionName: "disputeResponse", | ||
args: [requestId, responseId, proposer], |
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.
Same here, based on the contract function signatures we should use Request["prophetData"], Response["prophetData'}, Dispute["prophetData"]
.
I'm surprised that the types are not being enforced by viem with the help of the TypeScript.
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 (receipt.status !== "success") { | ||
throw new Error("Transaction failed"); |
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.
Custom error 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.
✅
|
||
if (receipt.status !== "success") { | ||
throw new Error("Transaction failed"); |
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.
Custom error 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.
✅
}); | ||
|
||
if (receipt.status !== "success") { | ||
throw new Error("Transaction failed"); |
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.
Custom error 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.
✅
await expect( | ||
protocolProvider.proposeResponse("0x123", 1n, "eip155:1" as Caip2ChainId, 100n), | ||
).resolves.not.toThrow(); | ||
}); |
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.
Let's test failures too when:
- Transaction couldn't be confirmed
- A
ContractFunctionRevertedError
is thrown by viem - Can the
waitForTransactionReceipt
throw a timeout error or something like that? Let's test that too
Testing failure behavior (specially in this class) is almost as important as the happy path. This applies to every Oracle provider method implemented.
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 these for proposeResponse
@0xyaco I think I've addressed all the PR comments but have some questions about changes I had to make to eboActor for example, in proposeResponse I had to refactor to this:
but I don't think this is the correct way to get the proposer. We're getting the proposer in another function by doing
then
but proposeResponse in eboActor is taking a chainId rather than an event... similarly, in onResponseProposed I refactored to:
but to get the disputer we would do dispute.prophetData.disputer and in onResponseProposed we're only getting an event |
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.
Great changes, left some other comments!
} | ||
|
||
const response: Response["prophetData"] = { | ||
// TODO: check if this is the correct proposer | ||
proposer: request.prophetData.requester, |
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.
The proposer must be the address of the transaction sender as Prophet actually checks that the sender === propose.proposer
[1].
Viem has some methods that might be helpful for this like getAddresses
[2] [3].
[1] https://github.com/defi-wonderland/prophet-core/blob/0954a47f7dbd225ee0d48c9face954a7db6aed30/solidity/contracts/Oracle.sol#L118-L119
[2] https://viem.sh/docs/clients/wallet.html#account-optional
[3] https://viem.sh/docs/actions/wallet/getAddresses.html
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 added a new function to protocolProvider to get the writeClient account address, added a new custom error and some tests
|
||
const dispute: Dispute["prophetData"] = { | ||
// TODO: check if this is the correct disputer | ||
disputer: this.actorRequest.id, |
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.
Same as the proposeResponse
case. The disputer is the transaction submitter address.
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.
✅
_request: Request["prophetData"], | ||
_response: Response["prophetData"], |
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 small detail, we want to remove those _
prefixes for arguments.
I think they were left there to be consistent with the methods being defined but not implemented (so the params had to be prefixed with an underscore so ESLint could pass).
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.
✅
_request: Request["prophetData"], | ||
_response: Response["prophetData"], | ||
_dispute: Dispute["prophetData"], |
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.
Same 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.
✅ got rid of the _ in the whole file--no lint issues
response.prophetData.response.block + 1n, | ||
expect.objectContaining({}), | ||
expect.objectContaining({ | ||
proposer: expect.any(String), |
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.
Remember to update this when setting up the proposer, super important to have this also validated
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.
✅
indexedEpochBlockNumber, | ||
expect.objectContaining(request.prophetData), | ||
expect.objectContaining({ | ||
proposer: expect.any(String), |
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.
Test proposer value here too
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.
✅
const mockRequest = {} as Request["prophetData"]; | ||
const mockResponse = {} as Response["prophetData"]; |
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.
As a sidenote, if for some reason you need to build objects populated with proper data, you have the mock builders for requests, responses and disputes at tests/mocks/eboActor.ts
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 the file I replaced all the blank {} with the mocked request in fixtures and added a mocked response and dispute
); | ||
|
||
(protocolProvider["readClient"].waitForTransactionReceipt as Mock).mockRejectedValue( | ||
new Error("Transaction receipt timeout"), |
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.
Check this out, proper error for this scenario!
https://viem.sh/docs/glossary/errors#waitfortransactionreceipttimeouterror
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, made it more accurate
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 one last comment and you've got the green light on my end 💯
@@ -72,11 +82,23 @@ describe("ProtocolProvider", () => { | |||
}, | |||
}), | |||
getBlock: vi.fn(), | |||
waitForTransactionReceipt: vi.fn().mockResolvedValue({ status: "success" }), | |||
waitForTransactionReceipt: vi.fn().mockImplementation(async ({ hash }) => { | |||
await new Promise((resolve) => setTimeout(resolve, 100)); |
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.
Do you need this timeout here? We don't want to simulate any delay during unit tests, let's just make the function instantly resolve to the return value we want to use.
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 it's ok to take out--I removed it
const mockRequest = {} as Request["prophetData"]; | ||
const mockResponse = {} as Response["prophetData"]; | ||
const mockRequestProphetData = DEFAULT_MOCKED_REQUEST_CREATED_DATA.prophetData; | ||
const mockResponseProphetData = DEFAULT_MOCKED_RESPONSE_DATA.prophetData; |
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 need to change this in this PR, just as a side note.
It might be better to use the mocks.buildResponse(request)
helper function here to make it clear that there's a link between the response and the request that you're using.
const mockResponseProphetData = DEFAULT_MOCKED_RESPONSE_DATA.prophetData; | |
const mockRequest = DEFAULT_MOCKED_RESPONSE_DATA; | |
const mockResponse = mocks.buildResponse(mockRequest); | |
await expect( | |
protocolProvider(mockRequest.prophetData, mockResponse.prophetData) | |
) |
Here you've got that helper function in action.
const expectedAddress = privateKeyToAccount(mockedPrivateKey).address; | ||
expect(protocolProvider.getAccountAddress()).toBe(expectedAddress); |
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.
Let's gooooo! 🚀 🚀
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.
niceee 🚀
🤖 Linear
Closes GRT-141
Description