-
Notifications
You must be signed in to change notification settings - Fork 44
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #10164 from hicommonwealth/malik.9898.pin-token-in…
…-community Added external token integration functionality in community integrations
- Loading branch information
Showing
71 changed files
with
1,873 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { type Query } from '@hicommonwealth/core'; | ||
import * as schemas from '@hicommonwealth/schemas'; | ||
import { Includeable } from 'sequelize'; | ||
import { models } from '../database'; | ||
|
||
export function GetPinnedTokens(): Query<typeof schemas.GetPinnedTokens> { | ||
return { | ||
...schemas.GetPinnedTokens, | ||
auth: [], | ||
secure: false, | ||
body: async ({ payload }) => { | ||
const { community_ids, with_chain_node } = payload; | ||
if (community_ids.length === 0) return []; | ||
const parsedIds = community_ids.split(',').filter((v) => v !== ''); | ||
if (parsedIds.length === 0) return []; | ||
|
||
const include: Includeable[] = []; | ||
if (with_chain_node) { | ||
include.push({ | ||
model: models.ChainNode, | ||
required: true, | ||
}); | ||
} | ||
|
||
return ( | ||
await models.PinnedToken.findAll({ | ||
where: { | ||
community_id: parsedIds, | ||
}, | ||
include, | ||
}) | ||
).map((t) => t.get({ plain: true })); | ||
}, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import { InvalidState, logger, type Command } from '@hicommonwealth/core'; | ||
import { commonProtocol as cp } from '@hicommonwealth/evm-protocols'; | ||
import { config } from '@hicommonwealth/model'; | ||
import * as schemas from '@hicommonwealth/schemas'; | ||
import { alchemyGetTokenPrices } from '@hicommonwealth/shared'; | ||
import { models } from '../database'; | ||
import { authRoles } from '../middleware'; | ||
import { mustExist } from '../middleware/guards'; | ||
|
||
const log = logger(import.meta); | ||
|
||
export const PinTokenErrors = { | ||
NotSupported: 'Pinned tokens only supported on Alchemy supported chains', | ||
FailedToFetchPrice: 'Failed to fetch token price', | ||
OnlyBaseSupport: 'Only Base (ETH) chain supported', | ||
LaunchpadTokenFound: (communityId: string) => | ||
`Community ${communityId} has an attached launchpad token`, | ||
}; | ||
|
||
export function PinToken(): Command<typeof schemas.PinToken> { | ||
return { | ||
...schemas.PinToken, | ||
auth: [authRoles('admin')], | ||
body: async ({ payload }) => { | ||
const { community_id, contract_address, chain_node_id } = payload; | ||
|
||
const chainNode = await models.ChainNode.scope('withPrivateData').findOne( | ||
{ | ||
where: { | ||
id: chain_node_id, | ||
}, | ||
}, | ||
); | ||
mustExist('ChainNode', chainNode); | ||
|
||
if (chainNode.eth_chain_id !== cp.ValidChains.Base) | ||
throw new InvalidState(PinTokenErrors.OnlyBaseSupport); | ||
|
||
const community = await models.Community.findOne({ | ||
where: { | ||
id: community_id, | ||
}, | ||
}); | ||
mustExist('Community', community); | ||
|
||
if (community.namespace) { | ||
const launchpadToken = await models.LaunchpadToken.findOne({ | ||
where: { | ||
namespace: community.namespace, | ||
}, | ||
}); | ||
|
||
if (launchpadToken) | ||
throw new InvalidState( | ||
PinTokenErrors.LaunchpadTokenFound(community_id), | ||
); | ||
} | ||
|
||
if ( | ||
!chainNode.url.includes('alchemy') || | ||
!chainNode.private_url?.includes('alchemy') || | ||
!chainNode.alchemy_metadata?.price_api_supported | ||
) { | ||
throw new InvalidState(PinTokenErrors.NotSupported); | ||
} | ||
|
||
let price: Awaited<ReturnType<typeof alchemyGetTokenPrices>> | undefined; | ||
try { | ||
price = await alchemyGetTokenPrices({ | ||
alchemyApiKey: config.ALCHEMY.APP_KEYS.PRIVATE, | ||
tokenSources: [ | ||
{ | ||
contractAddress: contract_address, | ||
alchemyNetworkId: chainNode.alchemy_metadata.network_id, | ||
}, | ||
], | ||
}); | ||
} catch (e: unknown) { | ||
if (e instanceof Error) | ||
log.error(e.message, e, { | ||
contractAddress: contract_address, | ||
alchemyNetworkId: chainNode.alchemy_metadata.network_id, | ||
}); | ||
else { | ||
log.error(JSON.stringify(e), undefined, { | ||
contractAddress: contract_address, | ||
alchemyNetworkId: chainNode.alchemy_metadata.network_id, | ||
}); | ||
} | ||
} | ||
|
||
if ( | ||
!Array.isArray(price?.data) || | ||
price.data.length !== 1 || | ||
price.data[0].error | ||
) { | ||
log.error(PinTokenErrors.FailedToFetchPrice, undefined, { | ||
price, | ||
}); | ||
throw new InvalidState(PinTokenErrors.FailedToFetchPrice); | ||
} | ||
|
||
return await models.PinnedToken.create({ | ||
community_id, | ||
chain_node_id, | ||
contract_address, | ||
}); | ||
}, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { InvalidState, type Command } from '@hicommonwealth/core'; | ||
import * as schemas from '@hicommonwealth/schemas'; | ||
import { models } from '../database'; | ||
import { authRoles } from '../middleware'; | ||
|
||
export const UnpinTokenErrors = { | ||
NotFound: 'Token not found', | ||
}; | ||
|
||
export function UnpinToken(): Command<typeof schemas.UnpinToken> { | ||
return { | ||
...schemas.UnpinToken, | ||
auth: [authRoles('admin')], | ||
body: async ({ payload }) => { | ||
const { community_id } = payload; | ||
const pinnedToken = await models.PinnedToken.findOne({ | ||
where: { | ||
community_id, | ||
}, | ||
}); | ||
|
||
if (!pinnedToken) throw new InvalidState(UnpinTokenErrors.NotFound); | ||
|
||
await pinnedToken.destroy(); | ||
return {}; | ||
}, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { PinnedToken } from '@hicommonwealth/schemas'; | ||
import Sequelize from 'sequelize'; | ||
import { z } from 'zod'; | ||
import type { ModelInstance } from './types'; | ||
|
||
export type PinnedTokenAttributes = z.infer<typeof PinnedToken>; | ||
|
||
export type PinnedTokenInstance = ModelInstance<PinnedTokenAttributes>; | ||
|
||
export default ( | ||
sequelize: Sequelize.Sequelize, | ||
): Sequelize.ModelStatic<PinnedTokenInstance> => | ||
sequelize.define<PinnedTokenInstance>( | ||
'PinnedToken', | ||
{ | ||
community_id: { | ||
type: Sequelize.STRING, | ||
primaryKey: true, | ||
}, | ||
contract_address: { | ||
type: Sequelize.STRING, | ||
allowNull: false, | ||
}, | ||
chain_node_id: { | ||
type: Sequelize.INTEGER, | ||
allowNull: false, | ||
}, | ||
created_at: { | ||
type: Sequelize.DATE, | ||
allowNull: false, | ||
}, | ||
updated_at: { | ||
type: Sequelize.DATE, | ||
allowNull: false, | ||
}, | ||
}, | ||
{ | ||
timestamps: true, | ||
createdAt: 'created_at', | ||
updatedAt: 'updated_at', | ||
underscored: true, | ||
tableName: 'PinnedTokens', | ||
}, | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.