o1js
Version:
TypeScript framework for zk-SNARKs and zkApps
102 lines (87 loc) • 3.95 kB
text/typescript
import { UInt64, SmartContract, Mina, AccountUpdate, method, Transaction } from 'o1js';
class MyContract extends SmartContract {
@method async shouldMakeCompileThrow() {
this.network.blockchainLength.get();
}
}
let contractAccount: Mina.TestPublicKey;
let contract: MyContract;
let feePayer: Mina.TestPublicKey;
describe('transactions', () => {
beforeAll(async () => {
// set up local blockchain, create contract account keys, deploy the contract
let Local = await Mina.LocalBlockchain({ proofsEnabled: false });
Mina.setActiveInstance(Local);
[] = Local.testAccounts;
contractAccount = Mina.TestPublicKey.random();
contract = new MyContract(contractAccount);
let tx = await Mina.transaction(feePayer, async () => {
AccountUpdate.fundNewAccount(feePayer);
await contract.deploy();
});
tx.sign([feePayer.key, contractAccount.key]).send();
});
it('setFee should not change nonce', async () => {
let tx = await Mina.transaction(feePayer, async () => {
contract.requireSignature();
AccountUpdate.attachToTransaction(contract.self);
});
let nonce = tx.transaction.feePayer.body.nonce;
let promise = await tx.sign([feePayer.key, contractAccount.key]).send();
let new_fee = await promise.setFee(new UInt64(100));
new_fee.sign([feePayer.key, contractAccount.key]);
// second send is rejected for using the same nonce
await expect(new_fee.send()).rejects.toThrowError('Account_nonce_precondition_unsatisfied');
// check that tx was applied, by checking nonce was incremented
expect(new_fee.transaction.feePayer.body.nonce).toEqual(nonce);
});
it('Second tx should work when first not sent', async () => {
let tx = await Mina.transaction(feePayer, async () => {
contract.requireSignature();
AccountUpdate.attachToTransaction(contract.self);
});
let nonce = tx.transaction.feePayer.body.nonce;
let promise = tx.sign([feePayer.key, contractAccount.key]);
let new_fee = promise.setFeePerSnarkCost(42.7);
await new_fee.sign([feePayer.key, contractAccount.key]);
await new_fee.send();
// check that tx was applied, by checking nonce was incremented
expect((await new_fee).transaction.feePayer.body.nonce).toEqual(nonce);
});
it('fromJSON should serialize and deserialize transaction correctly', async () => {
// Create a transaction
let originalTx = await Mina.transaction(feePayer, async () => {
contract.requireSignature();
AccountUpdate.attachToTransaction(contract.self);
});
// Serialize to JSON string
let jsonString = originalTx.toJSON();
// Deserialize using fromJSON with string input
let deserializedTx = Transaction.fromJSON(jsonString);
// Verify the deserialized transaction has the same structure
expect(deserializedTx.transaction.feePayer.body.publicKey.x).toEqual(
originalTx.transaction.feePayer.body.publicKey.x
);
expect(deserializedTx.transaction.feePayer.body.nonce).toEqual(
originalTx.transaction.feePayer.body.nonce
);
expect(deserializedTx.transaction.accountUpdates.length).toEqual(
originalTx.transaction.accountUpdates.length
);
expect(deserializedTx.toJSON()).toEqual(originalTx.toJSON());
});
it('should throw proper error for malformed JSON string input', async () => {
// Test invalid JSON syntax
expect(() => {
Transaction.fromJSON('{"invalid": json}');
}).toThrow('Failed to parse ZkappCommand from JSON string:');
// Test valid JSON but invalid ZkappCommand structure
expect(() => {
Transaction.fromJSON('{"not": "a", "valid": "zkapp", "command": true}');
}).toThrow('Failed to construct ZkappCommand from parsed JSON:');
// Test completely malformed string
expect(() => {
Transaction.fromJSON('not json at all');
}).toThrow('Failed to parse ZkappCommand from JSON string:');
});
});