mirror of
https://github.com/MetaFam/TheGame.git
synced 2026-01-24 13:47:57 -05:00
Allow users to force-update their linked ETH address
Removed sc_identity_id from Player table because it enforces ETH addresses to be used as the single source of truth for users and prevents conflicts when users update their ETH address in the ledger. sc_identity_id is not needing anywhere in our backend / data model.
This commit is contained in:
committed by
Alec LaLonde
parent
ba0be9fb09
commit
63eeca916f
@@ -83,6 +83,12 @@ yarn backend:dev
|
||||
yarn web:dev
|
||||
```
|
||||
|
||||
### Run Discord Bot
|
||||
|
||||
```shell script
|
||||
yarn discord-bot dev
|
||||
```
|
||||
|
||||
### Tooling
|
||||
|
||||
Start Hasura console
|
||||
|
||||
@@ -310,9 +310,8 @@
|
||||
- player_type_id
|
||||
- rank
|
||||
- role
|
||||
- sc_identity_id
|
||||
- total_xp
|
||||
- timezone
|
||||
- total_xp
|
||||
- username
|
||||
filter: {}
|
||||
allow_aggregations: true
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE "public"."player" ADD COLUMN "sc_identity_id" text;
|
||||
ALTER TABLE "public"."player" ALTER COLUMN "sc_identity_id" DROP NOT NULL;
|
||||
ALTER TABLE "public"."player" ADD CONSTRAINT Player_scIdentityId_key UNIQUE (sc_identity_id);
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE "public"."player" DROP COLUMN "sc_identity_id" CASCADE;
|
||||
@@ -1,4 +1,8 @@
|
||||
import { Constants, isNotNullOrUndefined } from '@metafam/utils';
|
||||
import {
|
||||
Constants,
|
||||
getLatestEthAddress,
|
||||
isNotNullOrUndefined,
|
||||
} from '@metafam/utils';
|
||||
import bluebird from 'bluebird';
|
||||
import { Request, Response } from 'express';
|
||||
import fetch from 'node-fetch';
|
||||
@@ -7,9 +11,6 @@ import { SCAccountsData, SCAlias, sourcecred as sc } from 'sourcecred';
|
||||
import {
|
||||
AccountType_Enum,
|
||||
Player_Account_Constraint,
|
||||
Player_Constraint,
|
||||
Player_Insert_Input,
|
||||
Player_Update_Column,
|
||||
} from '../../../lib/autogen/hasura-sdk';
|
||||
import { client } from '../../../lib/hasuraClient';
|
||||
import { computeRank } from '../../../lib/rankHelpers';
|
||||
@@ -73,10 +74,7 @@ export const migrateSourceCredAccounts = async (
|
||||
const discordId = linkedAccounts.find(({ type }) => type === 'DISCORD')
|
||||
?.identifier;
|
||||
|
||||
const ethAddress = a.account.identity.aliases.find((alias) => {
|
||||
const parts = sc.core.graph.NodeAddress.toParts(alias.address);
|
||||
return parts.indexOf('ethereum') > 0;
|
||||
})?.description;
|
||||
const ethAddress = getLatestEthAddress(a.account.identity);
|
||||
|
||||
if (!ethAddress) return null;
|
||||
|
||||
@@ -148,32 +146,11 @@ export const migrateSourceCredAccounts = async (
|
||||
},
|
||||
{ concurrency: 10 },
|
||||
);
|
||||
const usersToInsert: Player_Insert_Input[] = result
|
||||
.filter(isNotNullOrUndefined)
|
||||
.map((player) => ({
|
||||
username: player.ethereum_address,
|
||||
ethereum_address: player.ethereum_address,
|
||||
sc_identity_id: player.scIdentityId,
|
||||
rank: player.rank,
|
||||
total_xp: player.totalXp,
|
||||
}));
|
||||
const usersSkipped = result.filter(isNotNullOrUndefined);
|
||||
|
||||
const resultInsert = await client.UpsertPlayer({
|
||||
objects: usersToInsert,
|
||||
onConflict: {
|
||||
constraint: Player_Constraint.PlayerEthereumAddressUniqueKey,
|
||||
update_columns: [
|
||||
Player_Update_Column.ScIdentityId,
|
||||
Player_Update_Column.Username,
|
||||
Player_Update_Column.TotalXp,
|
||||
Player_Update_Column.Rank,
|
||||
],
|
||||
},
|
||||
});
|
||||
res.json({
|
||||
resultInsert,
|
||||
numUpdated: accountList.length - usersToInsert.length,
|
||||
numInserted: usersToInsert.length,
|
||||
numSkipped: usersSkipped.length,
|
||||
numUpdated: accountList.length - usersSkipped.length,
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn('Error migrating players/accounts', e.message);
|
||||
|
||||
@@ -29,60 +29,17 @@ export const UpsertAccount = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export const UpsertPlayer = gql`
|
||||
mutation UpsertPlayer(
|
||||
$objects: [player_insert_input!]!
|
||||
$onConflict: player_on_conflict
|
||||
) {
|
||||
insert_player(on_conflict: $onConflict, objects: $objects) {
|
||||
affected_rows
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const DeleteDuplicatePlayers = gql`
|
||||
mutation DeleteDuplicatePlayers($scIds: [String!] = "") {
|
||||
delete_player_account(
|
||||
where: { Player: { sc_identity_id: { _in: $scIds } } }
|
||||
) {
|
||||
affected_rows
|
||||
}
|
||||
delete_player(where: { sc_identity_id: { _in: $scIds } }) {
|
||||
affected_rows
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const UpdatePlayer = gql`
|
||||
mutation UpdatePlayer(
|
||||
$ethAddress: String
|
||||
$identityId: String
|
||||
$username: String
|
||||
$rank: PlayerRank_enum
|
||||
$totalXp: numeric
|
||||
$discordId: String
|
||||
) {
|
||||
update_player(
|
||||
where: {
|
||||
_or: [
|
||||
{
|
||||
ethereum_address: { _eq: $ethAddress }
|
||||
sc_identity_id: { _eq: $identityId }
|
||||
}
|
||||
{
|
||||
ethereum_address: { _eq: $ethAddress }
|
||||
sc_identity_id: { _is_null: true }
|
||||
}
|
||||
{
|
||||
ethereum_address: { _eq: $ethAddress }
|
||||
username: { _eq: $username }
|
||||
}
|
||||
{ sc_identity_id: { _eq: $identityId } }
|
||||
{ discord_id: { _eq: $discordId } }
|
||||
]
|
||||
}
|
||||
where: { ethereum_address: { _eq: $ethAddress } }
|
||||
_set: {
|
||||
ethereum_address: $ethAddress
|
||||
sc_identity_id: $identityId
|
||||
rank: $rank
|
||||
total_xp: $totalXp
|
||||
discord_id: $discordId
|
||||
@@ -92,7 +49,6 @@ export const UpdatePlayer = gql`
|
||||
returning {
|
||||
id
|
||||
ethereum_address
|
||||
sc_identity_id
|
||||
username
|
||||
}
|
||||
}
|
||||
|
||||
4
packages/discord-bot/.env.sample
Normal file
4
packages/discord-bot/.env.sample
Normal file
@@ -0,0 +1,4 @@
|
||||
DISCORD_BOT_TOKEN=
|
||||
DISCORD_BOT_CLIENT_ID=
|
||||
DISCORD_BOT_CLIENT_SECRET=
|
||||
GITHUB_API_TOKEN=
|
||||
@@ -21,7 +21,7 @@
|
||||
"dependencies": {
|
||||
"@types/express": "4.17.11",
|
||||
"@types/node-fetch": "2.5.10",
|
||||
"@metafam/utils": "^1.0.0",
|
||||
"@metafam/utils": "1.0.0",
|
||||
"@typeit/discord": "4.0.10",
|
||||
"discord.js": "12.5.3",
|
||||
"dotenv": "9.0.2",
|
||||
|
||||
@@ -5,9 +5,14 @@ import { loadSourceCredLedger, manager } from '../../sourcecred';
|
||||
|
||||
const addressUtils = sc.plugins.ethereum.utils.address;
|
||||
|
||||
type SetEthAddressArgs = {
|
||||
ethAddress: string;
|
||||
force: string;
|
||||
};
|
||||
|
||||
export abstract class SetEthAddress {
|
||||
@Command('!setAddress :ethAddress')
|
||||
async setAddress(message: CommandMessage) {
|
||||
@Command('!setAddress :ethAddress :force')
|
||||
async setAddress(message: CommandMessage<SetEthAddressArgs>) {
|
||||
const res = await loadSourceCredLedger();
|
||||
|
||||
if (res.error) {
|
||||
@@ -47,14 +52,21 @@ export abstract class SetEthAddress {
|
||||
|
||||
const account = manager.ledger.account(baseIdentityId);
|
||||
|
||||
const existing = account.identity.aliases.find((alias) => {
|
||||
const existingEthAliases = account.identity.aliases.filter((alias) => {
|
||||
const parts = sc.core.graph.NodeAddress.toParts(alias.address);
|
||||
return parts.indexOf('ethereum') > 0;
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
const latestEthAlias = existingEthAliases[existingEthAliases.length - 1];
|
||||
|
||||
const shouldForceUpdate = message.args.force === 'force';
|
||||
|
||||
if (latestEthAlias && !shouldForceUpdate) {
|
||||
await message.reply(
|
||||
`You already have linked the following ETH Address: \`${existing.description}\`.`,
|
||||
`You already have linked the following ETH Address: \`${latestEthAlias.description}\`. Are you sure you want to update it? Warning: This cannot be undone and you will have to recreate your MyMeta profile!
|
||||
|
||||
To force update your address, type \`!setAddress ${ethAddress} force\`.
|
||||
`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"bignumber.js": "9.0.1",
|
||||
"ethers": "5.3.0",
|
||||
"js-base64": "3.6.1",
|
||||
"uuid": "8.3.2"
|
||||
"uuid": "8.3.2",
|
||||
"sourcecred": "0.9.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,3 +5,4 @@ export * as DiscordUtil from './discordHelpers';
|
||||
export * as numbers from './numbers';
|
||||
export * from './promiseHelpers';
|
||||
export * from './rankHelpers';
|
||||
export * from './sourceCredHelpers';
|
||||
|
||||
15
packages/utils/src/sourceCredHelpers.ts
Normal file
15
packages/utils/src/sourceCredHelpers.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { ethers } from 'ethers';
|
||||
import { SCIdentity, sourcecred as sc } from 'sourcecred';
|
||||
|
||||
export const getLatestEthAddress = (identity: SCIdentity): string | null => {
|
||||
const ethAddress = identity.aliases.find((alias) => {
|
||||
const parts = sc.core.graph.NodeAddress.toParts(alias.address);
|
||||
return parts.indexOf('ethereum') > 0;
|
||||
})?.description;
|
||||
|
||||
if (ethAddress && ethers.utils.isAddress(ethAddress)) {
|
||||
return ethAddress.toLowerCase();
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
@@ -3567,7 +3567,6 @@ type player {
|
||||
): quest_aggregate!
|
||||
rank: PlayerRank_enum
|
||||
role: String
|
||||
sc_identity_id: String
|
||||
timezone: String
|
||||
|
||||
"""Remote relationship field"""
|
||||
@@ -3847,7 +3846,6 @@ input player_bool_exp {
|
||||
quests: quest_bool_exp
|
||||
rank: PlayerRank_enum_comparison_exp
|
||||
role: String_comparison_exp
|
||||
sc_identity_id: String_comparison_exp
|
||||
timezone: String_comparison_exp
|
||||
total_xp: numeric_comparison_exp
|
||||
updated_at: timestamptz_comparison_exp
|
||||
@@ -3864,9 +3862,6 @@ enum player_constraint {
|
||||
"""unique or primary key constraint"""
|
||||
Player_pkey
|
||||
|
||||
"""unique or primary key constraint"""
|
||||
Player_scIdentityId_key
|
||||
|
||||
"""unique or primary key constraint"""
|
||||
Player_username_unique_key
|
||||
|
||||
@@ -3904,7 +3899,6 @@ input player_insert_input {
|
||||
quests: quest_arr_rel_insert_input
|
||||
rank: PlayerRank_enum
|
||||
role: String
|
||||
sc_identity_id: String
|
||||
timezone: String
|
||||
total_xp: numeric
|
||||
updated_at: timestamptz
|
||||
@@ -3921,7 +3915,6 @@ type player_max_fields {
|
||||
id: uuid
|
||||
player_type_id: Int
|
||||
role: String
|
||||
sc_identity_id: String
|
||||
timezone: String
|
||||
total_xp: numeric
|
||||
updated_at: timestamptz
|
||||
@@ -3940,7 +3933,6 @@ input player_max_order_by {
|
||||
id: order_by
|
||||
player_type_id: order_by
|
||||
role: order_by
|
||||
sc_identity_id: order_by
|
||||
timezone: order_by
|
||||
total_xp: order_by
|
||||
updated_at: order_by
|
||||
@@ -3957,7 +3949,6 @@ type player_min_fields {
|
||||
id: uuid
|
||||
player_type_id: Int
|
||||
role: String
|
||||
sc_identity_id: String
|
||||
timezone: String
|
||||
total_xp: numeric
|
||||
updated_at: timestamptz
|
||||
@@ -3976,7 +3967,6 @@ input player_min_order_by {
|
||||
id: order_by
|
||||
player_type_id: order_by
|
||||
role: order_by
|
||||
sc_identity_id: order_by
|
||||
timezone: order_by
|
||||
total_xp: order_by
|
||||
updated_at: order_by
|
||||
@@ -4031,7 +4021,6 @@ input player_order_by {
|
||||
quests_aggregate: quest_aggregate_order_by
|
||||
rank: order_by
|
||||
role: order_by
|
||||
sc_identity_id: order_by
|
||||
timezone: order_by
|
||||
total_xp: order_by
|
||||
updated_at: order_by
|
||||
@@ -4076,9 +4065,6 @@ enum player_select_column {
|
||||
"""column name"""
|
||||
role
|
||||
|
||||
"""column name"""
|
||||
sc_identity_id
|
||||
|
||||
"""column name"""
|
||||
timezone
|
||||
|
||||
@@ -4105,7 +4091,6 @@ input player_set_input {
|
||||
player_type_id: Int
|
||||
rank: PlayerRank_enum
|
||||
role: String
|
||||
sc_identity_id: String
|
||||
timezone: String
|
||||
total_xp: numeric
|
||||
updated_at: timestamptz
|
||||
@@ -4713,9 +4698,6 @@ enum player_update_column {
|
||||
"""column name"""
|
||||
role
|
||||
|
||||
"""column name"""
|
||||
sc_identity_id
|
||||
|
||||
"""column name"""
|
||||
timezone
|
||||
|
||||
|
||||
Reference in New Issue
Block a user