Skip to content

Commit

Permalink
Refactoring: move classes, docs, delay between requests feature (#4)
Browse files Browse the repository at this point in the history
* refactoring

* version bump

* fix test

* bugfix

* fix missing timestamp clause

* e2e tests with chainsimulator (#5)

* first PoC of chain simulator e2e test

* fixes, esdt test, workflow

* merge

* fix hosts

* new host

* chainsimulator hostname

* custom network

* sleep

* remove custom network

* update hostnames again

* update workflow name

* refactoring

* add missing eol
  • Loading branch information
bogdan-rosianu authored Oct 23, 2024
1 parent fbaccda commit 3402190
Show file tree
Hide file tree
Showing 16 changed files with 757 additions and 80 deletions.
38 changes: 38 additions & 0 deletions .github/workflows/chainsimulator-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Chain simulator e2e tests workflow

on:
pull_request:

jobs:
test-chainsimulator-e2e:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x]
steps:
- uses: actions/checkout@v3

- name: Build and start the Docker images
run: docker compose -f "e2e-tests/chain-simulator-e2e/docker-compose.yml" up -d --build

- name: Wait for services to be ready
run: |
echo "Waiting for services to be healthy..."
docker ps
docker logs chainsimulator
sleep 20 # Wait for 20 seconds to ensure services are up
- name: Print docker containers
run: docker ps

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'

- name: Install dependencies
run: npm install

- name: Run e2e tests
run: npm run test:e2e
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ Event processor for JavaScript and TypeScript (written in TypeScript).
## Usage

```js
let eventProcessor = new EventProcessor();
let lastProcessedTimestamp = 1727858320;
const eventProcessor = new EventProcessor();
await eventProcessor.start({
elasticUrl: 'https://index.multiversx.com',
eventIdentifiers: ['swapTokensFixedInput'],
emitterAddresses: ['erd1qqqqqqqqqqqqqpgqt0uek344kaerr4gf9g2r8l0f4l8ygyha2jps82u9r6'],
pageSize: 1000,
scrollTimeout: "1m",
delayBetweenRequestsInMilliseconds: 100,
getLastProcessedTimestamp: async () => {
return lastProcessedTimestamp;
},
Expand Down
150 changes: 150 additions & 0 deletions e2e-tests/chain-simulator-e2e/chain.simulator.operations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import axios from "axios";

const VM_TYPE = "0500";
const CODE_METADATA = "0100";
const SC_DEPLOY_ADDRESS = 'erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu';
const ESDT_ADDRESS = 'erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u';

export async function fundAddress(chainSimulatorUrl: string, address: string) {
const payload = [
{
address: address,
balance: '100000000000000000000000',
},
];
await axios.post(`${chainSimulatorUrl}/simulator/set-state`, payload);
}

export async function getNonce(chainSimulatorUrl: string, address: string): Promise<number> {
try {
const currentNonceResponse = await axios.get(`${chainSimulatorUrl}/address/${address}/nonce`);
return currentNonceResponse.data.data.nonce;
} catch (e) {
console.error(e);
return 0;
}
}

export async function deploySc(args: DeployScArgs): Promise<string> {
try {
const contractCodeHex = Buffer.from(args.contractCodeRaw).toString('hex');
const contractArgs = [VM_TYPE, CODE_METADATA, ...args.hexArguments];
const contractPayload = contractCodeHex + '@' + contractArgs.join('@');

const txHash = await sendTransaction(new SendTransactionArgs({
chainSimulatorUrl: args.chainSimulatorUrl,
sender: args.deployer,
receiver: SC_DEPLOY_ADDRESS,
dataField: contractPayload,
}));

const txResponse = await axios.get(`${args.chainSimulatorUrl}/transaction/${txHash}?withResults=true`);
const scDeployLog = txResponse?.data?.data?.transaction?.logs?.events?.find((event: { identifier: string; }) => event.identifier === 'SCDeploy');
console.log(`Deployed SC. tx hash: ${txHash}. address: ${scDeployLog?.address}`);
return scDeployLog?.address;
} catch (e) {
console.error(e);
return 'n/a';
}
}

export async function issueEsdt(args: IssueEsdtArgs) {
const txHash = await sendTransaction(new SendTransactionArgs({
chainSimulatorUrl: args.chainSimulatorUrl,
sender: args.issuer,
receiver: ESDT_ADDRESS,
dataField: `issue@${Buffer.from(args.tokenName).toString('hex')}@${Buffer.from(args.tokenTicker).toString('hex')}@1e9b0e04e39e5845000000@12`,
value: '50000000000000000',
}));

const txResponse = await axios.get(`${args.chainSimulatorUrl}/transaction/${txHash}?withResults=true`);
const esdtIssueLog = txResponse?.data?.data?.transaction?.logs?.events?.find((event: { identifier: string; }) => event.identifier === 'issue');
const tokenIdentifier = Buffer.from(esdtIssueLog.topics[0], 'base64').toString();
console.log(`Issued token with ticker ${args.tokenTicker}. tx hash: ${txHash}. identifier: ${tokenIdentifier}`);
return tokenIdentifier;
}

export async function transferEsdt(args: TransferEsdtArgs) {
const transferValue = args.plainAmountOfTokens * (10 ** 18);
return await sendTransaction(new SendTransactionArgs({
chainSimulatorUrl: args.chainSimulatorUrl,
sender: args.sender,
receiver: args.receiver,
dataField: `ESDTTransfer@${Buffer.from(args.tokenIdentifier).toString('hex')}@${transferValue.toString(16)}`,
value: '0',
}));
}

export async function sendTransaction(args: SendTransactionArgs): Promise<string> {
try {
const nonce = await getNonce(args.chainSimulatorUrl, args.sender);

const tx = {
sender: args.sender,
receiver: args.receiver,
nonce: nonce,
value: args.value,
gasPrice: 1000000000,
gasLimit: args.gasLimit,
data: Buffer.from(args.dataField).toString('base64'),
signature: 'a'.repeat(128),
chainID: 'chain',
version: 1,
};

const txHashResponse = await axios.post(`${args.chainSimulatorUrl}/transaction/send`, tx);
const txHash = txHashResponse.data.data.txHash;
await axios.post(`${args.chainSimulatorUrl}/simulator/generate-blocks-until-transaction-processed/${txHash}`);
return txHash;
} catch (e) {
console.error(e);
return 'n/a';
}
}

export class SendTransactionArgs {
chainSimulatorUrl: string = '';
sender: string = '';
receiver: string = '';
dataField: string = '';
value?: string = '0';
gasLimit?: number = 100_000_000;

constructor(options: Partial<SendTransactionArgs> = {}) {
Object.assign(this, options);
}
}

export class IssueEsdtArgs {
chainSimulatorUrl: string = '';
issuer: string = '';
tokenName: string = '';
tokenTicker: string = '';

constructor(options: Partial<IssueEsdtArgs> = {}) {
Object.assign(this, options);
}
}

export class TransferEsdtArgs {
chainSimulatorUrl: string = '';
sender: string = '';
receiver: string = '';
tokenIdentifier: string = '';
plainAmountOfTokens: number = 1;

constructor(options: Partial<TransferEsdtArgs> = {}) {
Object.assign(this, options);
}
}

export class DeployScArgs {
chainSimulatorUrl: string = '';
deployer: string = '';
contractCodeRaw: Buffer = Buffer.from('');
hexArguments: string[] = [];

constructor(options: Partial<DeployScArgs> = {}) {
Object.assign(this, options);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
{
"buildInfo": {
"rustc": {
"version": "1.81.0",
"commitHash": "eeb90cda1969383f56a2637cbd3037bdf598841c",
"commitDate": "2024-09-04",
"channel": "Stable",
"short": "rustc 1.81.0 (eeb90cda1 2024-09-04)"
},
"contractCrate": {
"name": "ping-pong-egld",
"version": "0.0.2",
"gitVersion": "v0.53.2-4-g190f26fac"
},
"framework": {
"name": "multiversx-sc",
"version": "0.53.2"
}
},
"docs": [
"An example contract that emits logs for ping and pong calls. on init you can configure the egld amount to be called at ping"
],
"name": "PingPong",
"constructor": {
"docs": [
"Necessary configuration when deploying:",
"`ping_amount` - the exact EGLD amount that needs to be sent when `ping`-ing."
],
"inputs": [
{
"name": "ping_amount",
"type": "BigUint"
}
],
"outputs": []
},
"upgradeConstructor": {
"inputs": [
{
"name": "ping_amount",
"type": "BigUint"
}
],
"outputs": []
},
"endpoints": [
{
"docs": [
"User sends some EGLD to be locked in the contract for a period of time.",
"Optional `_data` argument is ignored."
],
"name": "ping",
"mutability": "mutable",
"payableInTokens": [
"EGLD"
],
"inputs": [
{
"name": "_data",
"type": "ignore",
"multi_arg": true
}
],
"outputs": []
},
{
"docs": [
"User can take back funds from the contract.",
"Can only be called after expiration."
],
"name": "pong",
"mutability": "mutable",
"inputs": [],
"outputs": []
},
{
"name": "getPingAmount",
"mutability": "readonly",
"inputs": [],
"outputs": [
{
"type": "BigUint"
}
]
}
],
"events": [
{
"docs": [
"Signals a successful ping by user with amount"
],
"identifier": "",
"inputs": [
{
"name": "caller",
"type": "Address",
"indexed": true
},
{
"name": "pinged_amount",
"type": "BigUint"
}
]
},
{
"docs": [
"Signals a successful pong by user with amount"
],
"identifier": "",
"inputs": [
{
"name": "caller",
"type": "Address",
"indexed": true
},
{
"name": "ponged_amount",
"type": "BigUint"
}
]
}
],
"esdtAttributes": [],
"hasCallback": false,
"types": {}
}
Binary file not shown.
37 changes: 37 additions & 0 deletions e2e-tests/chain-simulator-e2e/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
services:
elasticsearch:
ports:
- "9200:9200"
container_name: es-container
image: docker.elastic.co/elasticsearch/elasticsearch:7.16.1
environment:
- "discovery.type=single-node"
- "xpack.security.enabled=false"
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ulimits:
memlock:
soft: -1
hard: -1
healthcheck:
test: [ "CMD", "curl", "-f", "http://localhost:9200" ]
interval: 10s
timeout: 5s
retries: 5

chainsimulator:
container_name: chainsimulator
image: multiversx/chainsimulator
command: ["--node-override-config", "./overridable-config.toml"]
volumes:
- ./overridable-config.toml:/multiversx/overridable-config.toml
depends_on:
- elasticsearch
environment:
ELASTIC_SEARCH_URL: 'http://localhost:9200'
ports:
- "8085:8085"
healthcheck:
test: [ "CMD", "curl", "-f", "http://localhost:8085/simulator/observers" ]
interval: 10s
timeout: 5s
retries: 5
Loading

0 comments on commit 3402190

Please sign in to comment.