# Cryptography in Fluence Developer Rewards

Over 110,000 web3 developers have been allocated 5,000 `FLT` tokens each, with allocations halving every three months. The Fluence team has released a comprehensive guide on how to claim your allocation of the ERC20 token FLT-DROP. For more details, visit:

[Fluence Network - Developer Rewards](https://blog.fluence.network/flt-developer-reward/)

I happened to be one of the lucky devs who received an allocation, so I was curious how the distribution process works.

This article delves deeper into the cryptography underlying the distribution of rewards and outlines the conversion process from `FLT-DROP` to `FLT`.

# TL;DR

Once your lockup period concludes, you can transfer 5,000 FLT (or your specific allocation) to any address. The FLT-DROP contract will then transfer an equivalent amount of FLT to your address. This process is outlined in the `transfer(address to, uint256 value)` function within the `DevRewardDistributor` contract, which can be viewed here:

[Etherscan - DevRewardDistributor Contract](https://etherscan.io/token/0x6081d7f04a8c31e929f25152d4ad37c83638c62b#code)

```solidity
function transfer(address to, uint256 value) external returns (bool) {
		require(value > 0, "Value is 0");
		require(lockedBalances[msg.sender].amount == value, "Invalid amount");
		require(block.timestamp > lockedBalances[msg.sender].unlockTime, "Tokens are locked");
		
		// your FLT-DROP is set to 0
		lockedBalances[msg.sender].amount = 0;
		_totalSupply -= value;
		// FLT is transferred to you from the **DevRewardDistributor contract**
		IERC20(token).safeTransfer(msg.sender, value);
		emit Transfer(msg.sender, address(0x00), value);
		
		return true;
}
```

At the time of writing, the `DevRewardDistributor` (FLT-DROP) holds 48,740,000 FLT.

![DevRewardDistributor's FLT balance](https://cdn.hashnode.com/res/hashnode/image/upload/v1714422262961/aeae09a8-e037-472b-8c0b-3cb23f4a0372.png align="center")

You can verify this yourself in the "Read as Proxy" section:

[Etherscan - FLT](https://etherscan.io/token/0x236501327e701692a281934230af0b6be8df3353#readProxyContract)

Note that the balance is displayed in wei, which is 1/(10^18) of an Ether.

# What is Fluence?

Fluence is a distributed computing project which aims to liberate computation from centralized infrastructures, providing low-cost, decentralized, and verifiable computing solutions.

Learn more about their initiatives at [Fluence Network](https://fluence.network/).

# Rewards Architecture

The rewards program consists of two main phases:

* **Generation Phase:** Conducted prior to the contract deployment by the Fluence team.
    
* **Claim Phase:** Executed by developers once the contracts are live.
    

## Generation Phaze

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1714422350582/e222d01a-44d1-4af5-953f-81be3425f4a7.png align="center")

During the generation phase, the Fluence team:

* Compiled a list of developer public keys.
    
* Generated random Ethereum accounts, each linked to a developer's public key  
    The source of randomness for the private keys comes from a python package called `secrets`: [https://github.com/fluencelabs/dev-rewards/blob/c82cfa5acc4fd0384e0724baaa8b499abc4c6cea/python/generate.py#L99](https://github.com/fluencelabs/dev-rewards/blob/c82cfa5acc4fd0384e0724baaa8b499abc4c6cea/python/generate.py#L99)
    
* Encrypted the private keys of those Ethereum accounts with the developers’ GitHub pubkeys An asymmetric encryption tool called `age` has been used: [https://github.com/fluencelabs/dev-rewards/blob/c82cfa5acc4fd0384e0724baaa8b499abc4c6cea/python/generate.py#L106](https://github.com/fluencelabs/dev-rewards/blob/c82cfa5acc4fd0384e0724baaa8b499abc4c6cea/python/generate.py#L106) [https://github.com/FiloSottile/age](https://github.com/FiloSottile/age)
    
* Constructed a Merkle tree of the Ethereum account addresses to embed within the rewards contract. [https://github.com/fluencelabs/dev-rewards/blob/c82cfa5acc4fd0384e0724baaa8b499abc4c6cea/python/generate.py#L249](https://github.com/fluencelabs/dev-rewards/blob/c82cfa5acc4fd0384e0724baaa8b499abc4c6cea/python/generate.py#L249)
    

The source code for the generation script is accessible here:

[Fluence Labs - Developer Rewards Script](https://github.com/fluencelabs/dev-rewards/blob/c82cfa5acc4fd0384e0724baaa8b499abc4c6cea/python/generate.py#L145)

## Claim Phaze

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1714422386294/542f26c5-4f04-4c5e-a316-317eba2cf9ef.png align="center")

To claim their allocated tokens, a developer must:

* Decrypt the Ethereum account's private key using their GitHub private key.
    
* Generate a Merkle proof of inclusion for their Ethereum account in the tree.
    
* Sign an Ethereum message with the temporary account as proof of possession.
    

It is as easy as that!

We can check out the `DevRewardDistributor` contract to see the two cryptographic mechanisms in action:

```solidity
/**
 * @notice Claim reward token
 * @param userId - user id in merkle tree
 * @param merkleProof - merkle proof for leaf
 * @param temporaryAddress - temporary Ethereum address that's used only for signing
 * @param signature - signature of temporary Ethereum address
 *
 */
function claimTokens(
    uint32 userId,
    bytes32[] calldata merkleProof,
    address temporaryAddress,
    bytes calldata signature
) external whenClaimingIs(true) {
    ...
    // the value, which existance in the merkle tree you prove
    bytes32 leaf = keccak256(abi.encodePacked(userId, temporaryAddress));
		// MerkleProof.verify reverts if the merkle proof is invalid,
		// e.g. you are trying to prove a non-existant temporary account
		// or an invalid tree root
    require(MerkleProof.verify(merkleProof, merkleRoot, leaf), "Valid proof required");

		// the message signed by your temporary address
    bytes32 msgHash = keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\\n20", msg.sender));
    // the recovered signer of the provided message
    address signer = ECDSA.recover(msgHash, signature);
    // if the recovered signer is valid, you indeed posess this temporary account
    require(signer == temporaryAddress, "Invalid signature");

	  ...
} 
```

# Conclusion

The setup here is pretty straightforward and well designed.

At first I was a bit surprised by the fact that the team decided to create random accounts and encrypt the private keys.

This implementation involves some trust — we have to trust that the team didn’t keep copies of the keys before they got encrypted.

I believe this has been done for the sake of simplicity of the cryptographic scheme.

A simple cryptosystem:

* Is cheaper to develop (in developer hours).
    
* Is faster to compute.
    
* Is more straightforward to explain and understand.
    

Could it be done trustlessly though? Sure, that’s totally possible. But diving into how that might work is for another day and another article.
