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:
Hammad Jutt
2021-06-09 13:01:04 -06:00
committed by Alec LaLonde
parent ba0be9fb09
commit 63eeca916f
13 changed files with 61 additions and 104 deletions

View File

@@ -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);

View File

@@ -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
}
}

View File

@@ -0,0 +1,4 @@
DISCORD_BOT_TOKEN=
DISCORD_BOT_CLIENT_ID=
DISCORD_BOT_CLIENT_SECRET=
GITHUB_API_TOKEN=

View File

@@ -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",

View File

@@ -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;
}

View File

@@ -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"
}
}

View File

@@ -5,3 +5,4 @@ export * as DiscordUtil from './discordHelpers';
export * as numbers from './numbers';
export * from './promiseHelpers';
export * from './rankHelpers';
export * from './sourceCredHelpers';

View 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;
};