-
Notifications
You must be signed in to change notification settings - Fork 27
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 #1469 from kadena-community/feat/kadena-cli/devnet…
…-simulation [@kadena-clli] Add devnet simulate command (Feat)
- Loading branch information
Showing
21 changed files
with
991 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@kadena/kadena-cli': patch | ||
--- | ||
|
||
Added kadena devnet simulate command and auxiliary functionalities |
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
52 changes: 52 additions & 0 deletions
52
packages/tools/kadena-cli/src/devnet/commands/devnetSimulation.ts
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,52 @@ | ||
import chalk from 'chalk'; | ||
import debug from 'debug'; | ||
import type { CreateCommandReturnType } from '../../utils/createCommand.js'; | ||
import { createCommand } from '../../utils/createCommand.js'; | ||
import { globalOptions } from '../../utils/globalOptions.js'; | ||
import { networkIsAlive } from '../utils/network.js'; | ||
import { simulateCoin } from '../utils/simulation/coin/simulate.js'; | ||
import { simulationOptions } from '../utils/simulation/simulationOptions.js'; | ||
|
||
export const simulateCommand: CreateCommandReturnType = createCommand( | ||
'simulate', | ||
'Simulate traffic on a devnet', | ||
[ | ||
globalOptions.network(), | ||
simulationOptions.simulationNumberOfAccounts({ isOptional: true }), | ||
simulationOptions.simulationTransferInterval({ isOptional: true }), | ||
globalOptions.logFolder({ isOptional: true }), | ||
simulationOptions.simulationTokenPool({ isOptional: true }), | ||
simulationOptions.simulationMaxTransferAmount({ isOptional: true }), | ||
simulationOptions.simulationDefaultChainId({ isOptional: true }), | ||
simulationOptions.simulationSeed({ isOptional: true }), | ||
], | ||
async (config) => { | ||
try { | ||
debug('devnet-simulate:action')({ config }); | ||
|
||
if (!(await networkIsAlive(config.networkConfig.networkHost))) { | ||
console.log( | ||
'Network is not reachable. Please check if the provided host is exposed.', | ||
); | ||
return; | ||
} | ||
|
||
await simulateCoin({ | ||
network: { | ||
id: 'fast-development', | ||
host: config.networkConfig.networkHost, | ||
}, | ||
maxAmount: config.simulationMaxTransferAmount, | ||
numberOfAccounts: config.simulationNumberOfAccounts, | ||
transferInterval: config.simulationTransferInterval, | ||
tokenPool: config.simulationTokenPool, | ||
logFolder: config.logFolder, | ||
defaultChain: config.simulationDefaultChainId, | ||
seed: config.simulationSeed, | ||
}); | ||
} catch (error) { | ||
console.log(chalk.red(`\n${error.message}\n`)); | ||
process.exit(1); | ||
} | ||
}, | ||
); |
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,14 @@ | ||
import http from 'http'; | ||
|
||
export function networkIsAlive(networkHost: string): Promise<boolean> { | ||
return new Promise((resolve) => { | ||
http | ||
.get(networkHost, (res) => { | ||
resolve(res.statusCode === 200); | ||
}) | ||
.on('error', (err) => { | ||
console.error(`Error checking network: ${err.message}`); | ||
resolve(false); | ||
}); | ||
}); | ||
} |
70 changes: 70 additions & 0 deletions
70
packages/tools/kadena-cli/src/devnet/utils/simulation/coin/crosschain-transfer.ts
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,70 @@ | ||
import type { ChainId, ICommandResult } from '@kadena/client'; | ||
import { createSignWithKeypair } from '@kadena/client'; | ||
import { transferCrossChain } from '@kadena/client-utils/coin'; | ||
import type { IAccount } from '../../../../constants/devnets.js'; | ||
import { defaultAccount } from '../../../../constants/devnets.js'; | ||
|
||
export async function crossChainTransfer({ | ||
network, | ||
sender, | ||
receiver, | ||
amount, | ||
gasPayer, | ||
}: { | ||
network: { host: string; id: string }; | ||
sender: IAccount; | ||
receiver: IAccount; | ||
amount: number; | ||
gasPayer: IAccount; | ||
}): Promise<ICommandResult> { | ||
// Gas Payer validations | ||
if (gasPayer.chainId !== receiver.chainId && gasPayer !== defaultAccount) { | ||
console.log( | ||
`Gas payer ${gasPayer.account} does not for sure have an account on the receiver chain; using ${defaultAccount.account} as gas payer`, | ||
); | ||
gasPayer = defaultAccount; | ||
} | ||
|
||
if (!gasPayer.keys.map((key) => key.secretKey)) { | ||
console.log( | ||
`Gas payer ${gasPayer.account} does not have a secret key; using ${defaultAccount.account} as gas payer`, | ||
); | ||
gasPayer = defaultAccount; | ||
} | ||
|
||
console.log( | ||
`Crosschain Transfer from ${sender.account}, chain ${sender.chainId}\nTo ${receiver.account}, chain ${receiver.chainId}\nAmount: ${amount}\nGas Payer: ${gasPayer.account}`, | ||
); | ||
|
||
const crossChainTransferRequest = transferCrossChain( | ||
{ | ||
sender: { | ||
account: sender.account, | ||
publicKeys: sender.keys.map((key) => key.publicKey), | ||
}, | ||
receiver: { | ||
account: receiver.account, | ||
keyset: { | ||
keys: receiver.keys.map((key) => key.publicKey), | ||
pred: 'keys-all', | ||
}, | ||
}, | ||
targetChainGasPayer: { | ||
account: gasPayer.account, | ||
publicKeys: gasPayer.keys.map((key) => key.publicKey), | ||
}, | ||
chainId: sender.chainId as ChainId, | ||
targetChainId: receiver.chainId as ChainId, | ||
amount: amount.toString(), | ||
}, | ||
{ | ||
host: network.host, | ||
defaults: { | ||
networkId: network.id, | ||
}, | ||
sign: createSignWithKeypair([...sender.keys, ...gasPayer.keys]), | ||
}, | ||
); | ||
|
||
return crossChainTransferRequest.executeTo('listen-continuation'); | ||
} |
92 changes: 92 additions & 0 deletions
92
packages/tools/kadena-cli/src/devnet/utils/simulation/coin/safe-transfer.ts
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,92 @@ | ||
import type { ICommandResult } from '@kadena/client'; | ||
import { Pact, createSignWithKeypair } from '@kadena/client'; | ||
import { submitClient } from '@kadena/client-utils/core'; | ||
import { PactNumber } from '@kadena/pactjs'; | ||
import type { ChainId } from '@kadena/types'; | ||
import type { IAccount } from '../../../../constants/devnets.js'; | ||
import { stringifyProperty } from '../helper.js'; | ||
|
||
export async function safeTransfer({ | ||
network, | ||
receiver, | ||
chainId, | ||
sender, | ||
amount, | ||
}: { | ||
network: { host: string; id: string }; | ||
receiver: IAccount; | ||
chainId: ChainId; | ||
sender: IAccount; | ||
amount: number; | ||
}): Promise<ICommandResult> { | ||
const extraAmount = new PactNumber('0.00000001').toPactDecimal(); | ||
const pactAmount = new PactNumber(amount) | ||
.plus(extraAmount.decimal) | ||
.toPactDecimal(); | ||
|
||
console.log( | ||
`Safe Transfer from ${sender.account} to ${ | ||
receiver.account | ||
}\nPublic Key: ${stringifyProperty(receiver.keys, 'publicKey')}\nAmount: ${ | ||
pactAmount.decimal | ||
}`, | ||
); | ||
|
||
return submitClient({ | ||
host: network.host, | ||
sign: createSignWithKeypair([...sender.keys, ...receiver.keys]), | ||
defaults: { | ||
networkId: network.id, | ||
}, | ||
})( | ||
Pact.builder | ||
.execution( | ||
Pact.modules.coin['transfer-create']( | ||
sender.account, | ||
receiver.account, | ||
() => '(read-keyset "ks")', | ||
pactAmount, | ||
), | ||
Pact.modules.coin.transfer( | ||
receiver.account, | ||
sender.account, | ||
extraAmount, | ||
), | ||
) | ||
.addData('ks', { | ||
keys: receiver.keys.map((key) => key.publicKey), | ||
pred: 'keys-all', | ||
}) | ||
.addSigner( | ||
sender.keys.map((key) => key.publicKey), | ||
(withCap) => [ | ||
withCap('coin.GAS'), | ||
withCap( | ||
'coin.TRANSFER', | ||
sender.account, | ||
receiver.account, | ||
pactAmount, | ||
), | ||
], | ||
) | ||
.addSigner( | ||
receiver.keys.map((key) => key.publicKey), | ||
(withCap) => [ | ||
withCap( | ||
'coin.TRANSFER', | ||
receiver.account, | ||
sender.account, | ||
extraAmount, | ||
), | ||
], | ||
) | ||
.setMeta({ | ||
gasLimit: 1500, | ||
chainId, | ||
senderAccount: sender.account, | ||
ttl: 8 * 60 * 60, //8 hours | ||
}) | ||
.setNetworkId(network.id) | ||
.getCommand(), | ||
).executeTo('listen'); | ||
} |
Oops, something went wrong.