Skip to content
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

[streamr] remove erc20-balance-of functionality from streamr strategy #1

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/strategies/streamr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ Operators are the node running "miners" in the Streamr Network. They run Streamr

This is why part of the Operators' DATA tokens are staked in Sponsorships (through an Operator contract that they control). Only a small portion of DATA is expected to be in the Streamr Network participants' wallets, the rest is staked or delegated into the Streamr Network.

The point of the Streamr snapshot strategy is to allocate voting power not only according to DATA token holding (as in the plain erc-20-balance-of strategy), but also counting in the DATA tokens the token holders control via staking and delegation.
The Streamr snapshot strategy counts the "Operator value" of the Operator contract associated with the voter, which is the total DATA the voter controls through the Operator contract. It roughly equals the DATA delegated to the Operator, plus earnings the Operator has received from the Sponsorship contracts.

This makes it possible to give voting power not only according to DATA token holdings (using the plain erc20-balance-of strategy), but also counting in the DATA tokens the token holders have employed in the Streamr Network incentivization mechanisms.

## Parameters

```json
{
"symbol": "DATA (operator)",
"tokenAddress": "0x3a9A81d576d83FF21f26f325066054540720fC34",
"operatorFactoryAddress": "0x935734e66729b69260543Cf6e5EfeB42AC962183"
}
```
55 changes: 20 additions & 35 deletions src/strategies/streamr/index.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
import { BigNumber } from '@ethersproject/bignumber';
import { BigNumberish } from '@ethersproject/bignumber';
import { formatEther } from '@ethersproject/units';
import { Multicaller } from '../../utils';

export const author = 'streamr-dev';
export const version = '0.1.1';
export const version = '0.2.0';

const ADDRESS_ZERO = '0x0000000000000000000000000000000000000000';

const abi = [
'function balanceOf(address account) external view returns (uint256)', // in DATA token
'function operators(address owner) external view returns (address)', // in OperatorFactory, returns operator contract address
'function valueWithoutEarnings() external view returns (uint)' // in Operator contract
];

type Balances = {
tokens: BigNumber;
staked?: BigNumber;
};

export async function strategy(
space,
network,
Expand All @@ -27,45 +21,36 @@ export async function strategy(
snapshot
): Promise<Record<string, number>> {
const blockTag = typeof snapshot === 'number' ? snapshot : 'latest';
const factory = options.operatorFactoryAddress;

// find the Operator contract deployed by the DATA token holder, will return ADDRESS_ZERO if not found
const getContractOf = new Multicaller(network, provider, abi, { blockTag });
for (const tokenHolder of addresses) {
getContractOf.call(
tokenHolder,
options.operatorFactoryAddress,
'operators',
[tokenHolder]
);
for (const voter of addresses) {
getContractOf.call(voter, factory, 'operators', [voter]);
}
const contractOf: Record<string, string> = await getContractOf.execute();

// get both the "cash in hand", and DATA tokens staked through the Operator contract (Operator value)
const getBalances = new Multicaller(network, provider, abi, { blockTag });
for (const tokenHolder of addresses) {
getBalances.call(
`${tokenHolder}.tokens`,
options.tokenAddress,
'balanceOf',
[tokenHolder]
);
if (contractOf[tokenHolder] != ADDRESS_ZERO) {
getBalances.call(
`${tokenHolder}.staked`,
contractOf[tokenHolder],
// find the DATA tokens staked through the Operator contract (Operator value)
const getOperatorValueOf = new Multicaller(network, provider, abi, {
blockTag
});
for (const voter of addresses) {
if (contractOf[voter] != ADDRESS_ZERO) {
getOperatorValueOf.call(
voter,
contractOf[voter],
'valueWithoutEarnings',
[]
);
}
}
const balances: Record<string, Balances> = await getBalances.execute();
const operatorValueOf: Record<string, BigNumberish> =
await getOperatorValueOf.execute();

return Object.fromEntries(
Object.entries(balances).map(
([address, { tokens, staked = BigNumber.from(0) }]) => [
address,
parseFloat(formatEther(tokens.add(staked)))
]
)
addresses.map((voter) => [
voter,
parseFloat(formatEther(operatorValueOf[voter] ?? '0'))
])
);
}
8 changes: 0 additions & 8 deletions src/strategies/streamr/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,6 @@
"examples": ["e.g. DATA (operator)"],
"maxLength": 16
},
"tokenAddress": {
"type": "string",
"title": "DATA token address",
"examples": ["e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"],
"pattern": "^0x[a-fA-F0-9]{40}$",
"minLength": 42,
"maxLength": 42
},
"operatorFactoryAddress": {
"type": "string",
"title": "OperatorFactory address",
Expand Down