265 lines
11 KiB
TypeScript
265 lines
11 KiB
TypeScript
import {Blockchain, prettyLogTransactions, 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;
|
|
let memCode: Cell;
|
|
|
|
beforeAll(async () => {
|
|
code = await compile('UniversalWalletContract');
|
|
memCode = await compile('SignatureMemContract');
|
|
});
|
|
|
|
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,
|
|
memCode: memCode
|
|
}, 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()
|
|
);
|
|
prettyLogTransactions(commandResult.transactions);
|
|
printTransactionFees(commandResult.transactions);
|
|
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()
|
|
);
|
|
prettyLogTransactions(dublicateCommandReuslt.transactions)
|
|
printTransactionFees(dublicateCommandReuslt.transactions);
|
|
expect(dublicateCommandReuslt.transactions).toHaveTransaction({
|
|
to: universalWalletContract.address,
|
|
op: 0xbaca20
|
|
});
|
|
});
|
|
|
|
it('should be updated publicKey', async () => {
|
|
const newDataCell = universalWalletContractConfigToCell({
|
|
subwalletId: 3,
|
|
publicKey: Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex'),
|
|
admin: admin.address,
|
|
memCode: memCode
|
|
});
|
|
// 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')
|
|
});
|
|
}
|
|
});
|
|
});
|