Change the sysvar clock in Solana cargo tests

0xksure
4 min readDec 29, 2022

--

Photo by Murtaza Vora on Unsplash

A basic guide to mock the Clock sysvar in solana_program_test and why you should write integration tests in rust.

Note: If you are just here for the solution then jump down to 2. The Solution.

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

https://twitter.com/crispheaney/status/1607921657544318982?t=DV_pCeRxsa40MkOEQ6DgpQ&s=19

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 test-bpf=[]in your cargo.toml

Here is a code snippet without the imported crates. See here for the full code

Taken from https://github.com/Sure-Protocol/sure-v1/blob/f9a8d721d37b0a8862c010dfb5f556ca29b565e8/programs/oracle/tests/test.rs#L77

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

I hope this answered your question. If not please comment down 👇 below so I can update or create a new post.

Please follow me on twitter for shorter takes on the crypto space and especially solana development. Also, check out README.md for more of my stories.

--

--

0xksure
0xksure

Written by 0xksure

Developer and Mathematician

No responses yet