Skip to content

Commit

Permalink
Adding new strategy lrc-nft-search-mult[lrc-nft-search-mult] (snapsho…
Browse files Browse the repository at this point in the history
…t-labs#1098)

* initial commit of lrc-nft-search-mult

This is an extended functionality version of a previous Loopring NFT voting strategy. This allows for queried scores to be multiplied by a value to allow vote weighting in conjunction with multiple strategies.

* Update index.ts

Update to add lrc-nft-search-mult strategy

* Update src/strategies/lrc-nft-search-mult/index.ts

Update strategy version

Co-authored-by: Chaitanya <[email protected]>

* Update examples.json

Add additional addresses to satisfy test case conditions

---------

Co-authored-by: Chaitanya <[email protected]>
  • Loading branch information
Raecaug and ChaituVR authored Mar 15, 2023
1 parent 486b7a7 commit fce81f5
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/strategies/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ import * as lrcL2SubgraphBalanceOf from './lrc-l2-subgraph-balance-of';
import * as lrcL2NftBalanceOf from './lrc-l2-nft-balance-of';
import * as lrcLPSubgraphBalanceOf from './lrc-lp-subgraph-balance-of';
import * as lrcNFTDAOSearch from './lrc-nft-dao-search';
import * as lrcNFTmult from './lrc-nft-search-mult';
import * as erc3525VestingVoucher from './erc3525-vesting-voucher';
import * as rariFuse from './rari-fuse';
import * as selfswap from './selfswap';
Expand Down Expand Up @@ -802,6 +803,7 @@ const strategies = {
'lrc-l2-nft-balance-of': lrcL2NftBalanceOf,
'lrc-lp-subgraph-balance-of': lrcLPSubgraphBalanceOf,
'lrc-nft-dao-search': lrcNFTDAOSearch,
'lrc-nft-search-mult': lrcNFTmult,
'rari-fuse': rariFuse,
'bancor-pool-token-underlying-balance': bancorPoolTokenUnderlyingBalance,
selfswap,
Expand Down
29 changes: 29 additions & 0 deletions src/strategies/lrc-nft-search-mult/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# lrc-nft-search-mult

This is a further improvement of karamorf's lrc-l2-nft-balance of Snapshot voting strategy by raecaug(system32).

This strategy is an extension of lrc-nft-dao-search, allowing space owners to apply a multiplier to counted votes.
This can then be combined with additional voting strategies to allow for complex DAO setups with vote weighting of specific NFTs.
Providing a multiplier is necessary; if default behavior is desired, simply specify '1'.

Strategy to read account balances for NFTs (72 or 1155) from LoopringV2 subgraph. Assumes we only want tokens minted by a specific account id.

Here is an example of parameters:

```json
{
"graph": "https://api.thegraph.com/subgraphs/name/juanmardefago/loopring36",
"minter_account_id": "74447",
"tokens": ["token (Collection contract address) to include"],
"nft_ids": ["nftIDs, unique to every nft, even those under the same token contract"],
"blacklisted_account_ids": ["38482"],
"blacklisted_nft_ids": ["... nft id's to exclude ..."]
"multiplier": "3" // Setting this to '1' will result in normal vote counting behavior.
}
```

Use explorer.loopring.io to look up addresses and find account id's.

Account id `38482` maps to `0x000000000000000000000000000000000000dead` and is used for burning tokens.

to note: either the `minter_account_id` or the `tokens` parameter must be provided for this query to work. You do not need to specify both, just one of them.
22 changes: 22 additions & 0 deletions src/strategies/lrc-nft-search-mult/examples.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[
{
"name": "lrcNFTmult",
"strategy": {
"name": "lrc-nft-search-mult",
"params": {
"graph": "https://api.thegraph.com/subgraphs/name/juanmardefago/loopring36",
"minter_account_id": "157510",
"tokens": ["0xb6d91e38e4ac53c9f8952c6c6b1c7aee66c8b6f0"],
"nft_ids": [
"0x1e31297dd163ca44a5fad74de4ffbebf1ba11d46e1b448b0e105449d827fb264"
],
"blacklisted_account_ids": [""],
"blacklisted_nft_ids": [""],
"multiplier": "1"
}
},
"network": "1",
"addresses": ["0xeE253D3fCC30787a1E58570E355010d0b9C33B60","0xddCCE06088517c56FA938bD99cD0820094010F8e","0x9Fd19B8ca6E49eD92142339F54026497cE913492"],
"snapshot": 15677787
}
]
128 changes: 128 additions & 0 deletions src/strategies/lrc-nft-search-mult/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Original code by Karamorf, upgraded by Raecaug(system32)
// Allows querys of Loopring L2 accounts & balances by specifying a nft minter, token contract address(and optionally specifying individual ids to white/blacklist)

import { subgraphRequest } from '../../utils';
export const author = 'raecaug';
export const version = '0.1.0';

const LIMIT = 1000;

function makeQuery(
snapshot, // This is an Ethereum block # or defaults to 'latest'
minter, // This is referred to as account # or account id on the Loopring L2 block explorer
tokens, // NFT collection contract addresses, also referred to as 'token address'
skip, // Used to skip response lines in requests
blacklisted_account_ids, // Ditto properties of 'minter'
blacklisted_nft_ids, // This is the nft id, which is unique for every nft ever minted, allows distinction between nfts in a collection at the chain level
nft_ids // Ditto properties of blacklisted version
) {
const query: any = {
// Query constructor, builds request with params from snapshot space settings
accountNFTSlots: {
__args: {
where: {
nft_: {
id_not_in: blacklisted_nft_ids, // Excluding blacklisted nft ids
nftID_in: nft_ids // Including uniquely specified nft ids
},
account_not_in: blacklisted_account_ids // Excluding blacklisted account ids
},
first: LIMIT,
skip: skip
},
account: { address: true },
balance: true
}
};

if (minter && minter !== '') {
//Check to ensure minter id is specified and not blank
query.accountNFTSlots.__args.where.nft_.minter = minter;
}

if (tokens && tokens.length > 0) {
//Check to ensure at least 1 token to search for is specified
query.accountNFTSlots.__args.where.nft_.token_in = tokens;
}

if (snapshot !== 'latest') {
// If the snapshot date is manually specified, overwrite the 'latest' block, strict inequality check operand used
query.accountNFTSlots.__args = {
...query.accountNFTSlots.__args,
block: {
number: snapshot
}
};
}

return query;
}

export async function strategy( // *****Logical execution begins here; args passed in by Snapshot settings*****
space,
network,
provider,
addresses,
options,
snapshot
): Promise<Record<string, number>> {
let blacklisted_account_ids = options.blacklisted_account_ids;
let blacklisted_nft_ids = options.blacklisted_nft_ids;

let multiplier = options.multiplier; // Multiplier to be applied against returned NFT amounts

let nft_ids = options.nft_ids; // Unique NFT ids, distinguishable from 1155 token contracts

const balances = {}; // Initialization
let skip = 0;
let response_size = 0;

if (!blacklisted_account_ids || blacklisted_account_ids.length === 0) {
// If no blacklisted accts specified, set to empty
blacklisted_account_ids = [''];
}

if (!blacklisted_nft_ids || blacklisted_nft_ids.length === 0) {
// If no unique nft_ids specified, set to empty
blacklisted_nft_ids = [''];
}

if (!nft_ids || nft_ids.length === 0) {
// If no unique nft_ids specified, set to empty
nft_ids = [''];
}

do {
// Transmit query and await results
const response = await subgraphRequest(
// Constructs response variable from subgraph query function
options.graph, // Parameter 1, options specified
makeQuery(
// Query constructor(defined above) called, results are the second parameter
snapshot,
options.minter_account_id,
options.tokens,
skip,
blacklisted_account_ids,
blacklisted_nft_ids,
nft_ids
)
);

response.accountNFTSlots.forEach((slot) => {
// Checking against each accountNFTSlot element
if (!balances.hasOwnProperty(slot.account.address)) {
balances[slot.account.address] = 0; // If nothing returned, set this accounts balance to 0
}
balances[slot.account.address] += (multiplier * parseInt(slot.balance)); // Otherwise, a bigint is returned; parse it, apply multiplier and store in balances array
});
response_size = response.accountNFTSlots.length; // Value is set to 0 on loop entry, updated here, will break loop for anything other than 1000
skip += response_size;
} while (response_size == LIMIT);

const scores = Object.fromEntries(
addresses.map((address) => [address, balances[address.toLowerCase()]]) // Map returned addresses and balances as scores array
);

return scores; // Returns addresses and balances to Snapshot
}

0 comments on commit fce81f5

Please sign in to comment.