-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactoring: move classes, docs, delay between requests feature (#4)
* 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
1 parent
fbaccda
commit 3402190
Showing
16 changed files
with
757 additions
and
80 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,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 |
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
150 changes: 150 additions & 0 deletions
150
e2e-tests/chain-simulator-e2e/chain.simulator.operations.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,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); | ||
} | ||
} |
126 changes: 126 additions & 0 deletions
126
e2e-tests/chain-simulator-e2e/contracts/ping-pong/ping-pong-egld.abi.json
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,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 added
BIN
+1.32 KB
e2e-tests/chain-simulator-e2e/contracts/ping-pong/ping-pong-egld.wasm
Binary file not shown.
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,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 |
Oops, something went wrong.