-
Notifications
You must be signed in to change notification settings - Fork 3
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
Two handlers, Tests with Mock Events and Mock Blockchain #14
base: main
Are you sure you want to change the base?
Changes from all commits
f2092e4
affe3cd
e959c2e
42fcedf
11ba867
a6beede
21268d1
0ccdf4b
c2d7881
25ce73f
22658fc
486b0bd
c37566d
ba60e5e
fe315a6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,43 @@ | ||
-- Delegatees are account that receive delegation of voting power from Delegators | ||
CREATE TABLE delegation | ||
( | ||
token_address CHAR(20), | ||
delegator_address CHAR(20), | ||
delegatee_address CHAR(20), | ||
network INT, | ||
token_address CHAR(64), | ||
delegator_address CHAR(64), | ||
delegatee_address CHAR(64), | ||
proof TEXT, | ||
delegated_weight BIGINT, | ||
total_weight BIGINT, | ||
delegated_block BIGINT, | ||
PRIMARY KEY (token_address, | ||
delegator_address, | ||
delegatee_address) | ||
PRIMARY KEY ( | ||
network, | ||
token_address, | ||
delegator_address, | ||
delegatee_address, | ||
delegated_block) | ||
); | ||
CREATE INDEX delegation_index_delegator | ||
ON delegation ( | ||
network, | ||
token_address, | ||
delegator_address | ||
); | ||
|
||
CREATE INDEX delegation_index_delegatee | ||
ON delegation ( | ||
token_address, | ||
delegatee_address | ||
network, | ||
token_address, | ||
delegatee_address | ||
); | ||
|
||
-- Delegators delegate their voting power to Delegatees | ||
CREATE TABLE delegators | ||
( | ||
token_address CHAR(20), | ||
delegator_address CHAR(20), | ||
trie_root CHAR(32), | ||
network INT, | ||
token_address CHAR(64), | ||
delegator_address CHAR(64), | ||
trie_root CHAR(128), | ||
delegated_block BIGINT, | ||
PRIMARY KEY (token_address, delegator_address) | ||
total_weight INT, | ||
PRIMARY KEY (network, token_address, delegator_address) | ||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export const compLikeABI = | ||
'[{"inputs":[{"internalType":"address","name":"_admin","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"address","name":"fromDelegate","type":"address"},{"indexed":true,"internalType":"address","name":"toDelegate","type":"address"}],"name":"DelegateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegate","type":"address"},{"indexed":false,"internalType":"uint256","name":"previousBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBalance","type":"uint256"}],"name":"DelegateVotesChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldAdmin","type":"address"},{"indexed":true,"internalType":"address","name":"newAdmin","type":"address"}],"name":"NewAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldPendingAdmin","type":"address"},{"indexed":true,"internalType":"address","name":"newPendingAdmin","type":"address"}],"name":"NewPendingAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Snapshot","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DELEGATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SUPPLY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptAdmin","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"snapshotId","type":"uint256"}],"name":"balanceOfAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"checkpoints","outputs":[{"internalType":"uint256","name":"fromBlock","type":"uint256"},{"internalType":"uint256","name":"votes","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"delegateBySig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"delegates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getCurrentVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getPriorVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"numCheckpoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newPendingAdmin","type":"address"}],"name":"setPendingAdmin","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"snapshot","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"snapshotId","type":"uint256"}],"name":"totalSupplyAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (blocking): Typechain. Follow the standard as defined in https://github.com/windranger-io/windranger-solidity-template and use TypeChain, there should no abi files You can use either dependencies on NPM deployable, other Git repos or local contract interface files There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. gotcha |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import {ethers} from 'ethers' | ||
import * as abi from './abi' | ||
|
||
/** | ||
* Shape of the AWS Gateway event the handler has available | ||
* https://github.com/awsdocs/aws-lambda-developer-guide/blob/main/sample-apps/nodejs-apig/event.json | ||
*/ | ||
|
||
export const getPriorVotingPowerOf = async ( | ||
walletAddress: string, | ||
blockNumber: number | ||
): Promise<number> => { | ||
const provider = new ethers.providers.InfuraProvider( | ||
'rinkeby', | ||
'INFURA_KEY' | ||
) | ||
// RINKEBY COMP-like token | ||
const tokenContract: string = | ||
'0xCB198597184804f175Dc7b562b0b5AF0793e9176' as string | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment | ||
const contract = new ethers.Contract( | ||
tokenContract, | ||
abi.compLikeABI, | ||
provider | ||
) | ||
const result: number = parseInt( | ||
ethers.utils.formatEther( | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-argument | ||
await contract.getPriorVotes(walletAddress, blockNumber) | ||
) | ||
) | ||
return result | ||
} | ||
|
||
export const getCombinedVotingPowerOf = async ( | ||
walletAddress: string, | ||
blockNumber: number | ||
): Promise<number> => { | ||
const provider = new ethers.providers.AlchemyProvider( | ||
'rinkeby', | ||
'ALCHEMY_KEY' | ||
) | ||
// RINKEBY COMP-like token | ||
const tokenContract: string = | ||
'0xCB198597184804f175Dc7b562b0b5AF0793e9176' as string | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment | ||
const contract = new ethers.Contract( | ||
tokenContract, | ||
abi.compLikeABI, | ||
provider | ||
) | ||
let result = 0 | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call | ||
const delegatee: string = await contract.delegates(walletAddress) | ||
if (delegatee === ethers.constants.AddressZero) { | ||
result += parseInt( | ||
ethers.utils.formatEther( | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-argument | ||
await contract.balanceOf(walletAddress, { | ||
blockTag: blockNumber | ||
}) | ||
) | ||
) | ||
} | ||
result += parseInt( | ||
ethers.utils.formatEther( | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-argument | ||
await contract.getPriorVotes(walletAddress, blockNumber) | ||
) | ||
) | ||
return result | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
import {Client, ClientConfig} from 'pg' | ||
import {Database} from '../src/db/database' | ||
import {Database} from './database' | ||
import fs from 'fs' | ||
|
||
const dbUser = 'unit_test_user' | ||
|
@@ -58,6 +58,23 @@ async function query(dbConfig: ClientConfig, sql: string): Promise<void> { | |
await db.end() | ||
} | ||
|
||
async function queryWithParams( | ||
dbConfig: ClientConfig, | ||
sql: string, | ||
params?: any[] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question: how does this pass linting, there's rule about not allowing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added some exceptions, seems to be hard to parse the JSON in TypeScript because is polymorphic. |
||
): Promise<void> { | ||
let queryParams = [] | ||
if (typeof params === 'undefined') { | ||
queryParams = [] | ||
} else { | ||
queryParams = params | ||
} | ||
const db = new Client(dbConfig) | ||
await db.connect() | ||
await db.query(sql, queryParams) | ||
await db.end() | ||
} | ||
|
||
async function createMassDelegationTables( | ||
dbConfig: ClientConfig | ||
): Promise<void> { | ||
|
@@ -66,3 +83,29 @@ async function createMassDelegationTables( | |
fs.readFileSync('./scripts/sql/create_tables.sql', 'utf8') | ||
) | ||
} | ||
|
||
export async function insertDelegateDb( | ||
network: number, | ||
tokenAddress: string, | ||
delegator: string, | ||
delegatee: string, | ||
weight: number, | ||
totalWeight: number, | ||
blockNumber: number | ||
): Promise<void> { | ||
const textQuery = `INSERT INTO delegation | ||
(network, token_address, delegator_address, delegatee_address, | ||
proof, delegated_weight, total_weight, delegated_block) | ||
VALUES | ||
($1, decode($2,'hex'), decode($3,'hex'), decode($4,'hex'), $5, $6, $7, $8 ) RETURNING *` | ||
await queryWithParams(massDelegationDB, textQuery, [ | ||
network, | ||
tokenAddress.slice(2), | ||
delegator.slice(2), | ||
delegatee.slice(2), | ||
'', | ||
weight, | ||
totalWeight, | ||
blockNumber | ||
]) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,8 +4,26 @@ import { | |
} from 'aws-lambda/trigger/api-gateway-proxy' | ||
import {Context} from 'aws-lambda' | ||
import {log} from '../../config/logging' | ||
import {Database} from '../db/database' | ||
import {throwError} from '../error' | ||
import {getCombinedVotingPowerOf} from '../blockchain' | ||
import {getCombinedVotingPowerOfMock} from '../../test/blockchainMock' | ||
|
||
export const getPriorVotingPowerOfImpl = async ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: use function syntax.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, anon function is not very readble. |
||
walletAddress: string, | ||
blockNumber: number, | ||
useMocked: boolean | ||
): Promise<number> => { | ||
let retValue: number | ||
if (useMocked) { | ||
retValue = await getCombinedVotingPowerOfMock( | ||
walletAddress, | ||
blockNumber | ||
) | ||
} else { | ||
retValue = await getCombinedVotingPowerOf(walletAddress, blockNumber) | ||
} | ||
return retValue | ||
} | ||
|
||
/** | ||
* Shape of the AWS Gateway event the handler has available | ||
|
@@ -26,20 +44,36 @@ export const handler = async ( | |
event.queryStringParameters ?? | ||
throwError('Missing query string parameters') | ||
|
||
const address = | ||
queryStringParameters.tokenAddress ?? | ||
throwError('Missing tokenAddress parameter') | ||
const useMockedBlockchain = | ||
queryStringParameters.useMockedBlockchain?.toLowerCase() === 'true' || | ||
false | ||
|
||
const result = await Database.pool().query({ | ||
name: 'fetch-delegation-by-token-address', | ||
text: 'SELECT * FROM delegation WHERE token_address = $1', | ||
values: [address] | ||
}) | ||
const reqBody = event.body || '' | ||
|
||
const rows = result.rows | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment | ||
const obj: {[id: string]: string | string[] | number} = JSON.parse(reqBody) | ||
|
||
const addresses: string[] = | ||
(obj.addresses as string[]) ?? | ||
(throwError('Missing body "addresses" parameter') as unknown) | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access | ||
const blockNumber: number = obj.snapshot as number | ||
|
||
const scores = [] | ||
for (const addr of addresses) { | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment | ||
const addrDict: {[id: string]: string | number} = {address: addr} | ||
addrDict.score = await getPriorVotingPowerOfImpl( | ||
addr, | ||
blockNumber, | ||
useMockedBlockchain | ||
) | ||
scores.push(addrDict) | ||
} | ||
const resp = {score: scores} | ||
return { | ||
statusCode: 200, | ||
body: `Queries: ${rows.toString()}` | ||
body: JSON.stringify(resp) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (blocking): no
token_address
anymore.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and no network.