universal-wallet-contract/tests/UniversalWalletContract.spe...

258 lines
11 KiB
TypeScript

import {Blockchain, printTransactionFees, SandboxContract, TreasuryContract} from '@ton/sandbox';
import {beginCell, Cell, toNano} from '@ton/core';
import { UniversalWalletContract, universalWalletContractConfigToCell } from '../wrappers/UniversalWalletContract';
import '@ton/test-utils';
import { compile } from '@ton/blueprint';
import * as tweetnacl from 'tweetnacl-ts';
import BN from 'bn.js';
function shiftLeft32bitBigInt(num: bigint): bigint {
const shiftAmount = BigInt(2) ** BigInt(32);
return num * shiftAmount;
}
describe('UniversalWalletContract', () => {
const privateKeyHex = '86d1016f097805357a751a6b1ec8691eddb62def4f09aac6966ca10c6da9113e37625a5e1bf4a9bb4e8e57adf780d02781ee2c2b80129dc6a90f23b01657f9d9';
const privateKey = tweetnacl.sign_keyPair_fromSecretKey(Buffer.from(privateKeyHex, 'hex'));
let code: Cell;
beforeAll(async () => {
code = await compile('UniversalWalletContract');
});
let blockchain: Blockchain;
let deployer: SandboxContract<TreasuryContract>;
let admin: SandboxContract<TreasuryContract>;
let someUser: SandboxContract<TreasuryContract>;
let universalWalletContract: SandboxContract<UniversalWalletContract>;
beforeEach(async () => {
blockchain = await Blockchain.create();
blockchain.now = 100;
deployer = await blockchain.treasury('deployer');
admin = await blockchain.treasury('admin');
someUser = await blockchain.treasury('someUser');
universalWalletContract = blockchain.openContract(UniversalWalletContract.createFromConfig({
subwalletId: 3,
publicKey: Buffer.from(privateKey.publicKey),
admin: admin.address,
}, code));
const deployResult = await universalWalletContract.sendDeploy(deployer.getSender(), toNano('10'));
expect(deployResult.transactions).toHaveTransaction({
from: deployer.address,
to: universalWalletContract.address,
deploy: true,
success: true,
exitCode: 0
});
});
it('should deploy', async () => {
// the check is done inside beforeEach
// blockchain and universalWalletContract are ready to use
});
it('should provide admin message correctly', async () => {
const commandResult = await universalWalletContract.sendAdminProvideMessage(admin.getSender(), toNano('0.05'), [
{
rawMsg: (beginCell()
.storeUint(0x18, 6)
.storeAddress(someUser.address)
.storeCoins(toNano('3'))
.storeUint(0, 1 + 4 + 4 + 32 + 64 + 1 + 1)
.endCell()),
msgMode: 1
},
{
rawMsg: (beginCell()
.storeUint(0x18, 6)
.storeAddress(someUser.address)
.storeCoins(toNano('5'))
.storeUint(0, 1 + 4 + 4 + 32 + 64 + 1 + 1)
.endCell()),
msgMode: 1
}
]);
expect(commandResult.transactions).toHaveTransaction({
from: admin.address,
to: universalWalletContract.address,
deploy: false,
success: true,
exitCode: 0
});
expect(commandResult.transactions).toHaveTransaction({
from: universalWalletContract.address,
to: someUser.address,
value: toNano('3'),
});
expect(commandResult.transactions).toHaveTransaction({
from: universalWalletContract.address,
to: someUser.address,
value: toNano('5'),
});
});
it('should provide backend signed user message correctly', async () => {
let packedRequest = beginCell();
let createdAt = Math.floor(Date.now() / 1000);
packedRequest = packedRequest.storeUint(shiftLeft32bitBigInt(BigInt(createdAt + 45)), 64);
packedRequest = packedRequest.storeUint(3, 32);
// without 1 TON gas fee the message will not be sent and contract throw bounce
packedRequest = packedRequest.storeCoins(toNano('1'));
packedRequest = (
packedRequest
.storeUint(1, 8)
.storeRef(
beginCell()
.storeUint(0x18, 6)
.storeAddress(someUser.address)
.storeCoins(toNano('0.5'))
.storeUint(0, 1 + 4 + 4 + 32 + 64 + 1 + 1)
.storeUint(0x1248, 32)
.endCell()
)
);
const requestHash = new Uint8Array(packedRequest.endCell().hash());
let signedRequest = beginCell();
let requestSignature = Buffer.from(tweetnacl.sign_detached(requestHash, new Uint8Array(Buffer.from(privateKeyHex, 'hex')))); //, new Uint8Array([...privateKey.secretKey, ...privateKey.publicKey])));
signedRequest = signedRequest.storeBuffer(requestSignature);
signedRequest = signedRequest.storeSlice(packedRequest.endCell().beginParse());
const commandResult = await universalWalletContract.sendSignedRequest(
someUser.getSender(),
toNano('2'),
signedRequest.endCell()
);
expect(commandResult.transactions).toHaveTransaction({
to: someUser.address,
from: universalWalletContract.address,
op: 0x1248,
value: toNano('0.5')
});
});
it('should reject duplicate signature send', async () => {
let packedRequest = beginCell();
let createdAt = Math.floor(Date.now() / 1000);
packedRequest = packedRequest.storeUint(shiftLeft32bitBigInt(BigInt(createdAt + 45)), 64);
packedRequest = packedRequest.storeUint(3, 32);
// without 1 TON gas fee the message will not be sent and contract throw bounce
packedRequest = packedRequest.storeCoins(toNano('1'));
packedRequest = (
packedRequest
.storeUint(1, 8)
.storeRef(
beginCell()
.storeUint(0x18, 6)
.storeAddress(someUser.address)
.storeCoins(toNano('0.5'))
.storeUint(0, 1 + 4 + 4 + 32 + 64 + 1 + 1)
.storeUint(0x1248, 32)
.endCell()
)
);
const requestHash = new Uint8Array(packedRequest.endCell().hash());
let signedRequest = beginCell();
let requestSignature = Buffer.from(tweetnacl.sign_detached(requestHash, new Uint8Array(Buffer.from(privateKeyHex, 'hex')))); //, new Uint8Array([...privateKey.secretKey, ...privateKey.publicKey])));
signedRequest = signedRequest.storeBuffer(requestSignature);
signedRequest = signedRequest.storeSlice(packedRequest.endCell().beginParse());
const commandResult = await universalWalletContract.sendSignedRequest(
someUser.getSender(),
toNano('2'),
signedRequest.endCell()
);
expect(commandResult.transactions).toHaveTransaction({
to: someUser.address,
from: universalWalletContract.address,
op: 0x1248,
value: toNano('0.5')
});
const dublicateCommandReuslt = await universalWalletContract.sendSignedRequest(
someUser.getSender(),
toNano('2'),
signedRequest.endCell()
);
expect(dublicateCommandReuslt.transactions).toHaveTransaction({
to: universalWalletContract.address,
from: someUser.address,
exitCode: 79
});
});
it('should be updated publicKey', async () => {
const newDataCell = universalWalletContractConfigToCell({
subwalletId: 3,
publicKey: Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex'),
admin: admin.address
});
await blockchain.setVerbosityForAddress(universalWalletContract.address, {
blockchainLogs: true,
vmLogs: 'vm_logs',
print: true,
debugLogs: true
})
const commandResult = await universalWalletContract.sendEditCode(admin.getSender(), toNano('0.1'), code, newDataCell);
expect(commandResult.transactions).toHaveTransaction({
success: true
});
const messagesState = await universalWalletContract.getMessageStates();
expect(messagesState.publicKey).toEqual(0n);
});
return
it('test big comissions', async () => {
for (let i = 0; i < 151; i++) {
let packedRequest = beginCell();
let createdAt = (i >= 149) ? 480 : 98;
packedRequest = packedRequest.storeUint(shiftLeft32bitBigInt(BigInt(createdAt + 45)) + BigInt(i), 64);
packedRequest = packedRequest.storeUint(3, 32);
// without 1 TON gas fee the message will not be sent and contract throw bounce
packedRequest = packedRequest.storeCoins(toNano('1'));
packedRequest = (
packedRequest
.storeUint(1, 8)
.storeRef(
beginCell()
.storeUint(0x18, 6)
.storeAddress(someUser.address)
.storeCoins(toNano('0.5'))
.storeUint(0, 1 + 4 + 4 + 32 + 64 + 1 + 1)
.storeUint(0x1248, 32)
.endCell()
)
);
const requestHash = new Uint8Array(packedRequest.endCell().hash());
let signedRequest = beginCell();
let requestSignature = Buffer.from(tweetnacl.sign_detached(requestHash, new Uint8Array(Buffer.from(privateKeyHex, 'hex')))); //, new Uint8Array([...privateKey.secretKey, ...privateKey.publicKey])));
signedRequest = signedRequest.storeBuffer(requestSignature);
signedRequest = signedRequest.storeSlice(packedRequest.endCell().beginParse());
if (i === 149) {
blockchain.now = 500;
}
const commandResult = await universalWalletContract.sendSignedRequest(
someUser.getSender(),
toNano('2'),
signedRequest.endCell()
);
console.log(`Executing ${i}`);
printTransactionFees(commandResult.transactions);
expect(commandResult.transactions).toHaveTransaction({
to: someUser.address,
from: universalWalletContract.address,
op: 0x1248,
value: toNano('0.5')
});
}
});
});