echpochmak v0.3.5
About The Project
Echpochmak, is a framework based on TONOS Client Library for TON DApp development
It provides some high-level interfaces to interact with your TON smart-contracts, allowing you to write integration and e2e tests.
Content Table
- About The Project
- Content Table
- Getting Started
- Test Examples
- Usage
- Write test
- Tests are written using Mocha
- Restart tondev command
- Or set port (if you use don't default port in tondev-cli)
- The manager is in the global scope
- Create new Manager object
- Create client
- Generate keys
- Load contract
- Referring to the contract
- Deploy contract
- Deploy with parameters
- Or use custom keys
- Add contract from address
- Run contract
- Run contract(no sign)
- RunLocal contract
- Run contract with message
- Use Giver
- Contract fields
- Helpers
- Asserts
- Wallet Contract
- Custom Contract Object
- Contributing
- License
- Contact
Getting Started
- Install
Echpochmak
npm install -g echpochmak
Test Examples
You can see examples of tests here
Usage
To run all tests:
echpochmak-cli test -p ./path/to/tests/folder
Synopsis
test options
Options
Option | Description | Default |
---|---|---|
-p, --path | Path to test folder | |
-b, --bail | Enable bail | false |
-v, --verbose | Wite debug info | false |
-t, --timeout | Timeout time for test | 0 |
--version (boolean) | Show version | |
--help (boolean) | Show help information |
If you need help, use
echpochmak-cli --help
Write test
Tests are written using Mocha
Restart tondev command
beforeEach(async () => {
await restart(); //Recreate and start containers
});
Or set port (if you use don't default port in tondev-cli)
restart command does not set a port for the container, but only uses it for long polling
beforeEach(async () => {
await restart(8080); //Default 80
});
The manager is in the global scope
const manager = new Manager();
Create new Manager object
beforeEach(async () => {
manager = new Manager();
Create client
default
['net.ton.dev']
Signature
async createClient(servers: string[] = ['net.ton.dev'])
await manager.createClient(['http://localhost:8080/graphql']);
Generate keys
let keys = await manager.createKeysAndReturn();
Load contract
Signature
public async loadContract( contractPath: string, abiPath: string, options: loadOptions )
await manager.loadContract(
'./tests/contract/15_MessageReceiver.tvc', // tvc
'./tests/contract/15_MessageReceiver.abi.json' // abi
);
await manager.loadContract(
'./tests/contract/15_MessageReceiver.tvc', // tvc
'./tests/contract/15_MessageReceiver.abi.json' // abi
);
// Custom name
await manager.loadContract(
'./tests/contract/15_MessageSender.tvc',
'./tests/contract/15_MessageSender.abi.json',
{ contractName: 'sender ' } // Name
);
// Custom keys
await manager.loadContract(
'./tests/contract/15_MessageSender.tvc',
'./tests/contract/15_MessageSender.abi.json',
{ keys: anyKeys } // keys
);
Get future/current address
let address = manager.contracts['15_MessageReceiver'].address;
loadOption signature
export interface loadOptions {
keys?: any;
contractName?: string;
}
Referring to the contract
manager.contracts['Contract_name']; // Taken from the name of the tvc file without extension
Deploy contract
Signature
async deployContract(constructorParams = {}, giveGram = true, keys?)
await manager.contracts['15_MessageReceiver'].deployContract();
await manager.contracts['15_MessageSender'].deployContract();
Deploy with parameters
await manager.contracts['9_PiggyBank'].deployContract({
own: manager.contracts['9_PiggyBank_Owner'].address,
lim: 1000000,
});
"Complicated" deploy
This method allows you to provide: constructor parameters, constructor headers and init parameters of your deployable smart contract
await manager.contracts['someName'].complicatedDeploy(
constructorParams,
constructorHeader,
initParams,
)
await manager.contracts['9_PiggyBank'].complicatedDeploy(
{
own: manager.contracts['9_PiggyBank_Owner'].address,
lim: 1000000,
},
{
pubkey: keypair.public,
expire: new Date()
},
{
randomNonce: '0x' + crypto.createHash('sha256').update(crypto.randomBytes(32)).digest('hex')
}
);
### Or use custom keys
```js
await manager.contracts['9_PiggyBank'].deployContract({}, keys);
Add contract from address
Signature
async addContractFromAddress( address: string, abiPath: string, contractName: string, keyPair? )
await manager.addContractFromAddress(
'address',
'abiPath',
'contractName',
keyPair // HERE, the keys are indicated, if nothing is specified, those inside the contract object are used
);
Run contract
Signature
async runContract(functionName, input, keyPair?)
await manager.contracts['15_MessageSender'].runContract('sendMessage', {
anotherContract: manager.contracts['15_MessageReceiver'].address,
});
Run contract(no sign)
await manager.contracts['15_MessageSender'].runContract(
'sendMessage',
{
anotherContract: manager.contracts['15_MessageReceiver'].address,
},
null // HERE, the keys are indicated, if nothing is specified, those inside the contract object are used (see below);
);
RunLocal contract
Signature
async runLocal(functionName, input, keyPair?)
await manager.contracts['15_MessageSender'].runLocal('getData', {});
Run contract with message
Signature
public async runWithMessage(functionName, input, keyPair?)
const msg = await manager.contracts['15_MessageSender'].runWithMessage(
'sendMessage',
{
anotherContract: manager.contracts['15_MessageReceiver'].address,
}
);
console.log(`Tokens were sent. Transaction id is ${msg.transaction.id}`);
console.log(`Run fees are ${JSON.stringify(msg.fees, null, 2)}`);
Use Giver
Signature
async giveToAddress(address, amount?: number)
manager.giveToAddress(manager.contracts['15_MessageReceiver'].address); // give 1000000000000000 gram
Contract fields
manager.contracts['15_MessageSender'].address; // Contract address
manager.contracts['15_MessageSender'].contractPath; // Contract path
manager.contracts['15_MessageSender'].isDeployed; // boolean
manager.contracts['15_MessageSender'].contractPackage; //{ abi,imageBase64 }
Helpers
Use helpers
from Manager object
manager.helpers.<helper>;
deployCheck
Signature
public static async deployCheck(address, client)
await manager.helpers.deployCheck(contractAddress, manager.client);
getAccountBalance
Signature
public static async getAccountBalance(address, client)
await manager.helpers.getAccountBalance(contractAddress, manager.client);
hasOnBounced
Signature
public static async hasOnBounced(address, timestamp: number, client)
timestamp
- The time when the transaction preceding OnBounced was made, guarantees the assistant will wait for the last message OnBounced
await manager.helpers.hasOnBounced(contractAddress, manager.client);
lastTransaction
Signature
public static async lastMessage( address, client, fields = 'src, dst, bounce, bounced, value' )
let tx = await manager.helpers.lastTransaction(contractAddress, manager.client);
lastMessage
Signature
public static async lastMessage( address, client, fields = 'src, dst, bounce, bounced, value' )
let msg = await manager.helpers.lastMessage(contractAddress, manager.client);
balanceHasChanged
Signature
public static async balanceHasChanged( address, client, oldValue, type: hasChangedValue )
await manager.helpers.balanceHasChanged(
contractAddress,
manager.client,
100000,
hasChangedValue.big
);
hasChangedValue enum
export enum hasChangedValue {
big,
small,
}
getRunFees
Signature
static async getRunFees( contract: Contract, functionName, input, client, keyPair )
const fees = await manager.helpers.getRunFees(
manager.contracts['15_MessageSender'],
'send',
manager.client,
keys
);
getDeployFees
Signature
static async getDeployFees( contract: Contract, constructorParams, newAccount: boolean, client, keyPair )
const fees = await manager.helpers.getDeployFees(
manager.contracts['15_MessageSender'],
{},
true,
manager.client,
keys
);
Asserts
assertError
Signature
assertError = async (asyncFn: any, code: number, message?)
Example
await assertError(
async () => {
await manager.contracts['9_PiggyBank'].runContract('getData', {});
},
3025,
'getData'
);
Wallet Contract
A simple wallet contract, you may find it useful when testing fallback functions
Create Wallet object()
Signature
async createWallet(keys?)
let wallet = await manager.createWallet();
or set keys
let wallet = await manager.createWallet(keys);
Deploy wallet
await wallet.deploy();
SendTransaction
Signature
async sendTransaction(dest: string, value: number, bounce: boolean)
await wallet.sendTransaction(
(dest: string),
(value: number),
(bounce: boolean)
);
Wallet fields
wallet.address; // wallet address
wallet.contractPath; // wallet path
wallet.isDeployed; // boolean
wallet.contractPackage; //{ abi,imageBase64 }
Custom Contract Object
Create class
CustomObject.example.js
const BaseContract = require('echpochmak').BaseContract; // Import BaseContract class
class BankContract extends BaseContract {
async getData() {
// new
return await this.runLocal('getData', {}); // Using 'contract' object fields
}
}
module.exports = BankContract;
Use in tests
Import contract object
const BankContract = require('./CustomObject.example');
Create object (Constructor used from BaseContract class)
const customBankObject = await new BankContract(
'./tests/contract/9_PiggyBank.tvc',
'./tests/contract/9_PiggyBank.abi.json',
manager.client,
await manager.createKeysAndReturn()
);
Add object in manager
manager.addCustomContract(customBankObject, 'customBank');
Use custom fields
const res = await manager.contracts['customBank'].getData();
BaseContract class
export abstract class BaseContract { public contractPackage: any; public contractPath: string; public isDeployed = false; public address: any; protected client: any; protected keys: any;
constructor( contractPath: string, abiPath: string, client, keys: any, noPath = false ) { // read contract .tvc file let imageBase64 = ''; if (!noPath) { const buff = readFileSync(resolve(contractPath)); // convert tvc code into base64 imageBase64 = buff.toString('base64'); }
// eslint-disable-next-line @typescript-eslint/no-var-requires
const abi = require(resolve(abiPath));
this.contractPackage = { abi, imageBase64 };
this.client = client;
this.keys = keys;
this.isDeployed = false;
}
public async deployContract(constructorParams = {}, giveGram = true, keys?) { this.address = await deploy( this.client, this.contractPackage, keys || this.keys, constructorParams, giveGram ); this.isDeployed = true; }
public async runWithMessage(functionName, input, keyPair?) { if (!this.isDeployed) { throw new Error('Contract not deployed'); } if (!keyPair) { keyPair = this.keys; } const runMessage = await this.client.contracts.createRunMessage({ address: this.address, abi: this.contractPackage.abi, functionName, input, keyPair, }); const messageProcessingState = await this.client.contracts.sendMessage( runMessage.message ); const result = await this.client.contracts.waitForRunTransaction( runMessage, messageProcessingState ); return result; }
public async runLocal(functionName, input, keyPair?) { if (!this.isDeployed) { throw new Error('Contract not deployed'); } if (!keyPair) { keyPair = this.keys; } const response = await this.client.contracts.runLocal({ address: this.address, abi: this.contractPackage.abi, functionName, input, keyPair, }); return response; }
public async futureAddress() { const futureAddress = ( await this.client.contracts.getDeployData({ abi: this.contractPackage.abi, imageBase64: this.contractPackage.imageBase64, publicKeyHex: this.keys.public, }) ).address; return futureAddress; } public async runContract(functionName, input, keyPair?) { if (!this.isDeployed) { throw new Error('Contract not deployed'); } if (!keyPair) { keyPair = this.keys; } const response = await this.client.contracts.run({ address: this.address, abi: this.contractPackage.abi, functionName: functionName, input: input, keyPair: keyPair, //there is no pubkey key check in the contract so we can leave it empty }); return response.output; } }
</details>
---
## Contributing
Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**.
1. Fork the Project
2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
4. Push to the Branch (`git push origin feature/AmazingFeature`)
5. Open a Pull Request
<!-- LICENSE -->
## License
Distributed under the Apache 2.0 License. See `LICENSE` for more information.
<!-- CONTACT -->
## Contact
<a href="http://wintex.pro/" target="_blank">
<img src="https://github.com/halva-suite/assets/blob/master/wintex.png?raw=true" width="200" />
</a>
<!-- MARKDOWN LINKS & IMAGES -->
<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
[wintex-url]: http://wintex.pro/
[wintex-logo]: https://github.com/wintexpro/assets/blob/master/wintex.png?raw=true
[contributors-shield]: https://img.shields.io/github/contributors/wintexpro/echpochmak.svg?style=flat-square
[contributors-url]: https://github.com/hwintexpro/echpochmak/graphs/contributors
[forks-shield]: https://img.shields.io/github/forks/wintexpro/echpochmak.svg?style=flat-square
[forks-url]: https://github.com/wintexpro/echpochmak/network/members
[stars-shield]: https://img.shields.io/github/stars/wintexpro/echpochmak.svg?style=flat-square
[stars-url]: https://github.com/wintexpro/echpochmak/stargazers
[issues-shield]: https://img.shields.io/github/issues/wintexpro/echpochmak.svg?style=flat-square
[issues-url]: https://github.com/wintexpro/echpochmak/issues
[license-shield]: https://img.shields.io/github/license/wintexpro/echpochmak.svg?style=flat-square
[license-url]: https://github.com/wintexpro/echpochmak/blob/master/LICENSE.txt
[product-screenshot]: images/screenshot.png
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago