Merge PR #941 from 'nodech/batch-refactor'

This commit is contained in:
Nodari Chkuaselidze 2025-07-23 13:26:44 +04:00
commit a812f0bc8d
No known key found for this signature in database
GPG key ID: B018A7BB437D1F05
5 changed files with 519 additions and 234 deletions

View file

@ -2717,7 +2717,7 @@ class RPC extends RPCBase {
async sendBatch(args, help) {
const [actions, options] = this._validateBatch(args, help, 'sendbatch');
const wallet = this.wallet;
const tx = await wallet.sendBatch(actions, options);
const {tx} = await wallet.sendBatch(actions, options);
return tx.getJSON(this.network);
}
@ -2727,7 +2727,7 @@ class RPC extends RPCBase {
options.paths = true;
const wallet = this.wallet;
const mtx = await wallet.createBatch(actions, options);
const {mtx} = await wallet.createBatch(actions, options);
return mtx.getJSON(this.network);
}
@ -2762,7 +2762,7 @@ class RPC extends RPCBase {
'NONE action requires 2 arguments: address, value'
);
const {addr, value} = this._validateSendToAddress(action);
actions.push([type, addr, value]);
actions.push({ type: type, args: [addr, value] });
break;
}
case 'OPEN': {
@ -2771,7 +2771,7 @@ class RPC extends RPCBase {
'OPEN action requires 1 argument: name'
);
const {name} = this._validateOpen(action);
actions.push([type, name]);
actions.push({ type: type, args: [name] });
break;
}
case 'BID': {
@ -2780,7 +2780,7 @@ class RPC extends RPCBase {
'BID action requires 3 arguments: name, bid, value'
);
const {name, bid, value} = this._validateBid(action);
actions.push([type, name, bid, value]);
actions.push({ type: type, args: [name, bid, value] });
break;
}
case 'REVEAL': {
@ -2790,9 +2790,9 @@ class RPC extends RPCBase {
);
const {name} = this._validateReveal(action);
if (name)
actions.push([type, name]);
actions.push({ type: type, args: [name] });
else
actions.push([type]);
actions.push({ type: type });
break;
}
case 'REDEEM': {
@ -2802,9 +2802,9 @@ class RPC extends RPCBase {
);
const {name} = this._validateRedeem(action);
if (name)
actions.push([type, name]);
actions.push({ type: type, args: [name] });
else
actions.push([type]);
actions.push({ type: type });
break;
}
case 'UPDATE': {
@ -2813,7 +2813,7 @@ class RPC extends RPCBase {
'UPDATE action requires 2 arguments: name, data'
);
const {name, resource} = this._validateUpdate(action);
actions.push([type, name, resource]);
actions.push({ type: type, args: [name, resource] });
break;
}
case 'RENEW': {
@ -2823,9 +2823,9 @@ class RPC extends RPCBase {
);
if (action.length === 1) {
const {name} = this._validateRenewal(action);
actions.push([type, name]);
actions.push({ type: type, args: [name] });
} else {
actions.push([type]);
actions.push({ type: type });
}
break;
}
@ -2835,7 +2835,7 @@ class RPC extends RPCBase {
'TRANSFER action requires 2 arguments: name, address'
);
const {name, address} = this._validateTransfer(action);
actions.push([type, name, address]);
actions.push({ type: type, args: [name, address] });
break;
}
case 'FINALIZE': {
@ -2845,9 +2845,9 @@ class RPC extends RPCBase {
);
if (action.length === 1) {
const {name} = this._validateFinalize(action);
actions.push([type, name]);
actions.push({ type: type, args: [name] });
} else {
actions.push([type]);
actions.push({ type: type });
}
break;
}
@ -2857,7 +2857,7 @@ class RPC extends RPCBase {
'CANCEL action requires 1 argument: name'
);
const {name} = this._validateCancel(action);
actions.push([type, name]);
actions.push({ type: type, args: [name] });
break;
}
case 'REVOKE': {
@ -2866,7 +2866,7 @@ class RPC extends RPCBase {
'REVOKE action requires 1 argument: name'
);
const {name} = this._validateRevoke(action);
actions.push([type, name]);
actions.push({ type: type, args: [name] });
break;
}
default:

View file

@ -3862,13 +3862,45 @@ class Wallet extends EventEmitter {
}
/**
* Make a batch transaction with multiple actions.
* @param {Array} actions
* @param {Object} options
* @returns {Promise<MTX>}
* @typedef {Object} BatchAction
* @property {String} type - Action type.
* @property {String} [id] - Action ID.
* @property {Array} [args] - Arguments for the action.
*/
async makeBatch(actions, options) {
/**
* @typedef {Object} BatchError
* @property {String} message - Error message.
* @property {String} type - Type of the action that caused the error.
* @property {Error} error - Original error object.
* @property {String} [id] - Optional ID for the action.
*/
/**
* @typedef {Object} BatchResult
* @property {MTX} mtx - The resulting transaction
* @property {BatchError[]} errors - List of errors encountered during
* processing.
*/
/**
* @typedef {Object} BatchTXResult
* @property {TX} tx - The resulting transaction
* @property {BatchError[]} errors - List of errors encountered during
* processing.
*/
/**
* Make a batch transaction with multiple actions.
* @param {BatchAction[]} actions
* @param {Object} options
* @param {Number|String} [options.account=0] - Account index or name.
* @param {Boolean} [options.partialFailure=false] - Allow partial failure.
* @throws {Error} - general validations.
* @returns {Promise<BatchResult>}
*/
async makeBatch(actions, options = {}) {
assert(Array.isArray(actions));
assert(actions.length, 'Batches require at least one action.');
@ -3885,15 +3917,13 @@ class Wallet extends EventEmitter {
// covenant like NONE, OPEN, or BID could shift the
// output array out of sync with their corresponding inputs.
actions.sort((a, b) => {
assert(Array.isArray(a));
assert(Array.isArray(b));
assert(a.length);
assert(b.length);
assert(a.type);
assert(b.type);
if (a[0] === b[0])
if (a.type === b.type)
return 0;
switch (b[0]) {
switch (b.type) {
case 'REVEAL':
case 'REDEEM':
case 'UPDATE':
@ -3912,110 +3942,142 @@ class Wallet extends EventEmitter {
// We track that by bumping receiveIndex.
const account = await this.getAccount(acct);
let receiveIndex = account.receiveDepth - 1;
/** @type {BatchError[]} */
const errors = [];
// "actions" are arrays that start with a covenant type (or meta-type)
// followed by the arguments expected by the corresponding "make" function.
for (const action of actions) {
const type = action.shift();
assert(typeof type === 'string');
/**
* @param {BatchAction} action
* @param {Array} args
*/
switch (type) {
const handleAction = async (action, args) => {
switch (action.type) {
case 'NONE': {
assert(action.length === 2);
assert(args.length === 2);
this.makeTX([{
address: action[0],
value: action[1]
address: args[0],
value: args[1]
}], mtx);
break;
}
case 'OPEN': {
assert(action.length === 1, 'Bad arguments for OPEN.');
const name = action[0];
assert(args.length === 1, 'Bad arguments for OPEN.');
const name = args[0];
await this.makeOpen(name, acct, mtx);
break;
}
case 'BID': {
assert(action.length === 3, 'Bad arguments for BID.');
const address = account.deriveReceive(receiveIndex++).getAddress();
const name = action[0];
const value = action[1];
const lockup = action[2];
const bidIndex = receiveIndex++;
assert(args.length === 3, 'Bad arguments for BID.');
const address = account.deriveReceive(bidIndex).getAddress();
const name = args[0];
const value = args[1];
const lockup = args[2];
await this.makeBid(name, value, lockup, acct, mtx, address);
break;
}
case 'REVEAL': {
if (action.length === 1) {
const name = action[0];
if (args.length === 1) {
const name = args[0];
await this.makeReveal(name, acct, mtx);
break;
}
assert(action.length === 0, 'Bad arguments for REVEAL.');
assert(args.length === 0, 'Bad arguments for REVEAL.');
await this.makeRevealAll(mtx, witnessSize);
break;
}
case 'REDEEM': {
if (action.length === 1) {
const name = action[0];
if (args.length === 1) {
const name = args[0];
await this.makeRedeem(name, acct, mtx);
break;
}
assert(action.length === 0, 'Bad arguments for REDEEM.');
assert(args.length === 0, 'Bad arguments for REDEEM.');
await this.makeRedeemAll(mtx, witnessSize);
break;
}
case 'UPDATE': {
assert(action.length === 2, 'Bad arguments for UPDATE.');
const name = action[0];
const resource = action[1];
assert(args.length === 2, 'Bad arguments for UPDATE.');
const name = args[0];
const resource = args[1];
await this.makeUpdate(name, resource, acct, mtx);
break;
}
case 'RENEW': {
if (action.length === 1) {
const name = action[0];
if (args.length === 1) {
const name = args[0];
await this.makeRenewal(name, acct, mtx);
break;
}
assert(action.length === 0, 'Bad arguments for RENEW.');
assert(args.length === 0, 'Bad arguments for RENEW.');
await this.makeRenewalAll(mtx, witnessSize);
break;
}
case 'TRANSFER': {
assert(action.length === 2, 'Bad arguments for TRANSFER.');
const name = action[0];
const address = action[1];
assert(args.length === 2, 'Bad arguments for TRANSFER.');
const name = args[0];
const address = args[1];
await this.makeTransfer(name, address, acct, mtx);
break;
}
case 'FINALIZE': {
if (action.length === 1) {
const name = action[0];
if (args.length === 1) {
const name = args[0];
await this.makeFinalize(name, acct, mtx);
break;
}
assert(action.length === 0, 'Bad arguments for FINALIZE.');
assert(args.length === 0, 'Bad arguments for FINALIZE.');
await this.makeFinalizeAll(mtx, witnessSize);
break;
}
case 'CANCEL': {
assert(action.length === 1, 'Bad arguments for CANCEL.');
const name = action[0];
assert(args.length === 1, 'Bad arguments for CANCEL.');
const name = args[0];
await this.makeCancel(name, acct, mtx);
break;
}
case 'REVOKE': {
assert(action.length === 1, 'Bad arguments for REVOKE.');
const name = action[0];
assert(args.length === 1, 'Bad arguments for REVOKE.');
const name = args[0];
await this.makeRevoke(name, acct, mtx);
break;
}
default:
throw new Error(`Unknown action type: ${type}`);
throw new Error(`Unknown action type: ${action.type}`);
}
};
// "actions" are arrays that start with a covenant type (or meta-type)
// followed by the arguments expected by the corresponding "make" function.
for (const action of actions) {
assert(action);
assert(typeof action.type === 'string');
const args = action.args || [];
assert(Array.isArray(args), 'Action args must be an array.');
try {
await handleAction(action, args);
} catch (err) {
if (!options.partialFailure)
throw err;
if (action.type === 'BID')
receiveIndex--;
errors.push({
message: err.message,
type: action.type,
error: err,
id: action.id || null
});
}
if (rules.countOpens(mtx) > consensus.MAX_BLOCK_OPENS)
@ -4068,27 +4130,30 @@ class Wallet extends EventEmitter {
throw new Error('Batch output addresses would exceed lookahead.');
}
return mtx;
return {mtx, errors};
}
/**
* Make a batch transaction with multiple actions.
* @param {Array} actions
* @param {Object} options
* @returns {Promise<MTX>}
* @returns {Promise<BatchResult>}
*/
async _createBatch(actions, options) {
const mtx = await this.makeBatch(actions, options);
const {mtx, errors} = await this.makeBatch(actions, options);
await this.fill(mtx, options);
return this.finalize(mtx, options);
return {
mtx: await this.finalize(mtx, options),
errors
};
}
/**
* Make a batch transaction with multiple actions.
* @param {Array} actions
* @param {Object} options
* @returns {Promise<MTX>}
* @returns {Promise<BatchResult>}
*/
async createBatch(actions, options) {
@ -4104,20 +4169,23 @@ class Wallet extends EventEmitter {
* Create and send a batch transaction with multiple actions.
* @param {Array} actions
* @param {Object} options
* @returns {Promise<TX>}
* @returns {Promise<BatchTXResult>}
*/
async _sendBatch(actions, options) {
const passphrase = options ? options.passphrase : null;
const mtx = await this._createBatch(actions, options);
return this.sendMTX(mtx, passphrase);
const {mtx, errors} = await this._createBatch(actions, options);
return {
tx: await this.sendMTX(mtx, passphrase),
errors
};
}
/**
* Create and send a batch transaction with multiple actions.
* @param {Array} actions
* @param {Object} options
* @returns {Promise<TX>}
* @returns {Promise<BatchTXResult>}
*/
async sendBatch(actions, options) {
@ -5462,7 +5530,7 @@ class Wallet extends EventEmitter {
/**
* Get current change address.
* @param {Number} [acct=0]
* @param {Number|String} [acct=0]
* @returns {Promise<Address>}
*/

View file

@ -17,6 +17,8 @@ const Covenant = require('../lib/primitives/covenant');
const {Resource} = require('../lib/dns/resource');
const {forEvent} = require('./util/common');
/** @typedef {import('../lib/wallet/wallet')} Wallet */
const network = Network.get('regtest');
const {
treeInterval,
@ -637,13 +639,16 @@ describe('Wallet Auction', function() {
});
describe('Batch TXs', function() {
let wallet, receive;
/** @type {Wallet} */
let wallet;
let receive;
const hardFee = 12345;
const name1 = rules.grindName(3, 0, network);
const name2 = rules.grindName(4, 0, network);
const name3 = rules.grindName(5, 0, network);
const name4 = rules.grindName(6, 0, network);
const name5 = rules.grindName(7, 0, network);
const res1 = Resource.fromJSON({records: [{type: 'TXT', txt: ['one']}]});
const res2 = Resource.fromJSON({records: [{type: 'TXT', txt: ['two']}]});
@ -696,11 +701,11 @@ describe('Wallet Auction', function() {
});
it('should create multiple OPENs with options', async () => {
const mtx = await wallet.createBatch(
const {mtx, errors} = await wallet.createBatch(
[
['OPEN', name1],
['OPEN', name2],
['OPEN', name3]
{ type: 'OPEN', args: [name1] },
{ type: 'OPEN', args: [name2] },
{ type: 'OPEN', args: [name3] }
],
{
hardFee
@ -708,8 +713,9 @@ describe('Wallet Auction', function() {
);
assert(uniqueAddrs(mtx));
assert.strictEqual(errors.length, 0);
assert.strictEqual(mtx.outputs.length, 4);
let opens = 0;
for (const output of mtx.outputs) {
if (output.covenant.type === rules.types.OPEN)
@ -730,72 +736,176 @@ describe('Wallet Auction', function() {
await assert.rejects(
wallet.sendBatch(
[
['OPEN', 'google'],
['OPEN', name2],
['OPEN', name3]
{ type: 'OPEN', args: ['google'] },
{ type: 'OPEN', args: [name2] },
{ type: 'OPEN', args: [name3] }
]
),
{message: 'Name is reserved: google.'}
);
});
it('should fail if one action is invalid: OPEN reserved (partial)', async () => {
const {mtx, errors} = await wallet.createBatch([
{ type: 'OPEN', args: ['google'], id: 'google-id' },
{ type: 'OPEN', args: [name2] },
{ type: 'OPEN', args: [name3] }
], {
partialFailure: true
});
assert.strictEqual(errors.length, 1);
assert.deepStrictEqual(errors[0], {
id: 'google-id',
type: 'OPEN',
message: 'Name is reserved: google.',
error: new Error('Name is reserved: google.')
});
assert.strictEqual(mtx.outputs.length, 3);
});
it('should fail if one action is invalid: OPEN duplicated', async () => {
await assert.rejects(
wallet.sendBatch(
[
['OPEN', name1],
['OPEN', name1],
['OPEN', name3]
{ type: 'OPEN', args: [name1] },
{ type: 'OPEN', args: [name1] },
{ type: 'OPEN', args: [name3] }
]
),
{message: 'Duplicate name with exclusive action.'}
);
await assert.rejects(
wallet.sendBatch(
[
{ type: 'OPEN', args: [name1] },
{ type: 'OPEN', args: [name1] },
{ type: 'OPEN', args: [name3] }
], {
partialFailure: true
}
),
{message: 'Duplicate name with exclusive action.'}
);
});
it('should fail if one action is invalid: OPEN duplicated (partial)', async () => {
await assert.rejects(
wallet.sendBatch(
[
{ type: 'OPEN', args: [name1] },
{ type: 'OPEN', args: [name1] },
{ type: 'OPEN', args: [name3] }
], {
partialFailure: true
}
),
{message: 'Duplicate name with exclusive action.'}
);
});
it('should fail if one action is invalid: REVEAL before bid', async () => {
await assert.rejects(
wallet.sendBatch(
[
['OPEN', name1],
['OPEN', name1],
['OPEN', name3]
{ type: 'OPEN', args: [name1] },
{ type: 'OPEN', args: [name1] },
{ type: 'OPEN', args: [name3] }
]
),
{message: 'Duplicate name with exclusive action.'}
);
});
it('should fail if one action is invalid: REVEAL before bid (partial)', async () => {
await assert.rejects(
wallet.sendBatch(
[
{ type: 'OPEN', args: [name1] },
{ type: 'OPEN', args: [name1] },
{ type: 'OPEN', args: [name3] }
], {
partialFailure: true
}
),
{message: 'Duplicate name with exclusive action.'}
);
});
it('should fail if one action is invalid: BID early', async () => {
await assert.rejects(
wallet.sendBatch(
[
['BID', name1, 1, 1],
['OPEN', name2],
['OPEN', name3]
{ type: 'BID', args: [name1, 1, 1] },
{ type: 'OPEN', args: [name2] },
{ type: 'OPEN', args: [name3] }
]
),
{message: `Name has not reached the bidding phase yet: ${name1}.`}
);
});
it('should fail if one action is invalid: BID early (partial)', async () => {
const {mtx, errors} = await wallet.createBatch( [
{ type: 'BID', args: [name1, 1, 1], id: name1 },
{ type: 'OPEN', args: [name2] },
{ type: 'OPEN', args: [name3] }
], {
partialFailure: true
});
assert.strictEqual(errors.length, 1);
assert.deepStrictEqual(errors[0], {
id: name1,
type: 'BID',
message: `Name has not reached the bidding phase yet: ${name1}.`,
error: new Error(`Name has not reached the bidding phase yet: ${name1}.`)
});
assert.strictEqual(mtx.outputs.length, 3);
});
it('should fail if one action is invalid: wrong arguments', async () => {
await assert.rejects(
wallet.sendBatch(
[
['BID', name1, 21000000],
['OPEN', name2],
['OPEN', name3]
{ type: 'BID', args: [name1, 21000000] },
{ type: 'OPEN', args: [name2] },
{ type: 'OPEN', args: [name3] }
]
),
{message: 'Bad arguments for BID.'}
);
});
it('should fail if one action is invalid: wrong arguments (partial)', async () => {
const {mtx, errors} = await wallet.createBatch([
{ type: 'BID', args: [name1, 21000000] },
{ type: 'OPEN', args: [name2] },
{ type: 'OPEN', args: [name3] }
], {
partialFailure: true
});
assert.strictEqual(errors.length, 1);
assert.strictEqual(errors[0].error.message, 'Bad arguments for BID.');
delete errors[0].error;
assert.deepStrictEqual(errors[0], {
id: null,
type: 'BID',
message: 'Bad arguments for BID.'
});
assert.strictEqual(mtx.outputs.length, 3);
});
it('should fail if one action is invalid: REVEAL all before bid', async () => {
await assert.rejects(
wallet.sendBatch(
[
['REVEAL']
{ type: 'REVEAL' }
]
),
{message: 'Nothing to do.'}
@ -806,7 +916,7 @@ describe('Wallet Auction', function() {
await assert.rejects(
wallet.sendBatch(
[
['REDEEM']
{ type: 'REDEEM' }
]
),
{message: 'Nothing to do.'}
@ -818,69 +928,125 @@ describe('Wallet Auction', function() {
await assert.rejects(
wallet.sendBatch(
[
['OPEN', name1],
['OPEN', name1],
['OPEN', name3],
['NONE', addr, 1]
{ type: 'OPEN', args: [name1] },
{ type: 'OPEN', args: [name1] },
{ type: 'OPEN', args: [name3] },
{ type: 'NONE', args: [addr, 1] }
]
),
{message: 'Output is dust.'}
);
});
it('should fail if one action is invalid: NONE below dust (partial)', async () => {
const addr = Address.fromProgram(0, Buffer.alloc(20, 0x01)).toString('regtest');
const {mtx, errors} = await wallet.createBatch(
[
{ type: 'OPEN', args: [name1] },
{ type: 'OPEN', args: [name3] },
{ type: 'NONE', args: [addr, 1] }
],
{
partialFailure: true
}
);
assert.strictEqual(errors.length, 1);
assert.deepStrictEqual(errors[0], {
id: null,
type: 'NONE',
message: 'Output is dust.',
error: new Error('Output is dust.')
});
assert.strictEqual(mtx.outputs.length, 3);
});
it('should fail if one action is invalid: unknown action', async () => {
await assert.rejects(
wallet.sendBatch(
[
['OPEN', name1],
['OPEN', name1],
['OPEN', name3],
['open', name4]
{ type: 'OPEN', args: [name1] },
{ type: 'OPEN', args: [name1] },
{ type: 'OPEN', args: [name3] },
{ type: 'open', args: [name4] }
]
),
{message: 'Unknown action type: open'}
);
});
it('should fail if one action is invalid: unknown action (partial)', async () => {
const {mtx, errors} = await wallet.createBatch([
{ type: 'OPEN', args: [name1] },
{ type: 'OPEN', args: [name3] },
{ type: 'open', args: [name4] }
], {
partialFailure: true
});
assert.strictEqual(errors.length, 1);
assert.deepStrictEqual(errors[0], {
id: null,
type: 'open',
message: 'Unknown action type: open',
error: new Error('Unknown action type: open')
});
assert.strictEqual(mtx.outputs.length, 3);
});
describe('Complete auction and diverse-action batches', function() {
const addr = Address.fromProgram(0, Buffer.alloc(20, 0x01)).toString('regtest');
it('3 OPENs and 1 NONE', async () => {
const tx = await wallet.sendBatch(
const {tx, errors} = await wallet.sendBatch(
[
['OPEN', name1],
['OPEN', name2],
['OPEN', name3],
['NONE', addr, 10000]
{ type: 'OPEN', args: [name1] },
{ type: 'OPEN', args: [name2] },
{ type: 'OPEN', args: [name3] },
{ type: 'NONE', args: [addr, 10000] }
]
);
assert(uniqueAddrs(tx));
assert.strictEqual(errors.length, 0);
await mineBlocks(treeInterval + 1);
});
it('4 BIDs', async () => {
const tx = await wallet.sendBatch(
const {tx, errors} = await wallet.sendBatch(
[
['BID', name1, 10000, 20000],
['BID', name1, 10001, 20000], // self-snipe!
['BID', name2, 30000, 40000],
['BID', name3, 50000, 60000]
]
{ type: 'BID', args: [name1, 10000, 20000] },
{ type: 'BID', args: [name1, 10001, 20000] }, // self-snipe!
{ type: 'BID', args: [name2, 30000, 40000] },
{ type: 'BID', args: [name3, 50000, 60000] },
// Handle failed index increments.
{ type: 'BID', args: ['random', -100, -1]},
{ type: 'BID', args: ['random', -100, -1]},
{ type: 'BID', args: ['random', -100]},
{ type: 'OPEN', args: [name5]}
], {
partialFailure: true
}
);
assert(uniqueAddrs(tx));
assert.strictEqual(errors.length, 3);
await mineBlocks(biddingPeriod);
});
it('REVEAL all', async () => {
// Don't send this one
const revealAll = await wallet.createBatch(
const {mtx: revealAll, errors} = await wallet.createBatch(
[
['REVEAL']
{ type: 'REVEAL' }
]
);
assert.strictEqual(revealAll.outputs.length, 5);
assert.strictEqual(errors.length, 0);
let reveals = 0;
for (const output of revealAll.outputs) {
if (output.covenant.type === rules.types.REVEAL)
@ -890,20 +1056,21 @@ describe('Wallet Auction', function() {
});
it('2 REVEALs then 1 REVEAL', async () => {
const tx = await wallet.sendBatch(
const {tx, errors} = await wallet.sendBatch(
[
['REVEAL', name1],
['REVEAL', name2]
{ type: 'REVEAL', args: [name1] },
{ type: 'REVEAL', args: [name2] }
]
);
assert(uniqueAddrs(tx));
assert.strictEqual(errors.length, 0);
// No "could not resolve preferred inputs" error
// because names are being revealed individually.
await wallet.sendBatch(
[
['REVEAL', name3]
{ type: 'REVEAL', args: [name3] }
]
);
await mineBlocks(revealPeriod);
@ -911,13 +1078,15 @@ describe('Wallet Auction', function() {
it('REDEEM all', async () => {
// Don't send this one
const redeemAll = await wallet.createBatch(
const {mtx: redeemAll, errors} = await wallet.createBatch(
[
['REDEEM']
{ type: 'REDEEM' }
]
);
assert.strictEqual(redeemAll.outputs.length, 2);
assert.strictEqual(errors.length, 0);
let redeems = 0;
for (const output of redeemAll.outputs) {
if (output.covenant.type === rules.types.REDEEM)
@ -928,17 +1097,18 @@ describe('Wallet Auction', function() {
it('3 REGISTERs, 1 REDEEM and 1 OPEN', async () => {
// Complete all 4 bids win and/or lose in one TX
const batch1 = await wallet.sendBatch(
const {tx: batch1, errors} = await wallet.sendBatch(
[
['OPEN', name4],
['REDEEM', name1],
['UPDATE', name1, res1],
['UPDATE', name2, res2],
['UPDATE', name3, res3]
{ type: 'OPEN', args: [name4] },
{ type: 'REDEEM', args: [name1] },
{ type: 'UPDATE', args: [name1, res1] },
{ type: 'UPDATE', args: [name2, res2] },
{ type: 'UPDATE', args: [name3, res3] }
]
);
assert(uniqueAddrs(batch1));
assert.strictEqual(errors.length, 0);
// Unlinked covenant (OPEN) was listed first but
// should be sorted last with the change output (NONE).
@ -953,16 +1123,18 @@ describe('Wallet Auction', function() {
version: 31,
hash: Buffer.from([1, 2, 3])
});
const tx = await wallet.sendBatch(
const {tx, errors} = await wallet.sendBatch(
[
['TRANSFER', name1, nullAddr],
['TRANSFER', name2, nullAddr],
['TRANSFER', name3, nullAddr],
['BID', name4, 70000, 80000]
{ type: 'TRANSFER', args: [name1, nullAddr] },
{ type: 'TRANSFER', args: [name2, nullAddr] },
{ type: 'TRANSFER', args: [name3, nullAddr] },
{ type: 'BID', args: [name4, 70000, 80000] }
]
);
assert(uniqueAddrs(tx));
assert.strictEqual(errors.length, 0);
// True for regtest but not mainnet,
// should allow both REVEAL and FINALIZE
@ -972,16 +1144,17 @@ describe('Wallet Auction', function() {
});
it('1 FINALIZE, 1 CANCEL, 1 REVOKE and 1 REVEAL', async () => {
const tx = await wallet.sendBatch(
const {tx, errors} = await wallet.sendBatch(
[
['FINALIZE', name1],
['CANCEL', name2],
['REVOKE', name3],
['REVEAL', name4]
{ type: 'FINALIZE', args: [name1] },
{ type: 'CANCEL', args: [name2] },
{ type: 'REVOKE', args: [name3] },
{ type: 'REVEAL', args: [name4] }
]
);
assert(uniqueAddrs(tx));
assert.strictEqual(errors.length, 0);
// Should allow for both REGISTER and re-open revoked name
assert(auctionMaturity > revealPeriod);
@ -990,14 +1163,15 @@ describe('Wallet Auction', function() {
});
it('1 revoked name re-OPEN and 1 REGISTER', async () => {
const batch2 = await wallet.sendBatch(
const {tx: batch2, errors} = await wallet.sendBatch(
[
['OPEN', name3], // and the cycle begins again...
['UPDATE', name4, res4]
{ type: 'OPEN', args: [name3] }, // and the cycle begins again...
{ type: 'UPDATE', args: [name4, res4] }
]
);
assert(uniqueAddrs(batch2));
assert.strictEqual(errors.length, 0);
// Linked covenant (UPDATE) was listed last but should be sorted first.
assert.strictEqual(batch2.outputs[0].covenant.type, rules.types.REGISTER);
@ -1010,12 +1184,13 @@ describe('Wallet Auction', function() {
const actions = [];
for (let i = 0; i < 10; i++) {
const addr = Address.fromProgram(0, Buffer.alloc(20, i + 1));
actions.push(['NONE', addr, 10000]);
actions.push({ type: 'NONE', args: [addr, 10000] });
}
const batch = await wallet.sendBatch(actions, {hardFee: 1000});
const {tx: batch, errors} = await wallet.sendBatch(actions, {hardFee: 1000});
assert.strictEqual(batch.outputs.length, 11);
assert.strictEqual(errors.length, 0);
// Mine to some other wallet so reward doesn't affect our balance
receive = new Address();
@ -1056,13 +1231,15 @@ describe('Wallet Auction', function() {
});
it('2 RENEW', async () => {
await wallet.sendBatch(
const {errors} = await wallet.sendBatch(
[
['RENEW', name2],
['RENEW', name4]
{ type: 'RENEW', args: [name2] },
{ type: 'RENEW', args: [name4] }
]
);
assert.strictEqual(errors.length, 0);
await mineBlocks(1);
const ns1 = await chain.db.getNameStateByName(name1);
const ns2 = await chain.db.getNameStateByName(name2);
@ -1094,14 +1271,16 @@ describe('Wallet Auction', function() {
const wtxs = await wallet.toDetails(txs);
for (const wtx of wtxs) {
for (const output of wtx.outputs)
for (const output of wtx.outputs) {
if ( output.path
&& output.path.account === 0
&& output.path.branch === 0 // receive
) {
addrIndexes[output.path.index]++;
}
}
}
// Ensure every receive address was used at least once
assert(addrIndexes.indexOf(0) === -1);
});
@ -1130,14 +1309,16 @@ describe('Wallet Auction', function() {
it('should OPEN', async () => {
name = rules.grindName(4, chain.height, network);
await wallet.sendBatch([['OPEN', name]]);
await wallet.sendBatch([
{ type: 'OPEN', args: [name] }
]);
await mineBlocks(treeInterval + 1);
});
it('should not batch too many BIDs', async () => {
const batch = [];
for (let i = 201; i > 0; i--)
batch.push(['BID', name, i * 1000, i * 1000]);
batch.push({ type: 'BID', args: [name, i * 1000, i * 1000] });
await assert.rejects(
wallet.sendBatch(batch),
@ -1148,19 +1329,19 @@ describe('Wallet Auction', function() {
it('should batch BIDs', async () => {
let batch = [];
for (let i = 200; i > 0; i--)
batch.push(['BID', name, i * 1000, i * 1000]);
batch.push({ type: 'BID', args: [name, i * 1000, i * 1000] });
await wallet.sendBatch(batch);
batch = [];
for (let i = 200; i > 0; i--)
batch.push(['BID', name, i * 1001, i * 1001]);
batch.push({ type: 'BID', args: [name, i * 1001, i * 1001] });
await wallet.sendBatch(batch);
batch = [];
for (let i = 200; i > 0; i--)
batch.push(['BID', name, i * 1002, i * 1002]);
batch.push({ type: 'BID', args: [name, i * 1002, i * 1002] });
await wallet.sendBatch(batch);
batch = [];
for (let i = 150; i > 0; i--)
batch.push(['BID', name, i * 1003, i * 1003]);
batch.push({ type: 'BID', args: [name, i * 1003, i * 1003] });
await wallet.sendBatch(batch);
await mineBlocks(biddingPeriod);
@ -1175,7 +1356,7 @@ describe('Wallet Auction', function() {
it('should create batch just under weight limit', async () => {
// Start with the batch we would normally make
const mtx = await wallet.createBatch([['REVEAL']]);
const {mtx} = await wallet.createBatch([{ type: 'REVEAL' }]);
// Find a spendable coin
const coins = await wallet.getCoins();
@ -1213,12 +1394,12 @@ describe('Wallet Auction', function() {
it('should REVEAL all in several batches', async () => {
let reveals = 0;
const mtx1 = await wallet.createBatch([['REVEAL']]);
const {mtx: mtx1} = await wallet.createBatch([{ type: 'REVEAL' }]);
assert(mtx1.changeIndex >= 0);
reveals += mtx1.outputs.length - 1;
await wdb.addTX(mtx1.toTX());
const mtx2 = await wallet.createBatch([['REVEAL']]);
const {mtx: mtx2} = await wallet.createBatch([{ type: 'REVEAL' }]);
assert(mtx2.changeIndex >= 0);
reveals += mtx2.outputs.length - 1;
await wdb.addTX(mtx2.toTX());
@ -1239,12 +1420,12 @@ describe('Wallet Auction', function() {
it('should REDEEM all in several batches', async () => {
let reveals = 0;
const mtx1 = await wallet.createBatch([['REDEEM']]);
const {mtx: mtx1} = await wallet.createBatch([{ type: 'REDEEM' }]);
assert(mtx1.changeIndex >= 0);
reveals += mtx1.outputs.length - 1;
await wdb.addTX(mtx1.toTX());
const mtx2 = await wallet.createBatch([['REDEEM']]);
const {mtx: mtx2} = await wallet.createBatch([{ type: 'REDEEM' }]);
assert(mtx2.changeIndex >= 0);
reveals += mtx2.outputs.length - 1;
await wdb.addTX(mtx2.toTX());
@ -1292,7 +1473,7 @@ describe('Wallet Auction', function() {
it('should not batch too many OPENs', async () => {
const batch = [];
for (let i = 0; i < consensus.MAX_BLOCK_OPENS + 1; i++)
batch.push(['OPEN', names[i]]);
batch.push({ type: 'OPEN', args: [names[i]] });
await assert.rejects(
wallet.createBatch(batch),
@ -1305,7 +1486,7 @@ describe('Wallet Auction', function() {
for (let i = 1; i <= 8; i++) {
const batch = [];
for (let j = 1; j <= 100; j++) {
batch.push(['OPEN', names[count++]]);
batch.push({ type: 'OPEN', args: [names[count++]] });
}
await wallet.sendBatch(batch);
await mineBlocks(1);
@ -1319,8 +1500,8 @@ describe('Wallet Auction', function() {
const batch = [];
for (let j = 1; j <= 100; j++) {
batch.push(
['BID', names[count], 10000, 10000],
['BID', names[count++], 10000, 10000]
{ type: 'BID', args: [names[count], 10000, 10000]},
{ type: 'BID', args: [names[count++], 10000, 10000]}
);
}
await wallet.sendBatch(batch);
@ -1334,7 +1515,12 @@ describe('Wallet Auction', function() {
let reveals = 0;
for (;;) {
try {
const tx = await wallet.sendBatch([['REVEAL'], ['REDEEM'], ['RENEW'], ['FINALIZE']]);
const {tx} = await wallet.sendBatch([
{ type: 'REVEAL' },
{ type: 'REDEEM' },
{ type: 'RENEW' },
{ type: 'FINALIZE' }
]);
reveals += tx.outputs.length - 1; // Don't count change output
} catch (e) {
assert.strictEqual(e.message, 'Nothing to do.');
@ -1351,7 +1537,13 @@ describe('Wallet Auction', function() {
let redeems = 0;
for (;;) {
try {
const tx = await wallet.sendBatch([['REVEAL'], ['REDEEM'], ['RENEW'], ['FINALIZE']]);
const {tx} = await wallet.sendBatch([
{ type: 'REVEAL' },
{ type: 'REDEEM' },
{ type: 'RENEW' },
{ type: 'FINALIZE' }
]);
redeems += tx.outputs.length - 1; // Don't count change output
} catch (e) {
assert.strictEqual(e.message, 'Nothing to do.');
@ -1367,7 +1559,7 @@ describe('Wallet Auction', function() {
for (let i = 1; i <= 8; i++) {
const batch = [];
for (let j = 1; j <= 100; j++) {
batch.push(['UPDATE', names[count++], new Resource()]);
batch.push({ type: 'UPDATE', args: [names[count++], new Resource()]});
}
await wallet.sendBatch(batch);
await mineBlocks(1);
@ -1390,7 +1582,7 @@ describe('Wallet Auction', function() {
it('should not batch too many UPDATEs', async () => {
const batch = [];
for (let i = 0; i < consensus.MAX_BLOCK_UPDATES + 1; i++)
batch.push(['UPDATE', names[i], new Resource()]);
batch.push({ type: 'UPDATE', args: [names[i], new Resource()]});
await assert.rejects(
wallet.createBatch(batch),
@ -1406,7 +1598,12 @@ describe('Wallet Auction', function() {
);
await assert.rejects(
wallet.sendBatch([['REVEAL'], ['REDEEM'], ['RENEW'], ['FINALIZE']]),
wallet.sendBatch([
{ type: 'REVEAL' },
{ type: 'REDEEM' },
{ type: 'RENEW' },
{ type: 'FINALIZE' }
]),
{message: 'Nothing to do.'}
);
});
@ -1414,7 +1611,7 @@ describe('Wallet Auction', function() {
it('should not batch too many RENEWs', async () => {
const batch = [];
for (let i = 0; i < consensus.MAX_BLOCK_RENEWALS + 1; i++)
batch.push(['RENEW', names[i]]);
batch.push({ type: 'RENEW', args: [names[i]]});
await assert.rejects(
wallet.createBatch(batch),
@ -1427,7 +1624,12 @@ describe('Wallet Auction', function() {
let renewals = 0;
for (;;) {
const tx = await wallet.sendBatch([['REVEAL'], ['REDEEM'], ['RENEW'], ['FINALIZE']]);
const {tx} = await wallet.sendBatch([
{ type: 'REVEAL' },
{ type: 'REDEEM' },
{ type: 'RENEW' },
{ type: 'FINALIZE' }
]);
await mineBlocks(1);
if (!renewals) {
@ -1446,7 +1648,7 @@ describe('Wallet Auction', function() {
it('should not batch too many TRANSFERs', async () => {
const batch = [];
for (const name of names)
batch.push(['TRANSFER', name, new Address()]);
batch.push({ type: 'TRANSFER', args: [name, new Address()]});
await assert.rejects(
wallet.createBatch(batch),
@ -1460,7 +1662,7 @@ describe('Wallet Auction', function() {
for (let i = 1; i <= 8; i++) {
const batch = [];
for (let j = 1; j <= 100; j++) {
batch.push(['TRANSFER', names[count++], addr]);
batch.push({ type: 'TRANSFER', args: [names[count++], addr]});
}
await wallet.sendBatch(batch);
await mineBlocks(1);
@ -1471,7 +1673,12 @@ describe('Wallet Auction', function() {
await mineBlocks(network.names.lockupPeriod - 9);
await assert.rejects(
wallet.sendBatch([['REVEAL'], ['REDEEM'], ['RENEW'], ['FINALIZE']]),
wallet.sendBatch([
{ type: 'REVEAL' },
{ type: 'REDEEM' },
{ type: 'RENEW' },
{ type: 'FINALIZE' }
]),
{message: 'Nothing to do.'}
);
});
@ -1481,7 +1688,12 @@ describe('Wallet Auction', function() {
let finalizes = 0;
for (;;) {
const tx = await wallet.sendBatch([['REVEAL'], ['REDEEM'], ['RENEW'], ['FINALIZE']]);
const {tx} = await wallet.sendBatch([
{ type: 'REVEAL' },
{ type: 'REDEEM' },
{ type: 'RENEW' },
{ type: 'FINALIZE' }
]);
await mineBlocks(1);
finalizes += tx.outputs.length - 1; // Don't count change output
@ -1493,7 +1705,12 @@ describe('Wallet Auction', function() {
it('should have nothing to do', async () => {
await assert.rejects(
wallet.sendBatch([['REVEAL'], ['REDEEM'], ['RENEW'], ['FINALIZE']]),
wallet.sendBatch([
{ type: 'REVEAL' },
{ type: 'REDEEM' },
{ type: 'RENEW' },
{ type: 'FINALIZE' }
]),
{message: 'Nothing to do.'}
);
});

View file

@ -565,17 +565,17 @@ describe('Wallet Balance', function() {
const {nextAddr} = getAheadAddr(account, ahead);
await primary.sendBatch([
['OPEN', name1],
['OPEN', name2]
{ type: 'OPEN', args: [name1] },
{ type: 'OPEN', args: [name2] }
]);
await mineBlocks(openingPeriod);
const txOpts = { hardFee: HARD_FEE };
// all three bids are there.
const bidMTX = await wallet.createBatch([
['BID', name1, BID_AMOUNT_1, BLIND_AMOUNT_1],
['BID', name2, BID_AMOUNT_2, BLIND_AMOUNT_2]
const {mtx: bidMTX} = await wallet.createBatch([
{ type: 'BID', args: [name1, BID_AMOUNT_1, BLIND_AMOUNT_1] },
{ type: 'BID', args: [name2, BID_AMOUNT_2, BLIND_AMOUNT_2] }
], txOpts);
assert.strictEqual(bidMTX.outputs[0].covenant.type, types.BID);
@ -604,16 +604,16 @@ describe('Wallet Balance', function() {
await primary.sendReveal(name2);
await wallet.sendBatch([
['REVEAL', name1],
['REVEAL', name2]
{ type: 'REVEAL', args: [name1] },
{ type: 'REVEAL', args: [name2] }
], txOpts);
await mineBlocks(revealPeriod);
if (register !== false) {
await wallet.sendBatch([
['UPDATE', name1, EMPTY_RS],
['UPDATE', name2, EMPTY_RS]
{ type: 'UPDATE', args: [name1, EMPTY_RS] },
{ type: 'UPDATE', args: [name2, EMPTY_RS] }
], {
hardFee: HARD_FEE
});
@ -1018,9 +1018,9 @@ describe('Wallet Balance', function() {
const {nextAddr} = getAheadAddr(account, ahead);
const txOpts = { hardFee: HARD_FEE };
const bidMTX = await wallet.createBatch([
['BID', name, BID_AMOUNT_1, BLIND_AMOUNT_1],
['BID', name, BID_AMOUNT_2, BLIND_AMOUNT_2]
const {mtx: bidMTX} = await wallet.createBatch([
{ type: 'BID', args: [name, BID_AMOUNT_1, BLIND_AMOUNT_1] },
{ type: 'BID', args: [name, BID_AMOUNT_2, BLIND_AMOUNT_2] }
], txOpts);
assert.strictEqual(bidMTX.outputs[0].covenant.type, types.BID);
@ -1111,9 +1111,9 @@ describe('Wallet Balance', function() {
const {nextAddr} = getAheadAddr(account, ahead);
const txOpts = { hardFee: HARD_FEE };
const bidMTX = await primary.createBatch([
['BID', name, BID_AMOUNT_1, BLIND_AMOUNT_1],
['BID', name, BID_AMOUNT_2, BLIND_AMOUNT_2]
const {mtx: bidMTX} = await primary.createBatch([
{ type: 'BID', args: [name, BID_AMOUNT_1, BLIND_AMOUNT_1] },
{ type: 'BID', args: [name, BID_AMOUNT_2, BLIND_AMOUNT_2] }
], txOpts);
assert.strictEqual(bidMTX.outputs[0].covenant.type, types.BID);
@ -1208,9 +1208,9 @@ describe('Wallet Balance', function() {
const addr1 = getAheadAddr(altAccount, -altAccount.lookahead);
const addr2 = getAheadAddr(altAccount, ahead);
const bidMTX = await wallet.createBatch([
['BID', name, BID_AMOUNT_1, BLIND_AMOUNT_1],
['BID', name, BID_AMOUNT_2, BLIND_AMOUNT_2]
const {mtx: bidMTX} = await wallet.createBatch([
{ type: 'BID', args: [name, BID_AMOUNT_1, BLIND_AMOUNT_1] },
{ type: 'BID', args: [name, BID_AMOUNT_2, BLIND_AMOUNT_2] }
], txOpts);
assert.strictEqual(bidMTX.outputs[0].covenant.type, types.BID);
@ -1393,9 +1393,9 @@ describe('Wallet Balance', function() {
await primary.sendOpen(name, false);
await mineBlocks(openingPeriod);
const bidMTX = await clone.createBatch([
['BID', name, BID_AMOUNT_1, BLIND_AMOUNT_1],
['BID', name, BID_AMOUNT_2, BLIND_AMOUNT_2]
const {mtx: bidMTX} = await clone.createBatch([
{ type: 'BID', args: [name, BID_AMOUNT_1, BLIND_AMOUNT_1] },
{ type: 'BID', args: [name, BID_AMOUNT_2, BLIND_AMOUNT_2] }
], txOpts);
assert.strictEqual(bidMTX.outputs[0].covenant.type, types.BID);
@ -1511,8 +1511,8 @@ describe('Wallet Balance', function() {
await mineBlocks(openingPeriod);
await wallet.sendBatch([
['BID', name, BID_AMOUNT_1, BLIND_AMOUNT_1],
['BID', name, BID_AMOUNT_2, BLIND_AMOUNT_2]
{ type: 'BID', args: [name, BID_AMOUNT_1, BLIND_AMOUNT_1] },
{ type: 'BID', args: [name, BID_AMOUNT_2, BLIND_AMOUNT_2] }
], txOpts);
await mineBlocks(biddingPeriod);
};
@ -1738,8 +1738,8 @@ describe('Wallet Balance', function() {
await primary.sendOpen(name, false);
await mineBlocks(openingPeriod);
await primary.sendBatch([
['BID', name, BID_AMOUNT_1, BLIND_AMOUNT_1],
['BID', name, BID_AMOUNT_2, BLIND_AMOUNT_2]
{ type: 'BID', args: [name, BID_AMOUNT_1, BLIND_AMOUNT_1] },
{ type: 'BID', args: [name, BID_AMOUNT_2, BLIND_AMOUNT_2] }
]);
await mineBlocks(biddingPeriod);
};
@ -1840,8 +1840,8 @@ describe('Wallet Balance', function() {
const addr1 = getAheadAddr(cloneAccount, ahead);
await primary.sendBatch([
['OPEN', name1],
['OPEN', name2]
{ type: 'OPEN', args: [name1] },
{ type: 'OPEN', args: [name2] }
]);
await mineBlocks(openingPeriod);
@ -1852,9 +1852,9 @@ describe('Wallet Balance', function() {
const txOpts = { hardFee: HARD_FEE };
// all three bids are there.
const bidMTX = await clone.createBatch([
['BID', name1, BID_AMOUNT_1, BLIND_AMOUNT_1],
['BID', name2, BID_AMOUNT_2, BLIND_AMOUNT_2]
const {mtx: bidMTX} = await clone.createBatch([
{ type: 'BID', args: [name1, BID_AMOUNT_1, BLIND_AMOUNT_1] },
{ type: 'BID', args: [name2, BID_AMOUNT_2, BLIND_AMOUNT_2] }
], txOpts);
assert.strictEqual(bidMTX.outputs[0].covenant.type, types.BID);
@ -1873,8 +1873,8 @@ describe('Wallet Balance', function() {
await primary.sendReveal(name2);
await clone.sendBatch([
['REVEAL', name1],
['REVEAL', name2]
{ type: 'REVEAL', args: [name1] },
{ type: 'REVEAL', args: [name2] }
], txOpts);
await mineBlocks(revealPeriod + 1);
@ -1882,8 +1882,8 @@ describe('Wallet Balance', function() {
const sendRedeems = async (wallet, clone, ahead) => {
await clone.sendBatch([
['REDEEM', name1],
['REDEEM', name2]
{ type: 'REDEEM', args: [name1] },
{ type: 'REDEEM', args: [name2] }
], {
hardFee: HARD_FEE
});
@ -1984,8 +1984,8 @@ describe('Wallet Balance', function() {
const sendRedeems = async (wallet, clone, ahead) => {
await clone.sendBatch([
['UPDATE', name1, EMPTY_RS],
['UPDATE', name2, EMPTY_RS]
{ type: 'UPDATE', args: [name1, EMPTY_RS] },
{ type: 'UPDATE', args: [name2, EMPTY_RS] }
], {
hardFee: HARD_FEE
});
@ -2124,8 +2124,8 @@ describe('Wallet Balance', function() {
const sendUpdates = async (wallet, clone) => {
await clone.sendBatch([
['UPDATE', name1, EMPTY_RS],
['UPDATE', name2, EMPTY_RS]
{ type: 'UPDATE', args: [name1, EMPTY_RS] },
{ type: 'UPDATE', args: [name2, EMPTY_RS] }
], {
hardFee: HARD_FEE
});
@ -2165,8 +2165,8 @@ describe('Wallet Balance', function() {
const sendRevokes = async (wallet, clone) => {
await clone.sendBatch([
['REVOKE', name1],
['REVOKE', name2]
{ type: 'REVOKE', args: [name1] },
{ type: 'REVOKE', args: [name2] }
], {
hardFee: HARD_FEE
});
@ -2205,8 +2205,8 @@ describe('Wallet Balance', function() {
const sendRenews = async (wallet, clone) => {
await mineBlocks(treeInterval);
await clone.sendBatch([
['RENEW', name1],
['RENEW', name2]
{ type: 'RENEW', args: [name1] },
{ type: 'RENEW', args: [name2] }
], {
hardFee: HARD_FEE
});
@ -2244,8 +2244,8 @@ describe('Wallet Balance', function() {
const sendTransfers = async (wallet, clone) => {
await clone.sendBatch([
['TRANSFER', name1, await primary.receiveAddress()],
['TRANSFER', name2, await primary.receiveAddress()]
{ type: 'TRANSFER', args: [name1, await primary.receiveAddress()] },
{ type: 'TRANSFER', args: [name2, await primary.receiveAddress()] }
], {
hardFee: HARD_FEE
});
@ -2281,8 +2281,8 @@ describe('Wallet Balance', function() {
name2 = names[1];
await clone.sendBatch([
['TRANSFER', name1, await primary.receiveAddress()],
['TRANSFER', name2, await primary.receiveAddress()]
{ type: 'TRANSFER', args: [name1, await primary.receiveAddress()] },
{ type: 'TRANSFER', args: [name2, await primary.receiveAddress()] }
], {
hardFee: HARD_FEE
});
@ -2292,8 +2292,8 @@ describe('Wallet Balance', function() {
const sendFinalizes = async (wallet, clone) => {
await clone.sendBatch([
['FINALIZE', name1],
['FINALIZE', name2]
{ type: 'FINALIZE', args: [name1] },
{ type: 'FINALIZE', args: [name2] }
], {
hardFee: HARD_FEE
});
@ -2383,8 +2383,8 @@ describe('Wallet Balance', function() {
name2 = names[1];
await clone.sendBatch([
['TRANSFER', name1, recv],
['TRANSFER', name2, nextAddr]
{ type: 'TRANSFER', args: [name1, recv] },
{ type: 'TRANSFER', args: [name2, nextAddr] }
], {
hardFee: HARD_FEE
});
@ -2394,8 +2394,8 @@ describe('Wallet Balance', function() {
const sendFinalizes = async (wallet, clone) => {
await clone.sendBatch([
['FINALIZE', name1],
['FINALIZE', name2]
{ type: 'FINALIZE', args: [name1] },
{ type: 'FINALIZE', args: [name2] }
], {
hardFee: HARD_FEE
});

View file

@ -75,11 +75,11 @@ describe('Wallet Import Nonce', function () {
it('should bid with sendbatch', async () => {
const batch = [
['BID', NAME, BIDS[1].value, BIDS[1].lockup],
['BID', NAME, BIDS[2].value, BIDS[2].lockup]
{ type: 'BID', args: [NAME, BIDS[1].value, BIDS[1].lockup]},
{ type: 'BID', args: [NAME, BIDS[2].value, BIDS[2].lockup]}
];
const bidTx = await walletA.sendBatch(batch);
const {tx: bidTx} = await walletA.sendBatch(batch);
// Save address for importnonce later
for (const output of bidTx.outputs) {