This post is a series of posts on gotchas I have experienced as a Solana dev. Please check out the README.md for more gotchas and confusing bugs.
Now let’s get to work.
1. The motivation
If you’re using anchor to write your solana program you probably know that anchor auto generates an IDL and types you can use to interact with your program. This is especially awesome if you would like to write integration tests in typescript. It’s as easy as defining the anchor program by specifying the type as the IDL.
However, there are some downsides to writing integration tests in typescript. The client you are using is just a rpc connection. Don’t get me wrong, it’s the closest thing you get to a real thing.
One of the struggles of writing integration tests directly using an rpc
Another, which is the case for this post, is programs that need to live a longer period of time to test the full functionality. This is not possible when only using an RPC connection (or it is, but then you would have to time out to infinity)
2. The solution
I will assume that you have enabled
Here is a code snippet without the imported crates. See here for the full code
As you can see from the method
test_forward_time , first we get the clock sysvar from the bank client. Again, the fact that we have access to the bank_client is the sole reason we are doing it. Then, we clone the
Clock and add the appropriate duration.
Note: The unix_timestamp is the time in seconds since 1st Jan 1970. You can see the current unix time here.
Finally, in the last line in
test_forward_time we call
set_sysvar and pass a reference to our new clock.
Everything else inside
create_init_test is just creating the test context that holds the
bank_client and starting it.
3. The case
You might be thinking why would you ever sacrifice an ergonomic typescript setup for this.
The reason is that at Sure we are building an oracle that depends on the passage of time.
The basic flow is as follows
- Proposer creates a proposal at time t_0
- t_0 -> t_1: anyone can vote on what they believe is the correct value.
- t_1->t_2: users can reveal their vote values and calculate
- t_2 -> t_3: the proposer can calculate the vote distribution and it’s parameters. It’s assumed that the difference is exponentially distributed
- t_3->t_4: users can calculate their share of the voting pool and get paid depending on how close they came to consensus.
Now t_(i+1) — t_(i) is set in the configuration of the program and can be updated through governance. As you probably notice, this is a commit and reveal scheme.
To test the entire flow we need to fast forward in time to the next stage. It would be very time consuming to timeout the program for an entire hour or a day(!).
3. Show me an example
You can find the rust integration tests in this unmerged PR. The code example in 2. is taken from the PR.
4. Why you should not
You should probably not write the integration tests in rust if
- You don’t need to change any of the sysvars to test your entire program
- Rely heavily on running all unit and integration tests in a pipeline
- If you are starting out with Solana development