UNPKG

@chorus-one/ton

Version:

All-in-one tooling for building staking dApps on TON

134 lines (120 loc) 4.39 kB
import { TonNominatorPoolStaker } from '@chorus-one/ton' import { Address, TupleItem, TupleReader } from '@ton/core' import { describe, it } from 'mocha' import { use, assert, expect, spy } from 'chai' import { chaiAsPromised } from 'chai-promised' import spies from 'chai-spies' // Use chai-as-promised plugin for async tests use(chaiAsPromised) use(spies) describe('TonStaker', () => { const delegatorAddress = '0QDsF87nkTYgkvu1z5xveCEGTRnZmEVaVT0gdxoeyaNvmoCr' const validatorAddress = 'kf8SWCvzf6eJK4Q0ZOe14PqDdsT5wk0_Ni0wAThL0cVorNVU' const staker = new TonNominatorPoolStaker({ rpcUrl: 'https://ton.fake.website', addressDerivationConfig: { walletContractVersion: 4, workchain: 0, bounceable: false, testOnly: true, urlSafe: true, isBIP39: false } }) const getPoolDataResponse: TupleItem[] = [ { type: 'tuple', items: [] }, { type: 'int', value: BigInt(1) }, // nominators count { type: 'tuple', items: [] }, { type: 'tuple', items: [] }, { type: 'tuple', items: [] }, { type: 'tuple', items: [] }, { type: 'int', value: BigInt(10) }, // max nominator count { type: 'tuple', items: [] }, { type: 'int', value: BigInt(0) }, // nominator stake { type: 'tuple', items: [] }, { type: 'tuple', items: [] }, { type: 'tuple', items: [] }, { type: 'tuple', items: [] }, { type: 'tuple', items: [] }, { type: 'tuple', items: [] }, { type: 'tuple', items: [] }, { type: 'tuple', items: [] } ] const getNominatorListResponse: TupleItem[] = [{ type: 'tuple', items: [] }] spy.on(staker, ['getClient'], () => { return { runMethod: async ( _address: Address, name: string, _stack?: TupleItem[] ): Promise<{ gas_used: number; stack: TupleReader }> => { let stackmock: TupleItem[] switch (name) { case 'get_pool_data': { stackmock = getPoolDataResponse break } case 'list_nominators': { stackmock = getNominatorListResponse break } default: throw new Error(`Unknown method: ${name}`) } return { gas_used: 0, stack: new TupleReader(stackmock) } } } }) it('should generate correct unsigned delegate tx', async () => { const { tx } = await staker.buildStakeTx({ delegatorAddress, validatorAddress, amount: '0.5' }) assert.isDefined(tx.messages, 'tx.messages should be defined') if (tx.messages) { assert.equal(tx.messages[0]?.amount, BigInt('500000000')) assert.equal(tx.messages[0]?.bounceable, true) assert.equal(tx.messages[0]?.payload, 'd') assert.equal(tx.messages[0]?.address, 'kf8SWCvzf6eJK4Q0ZOe14PqDdsT5wk0_Ni0wAThL0cVorNVU') } }) it('should handle amount fuzzing correctly', async () => { await Promise.all( [ ['0', '0'], // zero handlung ['1', '1000000000'], // 1 * denomMultiplier = denomMultiplier ['1.2', '1200000000'], // most common case ['0.123456789', '123456789'], // max precision ['1000000000', '1000000000000000000'], // unlikely edge case to send 1 000 000 000 macro tokens ['0.1234567891', '', 'Invalid number'], // over max precision ['', '0'], // empty string defaults to 0 ['abc', '', 'Cannot convert abc to a BigInt'], // text is not a valid number ['-1', '', 'amount to stake (-1) is less than the minimum stake required (0)'] // negive amounts are not allowed ].map(async (testData) => { const [amount, expectedAmount, expectedError] = testData const runTest = async (amount: string): Promise<bigint> => { const { tx } = await staker.buildStakeTx({ delegatorAddress, validatorAddress, amount }) if (tx.messages === undefined) { throw new Error('tx.message is undefined') } return tx.messages[0]?.amount } if (expectedError) { await expect(runTest(amount)).to.be.rejectedWith(expectedError) } else { const returnedAmount = await runTest(amount) // check if we have the expected amount assert.equal(returnedAmount.toString(), expectedAmount) } }) ) }) })