From 2e120ea76dd73718b7b395071972aff45178940a Mon Sep 17 00:00:00 2001 From: ianrowan Date: Wed, 15 Jan 2025 15:15:12 -0600 Subject: [PATCH 1/2] implementation of bot commands --- .../src/abis/MomBotCandidateManagerAbi.ts | 131 ++++++++++++++++++ .../src/abis/MomBotLaunchpadAbi.ts | 63 +++++++++ libs/evm-protocols/src/abis/index.ts | 2 + .../src/common-protocol/chainConfig.ts | 8 ++ .../src/bot/AddMomBotCandidate.command.ts | 50 +++++++ libs/model/src/bot/LaunchMomToken.command.ts | 60 ++++++++ .../src/commands/bot-contest.schemas.ts | 20 +++ 7 files changed, 334 insertions(+) create mode 100644 libs/evm-protocols/src/abis/MomBotCandidateManagerAbi.ts create mode 100644 libs/evm-protocols/src/abis/MomBotLaunchpadAbi.ts create mode 100644 libs/model/src/bot/AddMomBotCandidate.command.ts create mode 100644 libs/model/src/bot/LaunchMomToken.command.ts diff --git a/libs/evm-protocols/src/abis/MomBotCandidateManagerAbi.ts b/libs/evm-protocols/src/abis/MomBotCandidateManagerAbi.ts new file mode 100644 index 00000000000..be38a69a28b --- /dev/null +++ b/libs/evm-protocols/src/abis/MomBotCandidateManagerAbi.ts @@ -0,0 +1,131 @@ +export const MomBotCandidateManager = [ + { + type: 'constructor', + inputs: [ + { name: '_registerApproved', type: 'address', internalType: 'address' }, + { name: '_owner', type: 'address', internalType: 'address' }, + { name: '_claimPercent', type: 'uint256', internalType: 'uint256' }, + ], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'MAX_CLAIMERS', + inputs: [], + outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'REGISTER_APPROVED', + inputs: [], + outputs: [{ name: '', type: 'address', internalType: 'address' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'addCandidate', + inputs: [ + { name: 'token', type: 'address', internalType: 'address' }, + { name: 'claimer', type: 'address', internalType: 'address' }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'allClaimers', + inputs: [ + { name: '', type: 'address', internalType: 'address' }, + { name: '', type: 'uint256', internalType: 'uint256' }, + ], + outputs: [{ name: '', type: 'address', internalType: 'address' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'claimPercent', + inputs: [], + outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'claimTokens', + inputs: [ + { name: 'token', type: 'address', internalType: 'address' }, + { name: 'claimer', type: 'address', internalType: 'address' }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'owner', + inputs: [], + outputs: [{ name: '', type: 'address', internalType: 'address' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'updateClaimPercent', + inputs: [ + { name: '_claimPercent', type: 'uint256', internalType: 'uint256' }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'updateRegisterApproved', + inputs: [ + { name: '_registerApproved', type: 'address', internalType: 'address' }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'event', + name: 'CandidateAdded', + inputs: [ + { + name: 'token', + type: 'address', + indexed: false, + internalType: 'address', + }, + { + name: 'candidate', + type: 'address', + indexed: false, + internalType: 'address', + }, + ], + anonymous: false, + }, + { + type: 'event', + name: 'TokensClaimed', + inputs: [ + { + name: 'token', + type: 'address', + indexed: false, + internalType: 'address', + }, + { + name: 'claimer', + type: 'address', + indexed: false, + internalType: 'address', + }, + { + name: 'amount', + type: 'uint256', + indexed: false, + internalType: 'uint256', + }, + ], + anonymous: false, + }, +]; diff --git a/libs/evm-protocols/src/abis/MomBotLaunchpadAbi.ts b/libs/evm-protocols/src/abis/MomBotLaunchpadAbi.ts new file mode 100644 index 00000000000..f0b0df350f5 --- /dev/null +++ b/libs/evm-protocols/src/abis/MomBotLaunchpadAbi.ts @@ -0,0 +1,63 @@ +export const MomBotLaunchpadAbi = [ + { + type: 'constructor', + inputs: [ + { name: '_paymentToken', type: 'address', internalType: 'address' }, + { name: '_protocolVault', type: 'address', internalType: 'address' }, + { name: '_protocolFee', type: 'uint256', internalType: 'uint256' }, + { name: '_curveManager', type: 'address', internalType: 'address' }, + { name: '_curveActionHook', type: 'address', internalType: 'address' }, + { name: '_candidateManager', type: 'address', internalType: 'address' }, + { name: '_curveId', type: 'uint256', internalType: 'uint256' }, + { name: '_reserveRatio', type: 'uint32', internalType: 'uint32' }, + ], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'CURVE_ID', + inputs: [], + outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'RESERVE_RATIO', + inputs: [], + outputs: [{ name: '', type: 'uint32', internalType: 'uint32' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'bondingCurve', + inputs: [], + outputs: [ + { name: '', type: 'address', internalType: 'contract ERC20BondingCurve' }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'candidateManager', + inputs: [], + outputs: [{ name: '', type: 'address', internalType: 'address' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'launchToken', + inputs: [ + { name: 'name', type: 'string', internalType: 'string' }, + { name: 'symbol', type: 'string', internalType: 'string' }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'paymentToken', + inputs: [], + outputs: [{ name: '', type: 'address', internalType: 'address' }], + stateMutability: 'view', + }, +]; diff --git a/libs/evm-protocols/src/abis/index.ts b/libs/evm-protocols/src/abis/index.ts index 1d94ebdd2b5..90f545e3837 100644 --- a/libs/evm-protocols/src/abis/index.ts +++ b/libs/evm-protocols/src/abis/index.ts @@ -1,3 +1,5 @@ +export * from './MomBotCandidateManagerAbi'; +export * from './MomBotLaunchpadAbi'; export * from './communityStakesAbi'; export * from './contestAbi'; export * from './erc20Abi'; diff --git a/libs/evm-protocols/src/common-protocol/chainConfig.ts b/libs/evm-protocols/src/common-protocol/chainConfig.ts index 4b32cf9a506..76c633f0aca 100644 --- a/libs/evm-protocols/src/common-protocol/chainConfig.ts +++ b/libs/evm-protocols/src/common-protocol/chainConfig.ts @@ -1,3 +1,5 @@ +import { ZERO_ADDRESS } from '@hicommonwealth/shared'; + // Chains with deployed namespace factories. As new chains are enabled, add here. export enum ValidChains { Base = 8453, @@ -27,6 +29,8 @@ export const CONTEST_VOTER_SHARE = 0; export const CONTEST_FEE_SHARE = 100; export const CREATE_CONTEST_TOPIC = '0x990f533044dbc89b838acde9cd2c72c400999871cf8f792d731edcae15ead693'; +export const CREATE_TOKEN_TOPIC = + '0xb9c0b7cfa02f69a492d2df0a4555a03757f4e17d24590bc9dbb9d9e24fefcd45'; type factoryContractsType = { [key in ValidChains]: { @@ -36,6 +40,8 @@ type factoryContractsType = { lpBondingCurve?: string; tokenCommunityManager?: string; referralFeeManager?: string; + momBotLaunchpad?: string; + momBotCandiateManager?: string; chainId: number; }; }; @@ -55,6 +61,8 @@ export const factoryContracts = { lpBondingCurve: '0x2ECc0af0e4794F0Ab4797549a5a8cf97688D7D21', tokenCommunityManager: '0xC8fe1F23AbC4Eb55f4aa9E52dAFa3761111CF03a', referralFeeManager: '0xdc07fEaf01666B7f5dED2F59D895543Ed3FAE1cA', + momBotLaunchpad: ZERO_ADDRESS, + momBotCandiateManager: ZERO_ADDRESS, chainId: 84532, }, [ValidChains.Blast]: { diff --git a/libs/model/src/bot/AddMomBotCandidate.command.ts b/libs/model/src/bot/AddMomBotCandidate.command.ts new file mode 100644 index 00000000000..1cba7caad8f --- /dev/null +++ b/libs/model/src/bot/AddMomBotCandidate.command.ts @@ -0,0 +1,50 @@ +import { ServerError, type Command } from '@hicommonwealth/core'; +import { + MomBotCandidateManager, + commonProtocol as cp, +} from '@hicommonwealth/evm-protocols'; +import { config } from '@hicommonwealth/model'; +import * as schemas from '@hicommonwealth/schemas'; +import { models } from '../database'; + +export function AddMomBotCandidate(): Command< + typeof schemas.AddMomBotCandidate +> { + return { + ...schemas.AddMomBotCandidate, + auth: [], + body: async ({ payload }) => { + const { token_address, candidate_address, chain_id } = payload; + + const chainNode = await models.ChainNode.findOne({ + where: { eth_chain_id: chain_id }, + attributes: ['eth_chain_id', 'url', 'private_url'], + }); + + if (!chainNode) { + throw new ServerError('Chain Node not found'); + } + + if (!config.WEB3.CONTEST_BOT_PRIVATE_KEY) + throw new ServerError('Contest bot private key not set!'); + + const web3 = cp.createPrivateEvmClient({ + rpc: chainNode.private_url!, + privateKey: config.WEB3.CONTEST_BOT_PRIVATE_KEY, + }); + + const candidateManager = new web3.eth.Contract( + MomBotCandidateManager, + cp.factoryContracts[ + chain_id as cp.ValidChains.SepoliaBase + ].momBotCandiateManager, + ); + + const receipt = await candidateManager.methods + .addCandidate(token_address, candidate_address) + .send({ from: web3.eth.defaultAccount }); + + return receipt; + }, + }; +} diff --git a/libs/model/src/bot/LaunchMomToken.command.ts b/libs/model/src/bot/LaunchMomToken.command.ts new file mode 100644 index 00000000000..c1fb47868af --- /dev/null +++ b/libs/model/src/bot/LaunchMomToken.command.ts @@ -0,0 +1,60 @@ +import { ServerError, type Command } from '@hicommonwealth/core'; +import { + MomBotLaunchpadAbi, + commonProtocol as cp, +} from '@hicommonwealth/evm-protocols'; +import { config } from '@hicommonwealth/model'; +import * as schemas from '@hicommonwealth/schemas'; +import { + CREATE_TOKEN_TOPIC, + decodeParameters, +} from 'evm-protocols/src/common-protocol'; +import { models } from '../database'; + +export function LaunchMomToken(): Command { + return { + ...schemas.LaunchMomToken, + auth: [], + body: async ({ payload }) => { + const { name, symbol, chain_id, icon_url, description } = payload; + + const chainNode = await models.ChainNode.findOne({ + where: { eth_chain_id: chain_id }, + attributes: ['eth_chain_id', 'url', 'private_url'], + }); + + if (!chainNode) { + throw new ServerError('Chain Node not found'); + } + + if (!config.WEB3.CONTEST_BOT_PRIVATE_KEY) + throw new ServerError('Contest bot private key not set!'); + + const web3 = cp.createPrivateEvmClient({ + rpc: chainNode.private_url!, + privateKey: config.WEB3.CONTEST_BOT_PRIVATE_KEY, + }); + const launchpadContract = new web3.eth.Contract( + MomBotLaunchpadAbi, + cp.factoryContracts[ + chain_id as cp.ValidChains.SepoliaBase + ].momBotLaunchpad, + ); + const receipt = await launchpadContract.methods + .launchToken(name, symbol) + .send({ from: web3.eth.defaultAccount }); + + const eventLog = receipt.logs.find( + (log) => log.topics![0] == CREATE_TOKEN_TOPIC, + ); + if (!eventLog || !eventLog.data) throw new Error('No event data'); + + const { 0: address } = decodeParameters({ + abiInput: ['address', 'uint256', 'uint256'], + data: eventLog.data.toString(), + }); + + return address; + }, + }; +} diff --git a/libs/schemas/src/commands/bot-contest.schemas.ts b/libs/schemas/src/commands/bot-contest.schemas.ts index 3294b8733bd..1f03238f3e0 100644 --- a/libs/schemas/src/commands/bot-contest.schemas.ts +++ b/libs/schemas/src/commands/bot-contest.schemas.ts @@ -9,3 +9,23 @@ export const CreateBotContest = { }), output: z.string().describe('New contest address'), }; + +export const LaunchMomToken = { + input: z.object({ + name: z.string().describe('The name of the token'), + symbol: z.string().describe('The symbol of the token'), + chain_id: z.number().describe('The chain id to launch token on'), + icon_url: z.string().describe('The icon url of the token'), + description: z.string().describe('The description of the token'), + }), + output: z.string().describe('New token address'), +}; + +export const AddMomBotCandidate = { + input: z.object({ + token_address: z.string().describe('The address of the contest'), + candidate_address: z.string().describe('The address of the candidate'), + chain_id: z.number().describe('The chain id to add candidate to'), + }), + output: z.object({}), +}; From 797cf41c50be8c7dcecfc98ac51fbb2cf60ae1c2 Mon Sep 17 00:00:00 2001 From: ianrowan Date: Thu, 16 Jan 2025 14:51:41 -0600 Subject: [PATCH 2/2] type fix --- libs/model/src/bot/LaunchMomToken.command.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/model/src/bot/LaunchMomToken.command.ts b/libs/model/src/bot/LaunchMomToken.command.ts index c1fb47868af..f3df791c823 100644 --- a/libs/model/src/bot/LaunchMomToken.command.ts +++ b/libs/model/src/bot/LaunchMomToken.command.ts @@ -54,7 +54,7 @@ export function LaunchMomToken(): Command { data: eventLog.data.toString(), }); - return address; + return address as string; }, }; }