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

Add/attestations #1129

Merged
merged 66 commits into from
Sep 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
52e783c
Scaffold attestations page
kristoferlund Aug 1, 2023
1cd6c16
Setup dependencies
kristoferlund Aug 2, 2023
8a5926e
useSigner utility hook
kristoferlund Aug 2, 2023
0e6d7c7
Make optimism default network
kristoferlund Aug 2, 2023
3bd4a2e
Attestations WIP
kristoferlund Aug 2, 2023
b00e0d5
Merge branch 'main' into add/attestations
kristoferlund Aug 3, 2023
e942446
Add support for filtering on hostname
kristoferlund Aug 3, 2023
f82bd67
Rename type to FindAllCommunitiesQueryDto
kristoferlund Aug 3, 2023
d0ad3ed
Add attestationsTxHash to period schema
kristoferlund Aug 3, 2023
41ebe87
EAS logo
kristoferlund Aug 3, 2023
208111c
objectToQs: Turn obj to qs
kristoferlund Aug 3, 2023
7e49bda
Hook to interact with Safe
kristoferlund Aug 3, 2023
90a663a
Export PeriodDates type
kristoferlund Aug 3, 2023
c1ac2df
Community selectors and dtos
kristoferlund Aug 3, 2023
b66f52c
Move useSigners to ethers folder
kristoferlund Aug 3, 2023
8ae7119
WIP
kristoferlund Aug 3, 2023
b328d1d
Make communities GET routes public
kristoferlund Aug 3, 2023
9fb2419
EAS, Safe and ETH constants
kristoferlund Aug 3, 2023
eda74d9
useAttestations hook WIP
kristoferlund Aug 3, 2023
6aa57d4
Move useSigner to eth folder
kristoferlund Aug 3, 2023
01e33ed
Format report fields
kristoferlund Aug 3, 2023
471a5cf
UI fix
kristoferlund Aug 3, 2023
3af30eb
New icon
kristoferlund Aug 3, 2023
1b8fad3
USe constants
kristoferlund Aug 3, 2023
70a0ea0
AttestationDialog WIP
kristoferlund Aug 3, 2023
3ceb375
Show attestations button only on correct chain
kristoferlund Aug 4, 2023
782a10d
Allow period.attestationsTxHash to be updated
kristoferlund Aug 4, 2023
fc22893
Make endDate optional
kristoferlund Aug 4, 2023
2187c0b
Add attestationsTxHash handling to period/PUT
kristoferlund Aug 4, 2023
4f9699f
Include txHash and signature in return
kristoferlund Aug 4, 2023
55c0c40
Update period when transaction signed
kristoferlund Aug 4, 2023
c5c7173
AttestationsDetailBox WIP
kristoferlund Aug 4, 2023
af216c6
Add startDate to period API responses
kristoferlund Aug 6, 2023
45061e6
SafeProvider and hooks
kristoferlund Aug 7, 2023
a171946
Attestation details page WIP
kristoferlund Aug 7, 2023
83be4e5
Eth error handling
kristoferlund Aug 7, 2023
4a0c946
Adding support for executing transactions
kristoferlund Aug 8, 2023
ec2cf7a
Make sure popover stays on top
kristoferlund Aug 8, 2023
8298462
Is current user owner of Safe?
kristoferlund Aug 8, 2023
dbd508a
Attestation page description
kristoferlund Aug 8, 2023
01a6c37
Styling and small fixes
kristoferlund Aug 8, 2023
e411f16
Await transaction indexing
kristoferlund Aug 8, 2023
1cb3103
Build openapi schema
kristoferlund Aug 8, 2023
82ed712
Sort period list to determine previous period
kristoferlund Aug 11, 2023
5af9c01
No need to get previous period when startDate
kristoferlund Aug 11, 2023
a90d482
Merge branch 'add/attestations' of https://github.com/commons-stack/p…
kristoferlund Aug 11, 2023
a20c544
Add switch network button
kristoferlund Aug 14, 2023
ec61dcc
List Safe owners
kristoferlund Aug 14, 2023
db7521d
Remove unused
kristoferlund Aug 14, 2023
2031772
Merge branch 'main' into add/attestations
kristoferlund Aug 23, 2023
e1391dc
Schema updates
kristoferlund Aug 23, 2023
3592001
Build openapi schema
kristoferlund Aug 23, 2023
6c052a7
Upgraded attestation components
kristoferlund Aug 23, 2023
6f4fbfb
yarn.lock
kristoferlund Aug 23, 2023
7510dab
Type name change
kristoferlund Aug 24, 2023
e8a95d1
Show disabled button when user
kristoferlund Aug 24, 2023
33b55e6
Login button polish
kristoferlund Aug 24, 2023
f9b7472
Reload transaction after signing
kristoferlund Aug 24, 2023
16f23e7
Remove unused
kristoferlund Sep 4, 2023
144d014
View on easscan.org
kristoferlund Sep 4, 2023
a8cda0a
Show attestation tab only if feature is enabled
kristoferlund Sep 4, 2023
e38e3b2
toast on safe init error
kristoferlund Sep 4, 2023
40a5b40
Always show connect button on wrong net
kristoferlund Sep 4, 2023
99c2073
Show Safe error to user
kristoferlund Sep 4, 2023
dab8390
Remove unused
kristoferlund Sep 4, 2023
959cf82
CHANGELOG
kristoferlund Sep 4, 2023
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- **Frontend/API**: Attestations! Communities can now create attestations based on Praise data using the Ethereum Attestation Service. Attest to top receivers, givers etc.
- **Frontend**: Custom reports can now be configured using an interactive form. #1131
- **Frontend**: New feature: Run custom reports from the reports page. #1050

Expand Down
10 changes: 10 additions & 0 deletions packages/api-types/out/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,11 @@ export interface components {
/** @enum {string} */
status: 'OPEN' | 'QUANTIFY' | 'CLOSED';
/** Format: date-time */
startDate: string;
/** Format: date-time */
endDate: string;
/** @example 0x46164b8581258eec4b4f44d626925953d0d7581514d9fd1335e3bd660d48e07c */
attestationsTxHash: string;
/** Format: date-time */
createdAt: string;
/** Format: date-time */
Expand Down Expand Up @@ -494,7 +498,11 @@ export interface components {
/** @enum {string} */
status: 'OPEN' | 'QUANTIFY' | 'CLOSED';
/** Format: date-time */
startDate: string;
/** Format: date-time */
endDate: string;
/** @example 0x46164b8581258eec4b4f44d626925953d0d7581514d9fd1335e3bd660d48e07c */
attestationsTxHash: string;
/** Format: date-time */
createdAt: string;
/** Format: date-time */
Expand All @@ -513,6 +521,8 @@ export interface components {
UpdatePeriodInputDto: {
/** @example June 2021 */
name?: string;
/** @example 0x46164b8581258eec4b4f44d626925953d0d7581514d9fd1335e3bd660d48e07c */
attestationsTxHash?: string;
endDate?: string;
};
UserAccountWithUserRefDto: {
Expand Down
2 changes: 1 addition & 1 deletion packages/api/openapi.json

Large diffs are not rendered by default.

11 changes: 1 addition & 10 deletions packages/api/src/auth/role-permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,7 @@ const PERMISSIONS_ADMIN = [
Permission.PeriodAssign,
];

/**
* Root users are users that are allowed to manage the system.
* In addition to the admin permissions, they can also manage communities.
*/
const PERMISSIONS_ROOT = [
...PERMISSIONS_ADMIN,
Permission.CommunitiesCreate,
Permission.CommunitiesView,
Permission.CommunitiesUpdate,
];
const PERMISSIONS_ROOT = [...PERMISSIONS_ADMIN];

/**
* API keys can be used to access the API. They can be created with different
Expand Down
13 changes: 13 additions & 0 deletions packages/api/src/community/dto/find-all-communities-query.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsOptional } from 'class-validator';
import { PaginatedQueryDto } from '../../shared/dto/pagination-query.dto';

export class FindAllCommunitiesQueryDto extends PaginatedQueryDto {
@ApiProperty({
required: false,
type: 'string',
example: 'hostname.givepraise.xyz',
})
@IsOptional()
hostname?: string;
}
14 changes: 11 additions & 3 deletions packages/api/src/periods/dto/update-period-input.dto.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { PartialType } from '@nestjs/swagger';
import { CreatePeriodInputDto } from './create-period-input.dto';
import { ApiProperty, PartialType, PickType } from '@nestjs/swagger';
import { Period } from '../schemas/periods.schema';
import { IsDateString, IsOptional } from 'class-validator';

export class UpdatePeriodInputDto extends PartialType(CreatePeriodInputDto) {}
export class UpdatePeriodInputDto extends PartialType(
PickType(Period, ['name', 'attestationsTxHash'] as const),
) {
@ApiProperty({ type: 'string', required: false })
@IsDateString()
@IsOptional()
endDate?: string;
}
12 changes: 12 additions & 0 deletions packages/api/src/periods/schemas/periods.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,25 @@ export class Period {
})
status: PeriodStatusType;

@ApiResponseProperty()
startDate?: Date;

@ApiResponseProperty()
@Prop({
required: true,
type: Date,
})
endDate: Date;

@ApiProperty({
example:
'0x46164b8581258eec4b4f44d626925953d0d7581514d9fd1335e3bd660d48e07c',
type: 'string',
})
@IsString()
@Prop({ required: false })
attestationsTxHash?: string;

@Prop({ type: Date })
@ApiResponseProperty()
createdAt: Date;
Expand Down
30 changes: 27 additions & 3 deletions packages/api/src/periods/services/periods.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export class PeriodsService {
options: PaginatedQueryDto,
): Promise<PeriodPaginatedResponseDto> {
const {
sortColumn = 'createdAt',
sortColumn = 'endDate',
sortType = 'desc',
page = 1,
limit = 100,
Expand All @@ -77,11 +77,26 @@ export class PeriodsService {
page,
limit,
sort: sortColumn && sortType ? { [sortColumn]: sortType } : undefined,
lean: true,
});

if (!periodPagination)
throw new ApiException(errorMessages.FAILED_TO_PAGINATE_PERIOD_DATA);

const periods = periodPagination.docs;

// Sort the periods by endDate in ascending order
periods.sort((a, b) => a.endDate.getTime() - b.endDate.getTime());

// Iterate through the periods and set the startDate based on the previous period's endDate
for (let i = 1; i < periods.length; i++) {
periods[i].startDate = periods[i - 1].endDate;
}

// Set the startDate for the first period, if needed
if (periods.length > 0 && !periods[0].startDate) {
periods[0].startDate = new Date('2000-01-01T00:00:00.000Z');
}
return periodPagination;
}

Expand Down Expand Up @@ -168,9 +183,9 @@ export class PeriodsService {
const period = await this.periodModel.findById(_id);
if (!period) throw new ApiException(errorMessages.PERIOD_NOT_FOUND);

const { name, endDate } = data;
const { name, endDate, attestationsTxHash } = data;

if (!name && !endDate)
if (!name && !endDate && !attestationsTxHash)
throw new ApiException(
errorMessages.UPDATE_PERIOD_NAME_OR_END_DATE_MUST_BE_SPECIFIED,
);
Expand Down Expand Up @@ -206,6 +221,14 @@ export class PeriodsService {
period.endDate = newEndDate;
}

if (attestationsTxHash) {
eventLogMessages.push(
`Updated the transaction hash of period "${period.name}" to "${attestationsTxHash}"`,
);

period.attestationsTxHash = attestationsTxHash;
}

await period.save();

logger.info(eventLogMessages.join(', '));
Expand Down Expand Up @@ -414,6 +437,7 @@ export class PeriodsService {

const periodDetails = {
...period,
startDate: previousPeriodEndDate,
numberOfPraise,
receivers,
givers,
Expand Down
4 changes: 2 additions & 2 deletions packages/api/src/shared/exceptions/error-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ export const errorMessages: { [key: string]: ErrorMessage } = {
httpStatusCode: 500,
code: 1013,
},
UPDATE_PERIOD_NAME_OR_END_DATE_MUST_BE_SPECIFIED: {
message: 'Updated name or endDate to must be specified',
UPDATE_PERIOD_SOME_PROP_MUST_BE_SPECIFIED: {
message: 'At least one property must be specified',
httpStatusCode: 400,
code: 1013,
},
Expand Down
1 change: 0 additions & 1 deletion packages/discord-bot/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ discordClient.on('interactionCreate', async (interaction): Promise<void> => {
discordClient,
interaction.guild.id
);
console.log(community);

if (community) {
await command.execute(discordClient, interaction, community.hostname);
Expand Down
4 changes: 2 additions & 2 deletions packages/discord-bot/src/utils/api-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ export type PraiseForwardInputDto =

export type Setting = components['schemas']['Setting'];

export type CommunityPaginatedResponseDto =
components['schemas']['CommunityPaginatedResponseDto'];
export type CommunityFindAllResponseDto =
components['schemas']['CommunityFindAllResponseDto'];
export type Community = components['schemas']['Community'];

export type PeriodPaginatedResponseDto =
Expand Down
9 changes: 2 additions & 7 deletions packages/discord-bot/src/utils/embeds/praiseSuccessEmbed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,6 @@ export const praiseSuccessEmbed = async (
.replace('{@receivers}', `${receivers.join(', ')}`)
.replace('{reason}', reason);

try {
const embed = new EmbedBuilder().setColor(0xe6007e).setDescription(msg);
return embed;
} catch (error) {
console.log(error);
throw error;
}
const embed = new EmbedBuilder().setColor(0xe6007e).setDescription(msg);
return embed;
};
4 changes: 2 additions & 2 deletions packages/discord-bot/src/utils/getHost.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { apiGet } from './api';
import Keyv from 'keyv';
import { Community, CommunityPaginatedResponseDto } from './api-schema';
import { Community, CommunityFindAllResponseDto } from './api-schema';
import { DiscordClient } from '../interfaces/DiscordClient';

/**
Expand All @@ -11,7 +11,7 @@ export const buildCommunityCache = async (cache: Keyv): Promise<void> => {
let currPage = 1;
let totalPages = 1;
while (currPage <= totalPages) {
const communityList = await apiGet<CommunityPaginatedResponseDto>(
const communityList = await apiGet<CommunityFindAllResponseDto>(
`/communities?page=${currPage}`
).then((res) => res.data);

Expand Down
16 changes: 6 additions & 10 deletions packages/discord-bot/src/utils/getUserAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,11 @@ export const getUserAccount = async (
.then((res) => res.data.filter((acc) => acc.platform === 'DISCORD'))
.catch(() => undefined);

try {
if (!data || !data.length) {
return await createUserAccount(user, host);
} else {
return await updateUserAccount(data[0], user, host);
}
} catch (e) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
console.log((e as any).data);
throw e;
if (!data || !data.length) {
return await createUserAccount(user, host);
} else {
return await updateUserAccount(data[0], user, host);
}
// No console.log please
// console.log((e as any).data);
};
1 change: 0 additions & 1 deletion packages/discord-bot/src/utils/givePraise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ export const givePraise = async (

await Promise.all(
praiseItems.map(async (praise) => {
console.log(praiseItems, receivers);
await sendReceiverDM(
praise._id,
receivers.filter(
Expand Down
8 changes: 5 additions & 3 deletions packages/frontend/craco.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ module.exports = {
* Possible alternatives: react-app-rewired or ejecting from CRA
*/
fallback: {
'util': require.resolve("util/"),
util: require.resolve('util/'),
stream: require.resolve('stream-browserify'),
assert: require.resolve('assert/'),
},
},
// with CRA 5 (webpack5), sourceMapLoader now complains about every third-party app that was compiled from
// typescript but doesn't have 'ts' files. This line ignores them.
// See: https://github.com/facebook/create-react-app/issues/11924
ignoreWarnings: [/to parse source map/i],
}
}
},
},
};
12 changes: 10 additions & 2 deletions packages/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
"description": "The Praise dashboard built on React/Recoil/Tailwind CSS.",
"private": true,
"engines": {
"node": ">=10.0.0"
"node": ">=16.0.0"
},
"dependencies": {
"@craco/craco": "^6.4.3",
"@duckdb/duckdb-wasm": "^1.20.0",
"@emotion/react": "^11.7.1",
"@emotion/styled": "^11.6.0",
"@endo/static-module-record": "^0.7.16",
"@ethereum-attestation-service/eas-sdk": "^1.1.0-beta.3",
"@ethersproject/providers": "^5.5.3",
"@fortawesome/fontawesome-svg-core": "^6.1.1",
"@fortawesome/free-brands-svg-icons": "^6.1.1",
Expand All @@ -23,19 +24,25 @@
"@mui/material": "^5.3.0",
"@observablehq/plot": "^0.6.5",
"@rainbow-me/rainbowkit": "^1.0.7",
"@safe-global/api-kit": "^1.3.0",
"@safe-global/protocol-kit": "^1.2.0",
"@safe-global/safe-core-sdk-types": "^2.2.0",
"@sgratzl/chartjs-chart-boxplot": "^4.1.1",
"@tailwindcss/container-queries": "^0.1.1",
"@tailwindcss/forms": "latest",
"@types/react-csv": "^1.1.2",
"@typescript-eslint/eslint-plugin": "^5.18.0",
"@typescript-eslint/parser": "^5.18.0",
"@ukstv/jazzicon-react": "^1.0.0",
"@wagmi/core": "~1.3.9",
"api-types": "0.1.0",
"assert": "^2.0.0",
"autoprefixer": "^10.4.7",
"axios": "^0.22.0",
"chart.js": "^4.2.0",
"chartjs-chart-treemap": "^2.3.0",
"craco-alias": "^3.0.1",
"csv-parse": "^5.4.0",
"date-fns": "^2.25.0",
"date-fns-tz": "^1.3.3",
"discord-markdown": "^2.5.1",
Expand All @@ -62,11 +69,12 @@
"recoil-persist": "^4.2.0",
"ses": "^0.18.7",
"source-map-explorer": "^2.5.2",
"stream-browserify": "^3.0.0",
"tailwindcss": "^3.0.24",
"use-error-boundary": "^2.0.6",
"util": "^0.12.4",
"viem": "^0.3.33",
"wagmi": "^1.0.5",
"wagmi": "~1.3.9",
"wasm-check": "^2.1.2"
},
"scripts": {
Expand Down
Binary file added packages/frontend/public/eas-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 6 additions & 2 deletions packages/frontend/src/components/auth/SignMessageButton.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useSignMessage } from 'wagmi';
import { LoaderSpinner } from '@/components/ui/LoaderSpinner';
import { Button } from '../ui/Button';
import { faPrayingHands } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

interface Props {
text: string;
Expand All @@ -26,7 +27,10 @@ const SignMessageButton = ({
});

return isLoading || isSuccess ? (
<LoaderSpinner />
<Button disabled>
<FontAwesomeIcon icon={faPrayingHands} spin className="w-4 mr-2" />
Signing in...
</Button>
) : (
<Button onClick={(): void => signMessage()}>{text}</Button>
);
Expand Down
Loading
Loading