Skip to content

Commit

Permalink
Merge pull request #1 from opensource-observer/jabolol/sdk-scaffold
Browse files Browse the repository at this point in the history
add: `weight` update functionality
  • Loading branch information
Jabolol authored Oct 20, 2024
2 parents 7f6829b + cb0c758 commit e98b886
Show file tree
Hide file tree
Showing 19 changed files with 3,685 additions and 0 deletions.
43 changes: 43 additions & 0 deletions .github/workflows/distribution.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Update Split Weights

on:
push:
branches:
- main
pull_request:
branches:
- main
schedule:
- cron: "0 19 * * 0"

jobs:
update-weights:
runs-on: ubuntu-latest
env:
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
SUPABASE_KEY: ${{ secrets.SUPABASE_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
INFURA_KEY: ${{ secrets.INFURA_KEY }}
INFURA_API_URL: ${{ secrets.INFURA_API_URL }}
HOT_WALLET_ADDRESS: ${{ secrets.HOT_WALLET_ADDRESS }}
HOT_WALLET_PRIVATE_KEY: ${{ secrets.HOT_WALLET_PRIVATE_KEY }}
SPLITS_API_KEY: ${{ secrets.SPLITS_API_KEY }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9
run_install: |
- recursive: true
args: [--frozen-lockfile, --strict-peer-dependencies]
- name: Setup node
uses: actions/setup-node@v4
with:
cache: "pnpm"
node-version: "20.x"
- name: Build sdk
run: pnpm build:sdk
- name: Update Weights
run: cd packages/sdk && pnpm update:weights
38 changes: 38 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# Dependencies
node_modules
.pnp
.pnp.js

# Local env files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

# Testing
coverage

# Turbo
.turbo

# Vercel
.vercel

# Build Outputs
.next/
out/
build
dist


# Debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Misc
.DS_Store
*.pem
35 changes: 35 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "ethglobal-sf-2024",
"description": "Funding public goods.",
"version": "0.0.0",
"author": "Kariba Labs",
"license": "Apache-2.0",
"private": true,
"repository": {
"type": "git",
"url": "git+https://github.com/opensource-observer/ethglobal-sf-2024.git"
},
"scripts": {
"build": "turbo run build --concurrency=100%",
"build:sdk": "turbo run build --filter=@ethglobal-sf-2024/sdk",
"lint": "turbo run lint --concurrency=100%",
"test": "turbo run test --concurrency=1"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^8.10.0",
"@typescript-eslint/parser": "^8.10.0",
"eslint": "^9.13.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-react": "^7.37.1",
"prettier": "^3.3.3",
"turbo": "^2.2.1"
},
"engines": {
"node": ">=18.x",
"pnpm": ">=8"
},
"packageManager": "pnpm@9.6.0",
"dependencies": {
"pyright": "^1.1.385"
}
}
9 changes: 9 additions & 0 deletions packages/sdk/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
SUPABASE_URL=
SUPABASE_KEY=
GITHUB_TOKEN=
INFURA_KEY=
INFURA_API_URL=
HOT_WALLET_ADDRESS=
HOT_WALLET_PRIVATE_KEY=
SPLITS_API_KEY=
POPULATE_USER_ID=
38 changes: 38 additions & 0 deletions packages/sdk/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "@ethglobal-sf-2024/sdk",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"build": "tsc",
"format": "prettier --write .",
"populate:db": "node dist/index.js populate:db",
"update:weights": "node dist/index.js update:weights",
"dispatch:email": "node dist/index.js dispatch:email"
},
"keywords": [],
"authors": [
{
"name": "Javier Ríos",
"email": "javi@karibalabs.co"
}
],
"license": "ISC",
"devDependencies": {
"@types/node": "^22.7.7",
"@types/yargs": "^17.0.33",
"prettier": "^3.3.3",
"typescript": "~5.6.3"
},
"dependencies": {
"@0xsplits/splits-sdk": "^4.1.0",
"@actions/core": "^1.11.1",
"@faker-js/faker": "^9.0.3",
"@octokit/rest": "^21.0.2",
"@supabase/supabase-js": "^2.45.6",
"dotenv": "^16.4.5",
"typescript-monads": "^9.0.0",
"viem": "^2.21.29",
"yargs": "^17.7.2"
}
}
32 changes: 32 additions & 0 deletions packages/sdk/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import yargs from "yargs";
import { updateWeights } from "./metrics/index.js";
import { populateDb } from "./utils/misc.js";

const args = await yargs(process.argv.slice(2))
.command("populate:db", "Populate the database with initial data")
.command("update:weights", "Update weights in the Split")
.command("dispatch:email", "Dispatch email with the new weights")
.demandCommand(1)
.help().argv;

const [command] = args._;

const commandMap: {
[key: string]: () => unknown;
} = {
"populate:db": async () => {
const result = await populateDb();
if (result.isFail()) {
throw result.unwrapFail();
}
},
"update:weights": async () => {
const result = await updateWeights();
if (result.isFail()) {
throw result.unwrapFail();
}
},
"dispatch:email": () => void 0,
};

await commandMap[command]();
108 changes: 108 additions & 0 deletions packages/sdk/src/metrics/computeWeights.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { supabase } from "../utils/client.js";
import { FailResult, OkResult, Result } from "typescript-monads";
import { fetchMetricsForPool } from "./fetchMetricsForPool.js";
import core from "@actions/core";

type FundingWeight = {
contributor: {
id: string;
wallet: string;
artifact_namespace: string;
artifact_name: string;
};
allocatedFunding: number;
};

export const computeWeights = async (): Promise<
Result<Record<string, FundingWeight[]>, Error>
> => {
core.info("Computing weights for funding pools");
const { data: poolData, error } = await supabase
.from("funding_pools")
.select("*");

if (error) {
return new FailResult(new Error(error.message));
}

const fundingWeights: {
[key: string]: FundingWeight[];
} = {};

for (const pool of poolData) {
const metricsResult = await fetchMetricsForPool(pool.id);

if (metricsResult.isFail()) {
return new FailResult(metricsResult.unwrapFail());
}
const metrics = metricsResult.unwrap();

const prWeight = 0.3;
const issueWeight = 0.2;
const linesWeight = 0.4;
const commitWeight = 0.1;

const fundingPool = 100;

let totalPRs = 0;
let totalIssues = 0;
let totalLinesAdded = 0;
let totalLinesDeleted = 0;
let totalCommits = 0;

metrics.forEach(({ metrics }) => {
totalPRs += metrics.prCount;
totalIssues += metrics.issueCount;
totalLinesAdded += metrics.linesAdded;
totalLinesDeleted += metrics.linesDeleted;
totalCommits += metrics.commitCount;
});

const contributorsShares = metrics.map(({ metrics, info }) => {
const normalizedPRs = metrics.prCount / totalPRs;
const normalizedIssues = metrics.issueCount / totalIssues;
const normalizedLinesAdded = metrics.linesAdded / totalLinesAdded;
const normalizedLinesDeleted = metrics.linesDeleted / totalLinesDeleted;
const normalizedCommits = metrics.commitCount / totalCommits;

const normalizedLines = (normalizedLinesAdded + normalizedLinesDeleted) /
2;

const share = prWeight * normalizedPRs +
issueWeight * normalizedIssues +
linesWeight * normalizedLines +
commitWeight * normalizedCommits;

return {
contributor: {
id: info.user_id,
wallet: info.wallet_address,
artifact_namespace: info.artifact_namespace,
artifact_name: info.artifact_name,
},
sharePercentage: share * 100,
};
});

const fundingDistribution = contributorsShares.map((contributorShare) => {
return {
contributor: contributorShare.contributor,
allocatedFunding: +((contributorShare.sharePercentage / 100) *
fundingPool).toFixed(4),
};
});

fundingWeights[pool.id] = fundingDistribution;

core.info(`Computed weights for pool ${pool.id}`);
core.startGroup("Weights");
fundingDistribution.forEach((fundingWeight) => {
core.info(
`${fundingWeight.contributor.artifact_namespace}/${fundingWeight.contributor.artifact_name}: ${fundingWeight.allocatedFunding}`,
);
});
core.endGroup();
}

return new OkResult(fundingWeights);
};
58 changes: 58 additions & 0 deletions packages/sdk/src/metrics/fetchMetricsForPool.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { supabase } from "../utils/client.js";
import { Tables } from "types.js";
import { FailResult, OkResult, Result } from "typescript-monads";
import { metricsForProject, type PoolEntryMetrics } from "../utils/metrics.js";
import core from "@actions/core";

type ExtendedMetrics = {
info: Tables<"pool_registrations">;
metrics: PoolEntryMetrics;
};

export const fetchMetricsForPool = async (
poolUuid: string,
): Promise<Result<ExtendedMetrics[], Error>> => {
core.info(`Fetching metrics for pool ${poolUuid}`);
const { data, error } = await supabase
.from("funding_pools")
.select("*")
.eq("id", poolUuid);
if (error) {
return new FailResult(error);
}

const [pool] = data;
const metrics: ExtendedMetrics[] = [];

const { data: poolData, error: poolError } = await supabase
.from("pool_registrations")
.select("*")
.eq("pool_id", pool.id);

if (poolError) {
return new FailResult(poolError);
}

for (const project of poolData) {
const projectMetric = await metricsForProject(
project.artifact_namespace,
project.artifact_name,
);

const flatMappedMetric = projectMetric.flatMap(
(metric) =>
new OkResult({
info: project,
metrics: metric,
}),
);

if (flatMappedMetric.isFail()) {
return new FailResult(flatMappedMetric.unwrapFail());
}

metrics.push(flatMappedMetric.unwrap());
}

return new OkResult(metrics);
};
1 change: 1 addition & 0 deletions packages/sdk/src/metrics/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./updateWeights.js";
Loading

0 comments on commit e98b886

Please sign in to comment.