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

[NayNay] File Restructure: Faucet #225

Merged
merged 9 commits into from
Sep 24, 2024
18 changes: 12 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,21 @@ Version header format: `[version] Name - year-month-day (entropy-core compatibil
- new: 'src/flows/user-program-management/view.ts' - service file for pure functions of viewing user programs
- new: 'src/flows/user-program-management/helpers/utils.ts' - utility helper file for user program management specific methods
- new: './src/flows/user-program-management/remove.ts' - service file for removing user program pure function
- new: './src/common/base-commands.ts' - base abstract class for new command classes
- new: './src/common/entropy-base.ts' - base abstract class for new command classes
- new: './src/balance' - new file structure for our CLI/TUI flows
- new: './src/balance/command.ts' - main entry file for balance command for tui/cli
- new: './src/balance/main.ts' - main entry file for balance command for tui/cli
- new: './src/balance/utils.ts' - utilities and helper methods for all things balance
- new: './src/transfer' - new file structure for our CLI/TUI flows
- new: './src/transfer/command.ts' - main entry file for transfer command for tui/cli
- new: './src/transfer/main.ts' - main entry file for transfer command for tui/cli
- new: './src/transfer/utils.ts' - utilities and helper methods for all things transfer
- new: './src/accounts' - new file structure for our CLI/TUI flows
- new: './src/accounts/command.ts' - main entry file for accounts command for tui/cli
- new: './src/accounts/utils.ts' - utilities and helper methods for all things accounts
- new: './src/account' - new file structure for our CLI/TUI flows
- new: './src/account/main.ts' - main entry file for accounts command for tui/cli
- new: './src/account/utils.ts' - utilities and helper methods for all things accounts
- new: './src/faucet' - new file structure for our CLI/TUI flows
- new: './src/faucet/main.ts' - main entry file for faucet methods used for command and interaction files
- new: './src/faucet/utils.ts' - utilities and helper methods for all things faucet
- new: './src/faucet/interaction.ts' - main entrypoint for TUI flows
- new: './src/faucet/command.ts' - main entrypoint for CLI flows

### Changed

Expand All @@ -48,6 +53,7 @@ Version header format: `[version] Name - year-month-day (entropy-core compatibil
- removed flows/entropyTransfer/*.ts directory with file restructure
- removed flows/manage-accounts/*/*.ts directory with file restructure
- removed flows/register/*.ts directory with file restructure
- removed flow/entropyFaucet/*.ts directory with file restructure


### Broke
Expand Down
3 changes: 2 additions & 1 deletion src/balance/command.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Command } from "commander";
import Entropy from "@entropyxyz/sdk";
import { cliWrite, endpointOption, loadEntropy, passwordOption } from "src/common/utils-cli";
import { EntropyBalance } from "./main";

Expand All @@ -10,7 +11,7 @@ export function entropyBalanceCommand () {
.addOption(passwordOption())
.addOption(endpointOption())
.action(async (address, opts) => {
const entropy = await loadEntropy(address, opts.endpoint)
const entropy: Entropy = await loadEntropy(address, opts.endpoint)
const BalanceService = new EntropyBalance(entropy, opts.endpoint)
const balance = await BalanceService.getBalance(address)
cliWrite(`${balance.toLocaleString('en-US')} BITS`)
Expand Down
2 changes: 2 additions & 0 deletions src/common/entropy-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import { EntropyLogger } from "./logger";
export abstract class EntropyBase {
protected logger: EntropyLogger
protected entropy: Entropy
protected endpoint: string

constructor ({ entropy, endpoint, flowContext }: { entropy: Entropy, endpoint: string, flowContext: string }) {
this.logger = new EntropyLogger(flowContext, endpoint)
this.entropy = entropy
this.endpoint = endpoint
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import Entropy from "@entropyxyz/sdk";
import type { Signer, SignerResult } from "@polkadot/api/types";
import { Registry, SignerPayloadJSON } from "@polkadot/types/types";
import { u8aToHex } from "@polkadot/util";
import { stripHexPrefix } from "../../common/utils";
import { blake2AsHex, decodeAddress, encodeAddress, signatureVerify } from "@polkadot/util-crypto";
import { stripHexPrefix } from "../../common/utils";

let id = 0
export default class FaucetSigner implements Signer {
Expand Down
44 changes: 44 additions & 0 deletions src/faucet/interaction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import Entropy from "@entropyxyz/sdk"
import { EntropyLogger } from '../common/logger'
import { TESTNET_PROGRAM_HASH } from "./utils"
import { EntropyFaucet } from "./main"
import { print } from "src/common/utils"

let chosenVerifyingKeys = []
const amount = "10000000000"
// context for logging file
const FLOW_CONTEXT = 'ENTROPY_FAUCET_INTERACTION'
export async function entropyFaucet (entropy: Entropy, options, logger: EntropyLogger) {
const { endpoint } = options
if (!entropy.registrationManager.signer.pair) {
throw new Error("Keys are undefined")
}
const faucetService = new EntropyFaucet(entropy, endpoint)
const verifyingKeys = await faucetService.getAllFaucetVerifyingKeys()
// @ts-expect-error
return sendMoneyFromRandomFaucet(entropy, options.endpoint, verifyingKeys, logger)
}

// Method that takes in the initial list of verifying keys (to avoid multiple calls to the rpc) and recursively retries each faucet until
// a successful transfer is made
async function sendMoneyFromRandomFaucet (entropy: Entropy, endpoint: string, verifyingKeys: string[], logger: EntropyLogger) {
const faucetService = new EntropyFaucet(entropy, endpoint)
const selectedAccountAddress = entropy.keyring.accounts.registration.address
const { chosenVerifyingKey, faucetAddress } = faucetService.getRandomFaucet(chosenVerifyingKeys, verifyingKeys)
try {
await faucetService.sendMoney({ amount, addressToSendTo: selectedAccountAddress, faucetAddress, chosenVerifyingKey, faucetProgramPointer: TESTNET_PROGRAM_HASH })
// reset chosen keys after successful transfer
chosenVerifyingKeys = []
print(`Account: ${selectedAccountAddress} has been successfully funded with ${parseInt(amount).toLocaleString('en-US')} BITS`)
} catch (error) {
logger.error('Error issuing funds through faucet', error, FLOW_CONTEXT)
chosenVerifyingKeys.push(chosenVerifyingKey)
if (error.message.includes('FaucetError') || chosenVerifyingKeys.length === verifyingKeys.length) {
console.error('ERR::', error.message)
return
} else {
// Check for non faucet errors (FaucetError) and retry faucet
await sendMoneyFromRandomFaucet(entropy, endpoint, verifyingKeys, logger)
}
}
}
101 changes: 101 additions & 0 deletions src/faucet/main.ts
frankiebee marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import Entropy from "@entropyxyz/sdk";
import { EntropyBase } from "../common/entropy-base";
import { blake2AsHex, encodeAddress } from "@polkadot/util-crypto";
import { FAUCET_PROGRAM_MOD_KEY, TESTNET_PROGRAM_HASH } from "./utils";
import { EntropyBalance } from "src/balance/main";
import { EntropyProgram } from "src/program/main";
import FaucetSigner from "./helpers/signer";
import { SendMoneyParams } from "./types";

const FLOW_CONTEXT = 'ENTROPY-FAUCET'

export class EntropyFaucet extends EntropyBase {
constructor (entropy: Entropy, endpoint: string) {
super({ entropy, endpoint, flowContext: FLOW_CONTEXT })
}

// Method used to sign and send the transfer request (transfer request = call argument) using the custom signer
// created to overwrite how we sign the payload that is sent up chain
async faucetSignAndSend (call: any, amount: number, senderAddress: string, chosenVerifyingKey: any): Promise<any> {
const api = this.entropy.substrate
const faucetSigner = new FaucetSigner(api.registry, this.entropy, amount, chosenVerifyingKey)

const sig = await call.signAsync(senderAddress, {
signer: faucetSigner,
});
return new Promise((resolve, reject) => {
sig.send(({ status, dispatchError }: any) => {
// status would still be set, but in the case of error we can shortcut
// to just check it (so an error would indicate InBlock or Finalized)
if (dispatchError) {
let msg: string
if (dispatchError.isModule) {
// for module errors, we have the section indexed, lookup
const decoded = api.registry.findMetaError(dispatchError.asModule);
// @ts-ignore
const { documentation, method, section } = decoded;

msg = `${section}.${method}: ${documentation.join(' ')}`
} else {
// Other, CannotLookup, BadOrigin, no extra info
msg = dispatchError.toString()
}
return reject(Error(msg))
}
if (status.isFinalized) resolve(status)
})
})
}

async getAllFaucetVerifyingKeys (programModKey = FAUCET_PROGRAM_MOD_KEY) {
const modifiableKeys = await this.entropy.substrate.query.registry.modifiableKeys(programModKey)
return modifiableKeys.toJSON()
}

// To handle overloading the individual faucet, multiple faucet accounts have been generated, and here is
// where we choose one of those faucet's at random
getRandomFaucet (previousVerifyingKeys: string[] = [], allVerifyingKeys: string[] = []) {
if (allVerifyingKeys.length === previousVerifyingKeys.length) {
throw new Error('FaucetError: There are no more faucets to choose from')
}
let chosenVerifyingKey = allVerifyingKeys[Math.floor(Math.random() * allVerifyingKeys.length)]
if (previousVerifyingKeys.length && previousVerifyingKeys.includes(chosenVerifyingKey)) {
const filteredVerifyingKeys = allVerifyingKeys.filter((key: string) => !previousVerifyingKeys.includes(key))
chosenVerifyingKey = filteredVerifyingKeys[Math.floor(Math.random() * filteredVerifyingKeys.length)]
}
const hashedKey = blake2AsHex(chosenVerifyingKey)
const faucetAddress = encodeAddress(hashedKey, 42).toString()

return { chosenVerifyingKey, faucetAddress }
}

async sendMoney (
{
amount,
addressToSendTo,
faucetAddress,
chosenVerifyingKey,
faucetProgramPointer = TESTNET_PROGRAM_HASH
}: SendMoneyParams
): Promise<any> {
const balanceService = new EntropyBalance(this.entropy, this.endpoint)
const programService = new EntropyProgram(this.entropy, this.endpoint)
// check balance of faucet address
const balance = await balanceService.getBalance(faucetAddress)
if (balance <= 0) throw new Error('FundsError: Faucet Account does not have funds')
// check verifying key for only one program matching the program hash
const programs = await programService.list({ verifyingKey: chosenVerifyingKey })
if (programs.length) {
if (programs.length > 1) throw new Error('ProgramsError: Faucet Account has too many programs attached, expected less')
if (programs.length === 1 && programs[0].program_pointer !== faucetProgramPointer) {
throw new Error('ProgramsError: Faucet Account does not possess Faucet program')
}
} else {
throw new Error('ProgramsError: Faucet Account has no programs attached')
}

const transfer = this.entropy.substrate.tx.balances.transferAllowDeath(addressToSendTo, BigInt(amount));
const transferStatus = await this.faucetSignAndSend(transfer, parseInt(amount), faucetAddress, chosenVerifyingKey)
if (transferStatus.isFinalized) return transferStatus
}
}
7 changes: 7 additions & 0 deletions src/faucet/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface SendMoneyParams {
amount: string
addressToSendTo: string
faucetAddress: string
chosenVerifyingKey: string
faucetProgramPointer: string
}
File renamed without changes.
98 changes: 0 additions & 98 deletions src/flows/entropyFaucet/faucet.ts

This file was deleted.

45 changes: 0 additions & 45 deletions src/flows/entropyFaucet/index.ts

This file was deleted.

1 change: 0 additions & 1 deletion src/flows/index.ts

This file was deleted.

Loading
Loading