diff --git a/src/strategies/index.ts b/src/strategies/index.ts index 3b712e356..37f6bf477 100644 --- a/src/strategies/index.ts +++ b/src/strategies/index.ts @@ -480,6 +480,7 @@ import * as dappcomposerGetVotingUnits from './dappcomposer-getvotingunits'; import * as erc20BalanceOfSaevo from './erc20-balance-of-saevo'; import * as apecoinStaking from './apecoin-staking'; import * as shroomyVotingPower from './shroomy-voting-power'; +import * as pufferGetPastVotes from './puffer-getpastvotes'; import * as prlInSpRL2Balance from './prl-in-sprl2-balance'; import * as edenOnlineOverride from './eden-online-override'; @@ -974,6 +975,7 @@ const strategies = { 'superfluid-vesting': superfluidVesting, synapse, 'dappcomposer-getvotingunits': dappcomposerGetVotingUnits, + 'puffer-getpastvotes': pufferGetPastVotes, 'prl-in-sprl2-balance': prlInSpRL2Balance, 'eden-online-override': edenOnlineOverride }; diff --git a/src/strategies/puffer-getpastvotes/README.md b/src/strategies/puffer-getpastvotes/README.md new file mode 100644 index 000000000..6266c7d13 --- /dev/null +++ b/src/strategies/puffer-getpastvotes/README.md @@ -0,0 +1,5 @@ +# Puffer Governance: Voting + +In Puffer, the voting power is calculated using the amount of `$vlPUFFER` delegated to user at the end of the previous period. + +This strategy is used to get the voting power at the end of the previous period. \ No newline at end of file diff --git a/src/strategies/puffer-getpastvotes/examples.json b/src/strategies/puffer-getpastvotes/examples.json new file mode 100644 index 000000000..3ed580468 --- /dev/null +++ b/src/strategies/puffer-getpastvotes/examples.json @@ -0,0 +1,19 @@ +[ + { + "name": "vlPUFFER Example", + "strategy": { + "name": "puffer-getpastvotes", + "params": { + "address": "0xcD1adFB7Ae5E08F7494cdE2EA6666CeF5cc4804c", + "decimals": 18 + } + }, + "network": "11155111", + "addresses": [ + "0xe19cBAAa2e546f8C6175892190d1f26279d19995", + "0xDDDeAfB492752FC64220ddB3E7C9f1d5CcCdFdF0", + "0x65d2dd7A66a2733a36559fE900A236280A05FBD6" + ], + "snapshot": 8422135 + } +] diff --git a/src/strategies/puffer-getpastvotes/index.ts b/src/strategies/puffer-getpastvotes/index.ts new file mode 100644 index 000000000..f2855a603 --- /dev/null +++ b/src/strategies/puffer-getpastvotes/index.ts @@ -0,0 +1,67 @@ +import { BigNumberish } from '@ethersproject/bignumber'; +import { formatUnits } from '@ethersproject/units'; +import { Multicaller } from '../../utils'; + +export const author = 'bxmmm1'; +export const version = '0.1.0'; + +const abi = [ + 'function getPastVotes(address account, uint256 timepoint) view returns (uint256)' +]; + +export async function strategy( + _space, + network, + provider, + addresses, + options, + snapshot +): Promise> { + const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; + + // Create a multicaller for the block tag, either past / present + const multi = new Multicaller(network, provider, abi, { blockTag }); + + // Figure out the current block number + let blockNumber = blockTag; + + // If the block tag is not the latest block, use the block tag as blockNumber + if (blockTag === 'latest') { + blockNumber = await provider.getBlockNumber(); + } + + // Fetch that block to figure out the block timestamp (current interval block timestamp) + const block = await provider.getBlock(blockNumber); + + // We want to use `getPastVotes` because of the delegation happening on the contract + // We want to use the past timestamp to get the votes at the start of the previous week + + // Calculate the timestamp at the end of the most recent complete 7-day period + // This ensures alignment with contract's 7-day voting periods + const SECONDS_PER_WEEK = 7 * 24 * 60 * 60; + // Get the current timestamp in seconds + const currentIntervalBlockTimestampInSeconds = block.timestamp; + + // Calculate the current period based on the current timestamp + const currentPeriod = Math.floor( + currentIntervalBlockTimestampInSeconds / SECONDS_PER_WEEK + ); + // Calculate the timestamp at the end of the previous period + const previousPeriodEnd = currentPeriod * SECONDS_PER_WEEK - 1; + + addresses.forEach((address) => + multi.call(address, options.address, 'getPastVotes', [ + address, + previousPeriodEnd.toString() + ]) + ); + + const result: Record = await multi.execute(); + + return Object.fromEntries( + Object.entries(result).map(([address, balance]) => [ + address, + parseFloat(formatUnits(balance, options.decimals)) + ]) + ); +} diff --git a/src/strategies/puffer-getpastvotes/schema.json b/src/strategies/puffer-getpastvotes/schema.json new file mode 100644 index 000000000..78af53673 --- /dev/null +++ b/src/strategies/puffer-getpastvotes/schema.json @@ -0,0 +1,28 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$ref": "#/definitions/Strategy", + "definitions": { + "Strategy": { + "title": "Strategy", + "type": "object", + "properties": { + "address": { + "type": "string", + "title": "Contract address", + "examples": ["e.g. 0xcD1adFB7Ae5E08F7494cdE2EA6666CeF5cc4804c"], + "pattern": "^0x[a-fA-F0-9]{40}$", + "minLength": 42, + "maxLength": 42 + }, + "decimals": { + "type": "number", + "title": "Decimals", + "examples": ["e.g. 18"], + "minimum": 0 + } + }, + "required": ["address", "decimals"], + "additionalProperties": false + } + } +}