fix: Squash budget bugs
This commit is contained in:
parent
c99d8dd15c
commit
87c1becbef
|
@ -9,7 +9,6 @@ const solanaWeb3 = require('..');
|
|||
|
||||
const account1 = new solanaWeb3.Account();
|
||||
const account2 = new solanaWeb3.Account();
|
||||
const contractFunds = new solanaWeb3.Account();
|
||||
const contractState = new solanaWeb3.Account();
|
||||
|
||||
let url;
|
||||
|
@ -23,28 +22,20 @@ function showBalance() {
|
|||
return Promise.all([
|
||||
connection.getBalance(account1.publicKey),
|
||||
connection.getBalance(account2.publicKey),
|
||||
connection.getBalance(contractFunds.publicKey),
|
||||
connection.getBalance(contractState.publicKey),
|
||||
]).then(
|
||||
([fromBalance, toBalance, contractFundsBalance, contractStateBalance]) => {
|
||||
console.log(
|
||||
`Account1: ${account1.publicKey} has a balance of ${fromBalance}`,
|
||||
);
|
||||
console.log(
|
||||
`Account2: ${account2.publicKey} has a balance of ${toBalance}`,
|
||||
);
|
||||
console.log(
|
||||
`Contract Funds: ${
|
||||
contractFunds.publicKey
|
||||
} has a balance of ${contractFundsBalance}`,
|
||||
);
|
||||
console.log(
|
||||
`Contract State: ${
|
||||
contractState.publicKey
|
||||
} has a balance of ${contractStateBalance}`,
|
||||
);
|
||||
},
|
||||
);
|
||||
]).then(([fromBalance, toBalance, contractStateBalance]) => {
|
||||
console.log(
|
||||
`Account1: ${account1.publicKey} has a balance of ${fromBalance}`,
|
||||
);
|
||||
console.log(
|
||||
`Account2: ${account2.publicKey} has a balance of ${toBalance}`,
|
||||
);
|
||||
console.log(
|
||||
`Contract State: ${
|
||||
contractState.publicKey
|
||||
} has a balance of ${contractStateBalance}`,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function confirmTransaction(signature) {
|
||||
|
@ -52,6 +43,10 @@ function confirmTransaction(signature) {
|
|||
return connection.getSignatureStatus(signature).then(confirmation => {
|
||||
if (confirmation && 'Ok' in confirmation) {
|
||||
console.log('Transaction confirmed');
|
||||
} else if (confirmation) {
|
||||
throw new Error(
|
||||
`Transaction was not confirmed (${JSON.stringify(confirmation.Err)})`,
|
||||
);
|
||||
} else {
|
||||
throw new Error(`Transaction was not confirmed (${confirmation})`);
|
||||
}
|
||||
|
@ -68,36 +63,10 @@ function airDrop() {
|
|||
showBalance()
|
||||
.then(airDrop)
|
||||
.then(showBalance)
|
||||
.then(() => {
|
||||
console.log(`\n== Creating account for the contract funds`);
|
||||
const transaction = solanaWeb3.SystemProgram.createAccount(
|
||||
account1.publicKey,
|
||||
contractFunds.publicKey,
|
||||
50, // number of lamports to transfer
|
||||
0,
|
||||
solanaWeb3.BudgetProgram.programId,
|
||||
);
|
||||
return connection.sendTransaction(transaction, account1);
|
||||
})
|
||||
.then(confirmTransaction)
|
||||
.then(showBalance)
|
||||
.then(() => {
|
||||
console.log(`\n== Creating account for the contract state`);
|
||||
const transaction = solanaWeb3.SystemProgram.createAccount(
|
||||
account1.publicKey,
|
||||
contractState.publicKey,
|
||||
1, // account1 pays 1 lamport to hold the contract state
|
||||
solanaWeb3.BudgetProgram.space,
|
||||
solanaWeb3.BudgetProgram.programId,
|
||||
);
|
||||
return connection.sendTransaction(transaction, account1);
|
||||
})
|
||||
.then(confirmTransaction)
|
||||
.then(showBalance)
|
||||
.then(() => {
|
||||
console.log(`\n== Initializing contract`);
|
||||
const transaction = solanaWeb3.BudgetProgram.pay(
|
||||
contractFunds.publicKey,
|
||||
account1.publicKey,
|
||||
contractState.publicKey,
|
||||
account2.publicKey,
|
||||
50,
|
||||
|
@ -106,7 +75,11 @@ showBalance()
|
|||
new Date('2050'),
|
||||
),
|
||||
);
|
||||
return connection.sendTransaction(transaction, contractFunds);
|
||||
return solanaWeb3.sendAndConfirmTransaction(
|
||||
connection,
|
||||
transaction,
|
||||
account1,
|
||||
);
|
||||
})
|
||||
.then(confirmTransaction)
|
||||
.then(showBalance)
|
||||
|
@ -118,7 +91,11 @@ showBalance()
|
|||
account2.publicKey,
|
||||
new Date('2050'),
|
||||
);
|
||||
return connection.sendTransaction(transaction, account1);
|
||||
return solanaWeb3.sendAndConfirmTransaction(
|
||||
connection,
|
||||
transaction,
|
||||
account1,
|
||||
);
|
||||
})
|
||||
.then(confirmTransaction)
|
||||
.then(showBalance)
|
||||
|
|
|
@ -8,7 +8,6 @@ const solanaWeb3 = require('..');
|
|||
|
||||
const account1 = new solanaWeb3.Account();
|
||||
const account2 = new solanaWeb3.Account();
|
||||
const contractFunds = new solanaWeb3.Account();
|
||||
const contractState = new solanaWeb3.Account();
|
||||
|
||||
const approver1 = new solanaWeb3.Account();
|
||||
|
@ -24,37 +23,34 @@ function showBalance() {
|
|||
return Promise.all([
|
||||
connection.getBalance(account1.publicKey),
|
||||
connection.getBalance(account2.publicKey),
|
||||
connection.getBalance(contractFunds.publicKey),
|
||||
connection.getBalance(contractState.publicKey),
|
||||
]).then(
|
||||
([fromBalance, toBalance, contractFundsBalance, contractStateBalance]) => {
|
||||
console.log(
|
||||
`Account1: ${account1.publicKey} has a balance of ${fromBalance}`,
|
||||
);
|
||||
console.log(
|
||||
`Account2: ${account2.publicKey} has a balance of ${toBalance}`,
|
||||
);
|
||||
console.log(
|
||||
`Contract Funds: ${
|
||||
contractFunds.publicKey
|
||||
} has a balance of ${contractFundsBalance}`,
|
||||
);
|
||||
console.log(
|
||||
`Contract State: ${
|
||||
contractState.publicKey
|
||||
} has a balance of ${contractStateBalance}`,
|
||||
);
|
||||
},
|
||||
);
|
||||
]).then(([fromBalance, toBalance, contractStateBalance]) => {
|
||||
console.log(
|
||||
`Account1: ${account1.publicKey} has a balance of ${fromBalance}`,
|
||||
);
|
||||
console.log(
|
||||
`Account2: ${account2.publicKey} has a balance of ${toBalance}`,
|
||||
);
|
||||
console.log(
|
||||
`Contract State: ${
|
||||
contractState.publicKey
|
||||
} has a balance of ${contractStateBalance}`,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function confirmTransaction(signature) {
|
||||
console.log('Confirming transaction:', signature);
|
||||
return connection.getSignatureStatus(signature).then(confirmation => {
|
||||
if (confirmation !== 'Confirmed') {
|
||||
if (confirmation && 'Ok' in confirmation) {
|
||||
console.log('Transaction confirmed');
|
||||
} else if (confirmation) {
|
||||
throw new Error(
|
||||
`Transaction was not confirmed (${JSON.stringify(confirmation.Err)})`,
|
||||
);
|
||||
} else {
|
||||
throw new Error(`Transaction was not confirmed (${confirmation})`);
|
||||
}
|
||||
console.log('Transaction confirmed');
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -88,43 +84,21 @@ showBalance()
|
|||
})
|
||||
.then(confirmTransaction)
|
||||
.then(showBalance)
|
||||
.then(() => {
|
||||
console.log(`\n== Creating account for the contract funds`);
|
||||
const transaction = solanaWeb3.SystemProgram.createAccount(
|
||||
account1.publicKey,
|
||||
contractFunds.publicKey,
|
||||
50, // number of lamports to transfer
|
||||
0,
|
||||
solanaWeb3.BudgetProgram.programId,
|
||||
);
|
||||
return connection.sendTransaction(transaction, account1);
|
||||
})
|
||||
.then(confirmTransaction)
|
||||
.then(showBalance)
|
||||
.then(() => {
|
||||
console.log(`\n== Creating account for the contract state`);
|
||||
const transaction = solanaWeb3.SystemProgram.createAccount(
|
||||
account1.publicKey,
|
||||
contractState.publicKey,
|
||||
1, // account1 pays 1 lamport to hold the contract state
|
||||
solanaWeb3.BudgetProgram.space,
|
||||
solanaWeb3.BudgetProgram.programId,
|
||||
);
|
||||
return connection.sendTransaction(transaction, account1);
|
||||
})
|
||||
.then(confirmTransaction)
|
||||
.then(showBalance)
|
||||
.then(() => {
|
||||
console.log(`\n== Initializing contract`);
|
||||
const transaction = solanaWeb3.BudgetProgram.payOnBoth(
|
||||
contractFunds.publicKey,
|
||||
account1.publicKey,
|
||||
contractState.publicKey,
|
||||
account2.publicKey,
|
||||
50,
|
||||
solanaWeb3.BudgetProgram.signatureCondition(approver1.publicKey),
|
||||
solanaWeb3.BudgetProgram.signatureCondition(approver2.publicKey),
|
||||
);
|
||||
return connection.sendTransaction(transaction, contractFunds);
|
||||
return solanaWeb3.sendAndConfirmTransaction(
|
||||
connection,
|
||||
transaction,
|
||||
account1,
|
||||
);
|
||||
})
|
||||
.then(confirmTransaction)
|
||||
.then(showBalance)
|
||||
|
@ -135,7 +109,11 @@ showBalance()
|
|||
contractState.publicKey,
|
||||
account2.publicKey,
|
||||
);
|
||||
return connection.sendTransaction(transaction, approver1);
|
||||
return solanaWeb3.sendAndConfirmTransaction(
|
||||
connection,
|
||||
transaction,
|
||||
approver1,
|
||||
);
|
||||
})
|
||||
.then(confirmTransaction)
|
||||
.then(showBalance)
|
||||
|
@ -146,7 +124,11 @@ showBalance()
|
|||
contractState.publicKey,
|
||||
account2.publicKey,
|
||||
);
|
||||
return connection.sendTransaction(transaction, approver2);
|
||||
return solanaWeb3.sendAndConfirmTransaction(
|
||||
connection,
|
||||
transaction,
|
||||
approver2,
|
||||
);
|
||||
})
|
||||
.then(confirmTransaction)
|
||||
.then(showBalance)
|
||||
|
|
|
@ -5851,7 +5851,7 @@
|
|||
"dependencies": {
|
||||
"marked": {
|
||||
"version": "0.3.19",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-0.3.19.tgz",
|
||||
"resolved": "http://registry.npmjs.org/marked/-/marked-0.3.19.tgz",
|
||||
"integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -6115,7 +6115,7 @@
|
|||
},
|
||||
"marked": {
|
||||
"version": "0.3.19",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-0.3.19.tgz",
|
||||
"resolved": "http://registry.npmjs.org/marked/-/marked-0.3.19.tgz",
|
||||
"integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -12399,7 +12399,7 @@
|
|||
},
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -16878,7 +16878,7 @@
|
|||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
|
||||
"dev": true
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import * as BufferLayout from 'buffer-layout';
|
|||
|
||||
import {Transaction} from './transaction';
|
||||
import {PublicKey} from './publickey';
|
||||
import * as Layout from './layout';
|
||||
import {SystemProgram} from './system-program';
|
||||
|
||||
/**
|
||||
* Represents a condition that is met by executing a `applySignature()`
|
||||
|
@ -114,20 +114,10 @@ function serializeCondition(condition: BudgetCondition) {
|
|||
return data;
|
||||
}
|
||||
case 'signature': {
|
||||
const dataLayout = BufferLayout.struct([
|
||||
BufferLayout.u32('condition'),
|
||||
Layout.publicKey('from'),
|
||||
]);
|
||||
|
||||
const from = condition.from.toBuffer();
|
||||
const data = Buffer.alloc(4 + from.length);
|
||||
dataLayout.encode(
|
||||
{
|
||||
instruction: 1, // Signature
|
||||
from,
|
||||
},
|
||||
data,
|
||||
);
|
||||
data.writeUInt32LE(1, 0); // Condition enum = Signature
|
||||
from.copy(data, 4);
|
||||
return data;
|
||||
}
|
||||
default:
|
||||
|
@ -190,8 +180,8 @@ export class BudgetProgram {
|
|||
pos += 4;
|
||||
|
||||
switch (conditions.length) {
|
||||
case 0:
|
||||
data.writeUInt32LE(0, pos); // Budget enum = Pay
|
||||
case 0: {
|
||||
data.writeUInt32LE(0, pos); // BudgetExpr enum = Pay
|
||||
pos += 4;
|
||||
|
||||
{
|
||||
|
@ -199,17 +189,27 @@ export class BudgetProgram {
|
|||
payment.copy(data, pos);
|
||||
pos += payment.length;
|
||||
}
|
||||
const trimmedData = data.slice(0, pos);
|
||||
|
||||
return new Transaction().add({
|
||||
const transaction = SystemProgram.createAccount(
|
||||
from,
|
||||
program,
|
||||
amount,
|
||||
trimmedData.length,
|
||||
this.programId,
|
||||
);
|
||||
|
||||
return transaction.add({
|
||||
keys: [
|
||||
{pubkey: from, isSigner: true, isDebitable: true},
|
||||
{pubkey: to, isSigner: false, isDebitable: false},
|
||||
{pubkey: program, isSigner: false, isDebitable: true},
|
||||
],
|
||||
programId: this.programId,
|
||||
data: data.slice(0, pos),
|
||||
data: trimmedData,
|
||||
});
|
||||
case 1:
|
||||
data.writeUInt32LE(1, pos); // Budget enum = After
|
||||
}
|
||||
case 1: {
|
||||
data.writeUInt32LE(1, pos); // BudgetExpr enum = After
|
||||
pos += 4;
|
||||
{
|
||||
const condition = conditions[0];
|
||||
|
@ -218,23 +218,32 @@ export class BudgetProgram {
|
|||
conditionData.copy(data, pos);
|
||||
pos += conditionData.length;
|
||||
|
||||
data.writeUInt32LE(0, pos); // BudgetExpr enum = Pay
|
||||
pos += 4;
|
||||
|
||||
const paymentData = serializePayment({amount, to});
|
||||
paymentData.copy(data, pos);
|
||||
pos += paymentData.length;
|
||||
}
|
||||
const trimmedData = data.slice(0, pos);
|
||||
|
||||
return new Transaction().add({
|
||||
keys: [
|
||||
{pubkey: from, isSigner: true, isDebitable: true},
|
||||
{pubkey: program, isSigner: false, isDebitable: true},
|
||||
{pubkey: to, isSigner: false, isDebitable: false},
|
||||
],
|
||||
const transaction = SystemProgram.createAccount(
|
||||
from,
|
||||
program,
|
||||
amount,
|
||||
trimmedData.length,
|
||||
this.programId,
|
||||
);
|
||||
|
||||
return transaction.add({
|
||||
keys: [{pubkey: program, isSigner: false, isDebitable: true}],
|
||||
programId: this.programId,
|
||||
data: data.slice(0, pos),
|
||||
data: trimmedData,
|
||||
});
|
||||
}
|
||||
|
||||
case 2:
|
||||
data.writeUInt32LE(2, pos); // Budget enum = Or
|
||||
case 2: {
|
||||
data.writeUInt32LE(2, pos); // BudgetExpr enum = Or
|
||||
pos += 4;
|
||||
|
||||
for (let condition of conditions) {
|
||||
|
@ -242,20 +251,29 @@ export class BudgetProgram {
|
|||
conditionData.copy(data, pos);
|
||||
pos += conditionData.length;
|
||||
|
||||
data.writeUInt32LE(0, pos); // BudgetExpr enum = Pay
|
||||
pos += 4;
|
||||
|
||||
const paymentData = serializePayment({amount, to});
|
||||
paymentData.copy(data, pos);
|
||||
pos += paymentData.length;
|
||||
}
|
||||
const trimmedData = data.slice(0, pos);
|
||||
|
||||
return new Transaction().add({
|
||||
keys: [
|
||||
{pubkey: from, isSigner: true, isDebitable: true},
|
||||
{pubkey: program, isSigner: false, isDebitable: true},
|
||||
{pubkey: to, isSigner: false, isDebitable: false},
|
||||
],
|
||||
const transaction = SystemProgram.createAccount(
|
||||
from,
|
||||
program,
|
||||
amount,
|
||||
trimmedData.length,
|
||||
this.programId,
|
||||
);
|
||||
|
||||
return transaction.add({
|
||||
keys: [{pubkey: program, isSigner: false, isDebitable: true}],
|
||||
programId: this.programId,
|
||||
data: data.slice(0, pos),
|
||||
data: trimmedData,
|
||||
});
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(
|
||||
|
@ -282,7 +300,7 @@ export class BudgetProgram {
|
|||
data.writeUInt32LE(0, pos); // NewBudget instruction
|
||||
pos += 4;
|
||||
|
||||
data.writeUInt32LE(3, pos); // Budget enum = And
|
||||
data.writeUInt32LE(3, pos); // BudgetExpr enum = And
|
||||
pos += 4;
|
||||
|
||||
for (let condition of [condition1, condition2]) {
|
||||
|
@ -291,18 +309,27 @@ export class BudgetProgram {
|
|||
pos += conditionData.length;
|
||||
}
|
||||
|
||||
data.writeUInt32LE(0, pos); // BudgetExpr enum = Pay
|
||||
pos += 4;
|
||||
|
||||
const paymentData = serializePayment({amount, to});
|
||||
paymentData.copy(data, pos);
|
||||
pos += paymentData.length;
|
||||
|
||||
return new Transaction().add({
|
||||
keys: [
|
||||
{pubkey: from, isSigner: true, isDebitable: true},
|
||||
{pubkey: program, isSigner: false, isDebitable: true},
|
||||
{pubkey: to, isSigner: false, isDebitable: false},
|
||||
],
|
||||
const trimmedData = data.slice(0, pos);
|
||||
|
||||
const transaction = SystemProgram.createAccount(
|
||||
from,
|
||||
program,
|
||||
amount,
|
||||
trimmedData.length,
|
||||
this.programId,
|
||||
);
|
||||
|
||||
return transaction.add({
|
||||
keys: [{pubkey: program, isSigner: false, isDebitable: true}],
|
||||
programId: this.programId,
|
||||
data: data.slice(0, pos),
|
||||
data: trimmedData,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -164,6 +164,8 @@ export class Transaction {
|
|||
let numCreditOnlySignedAccounts = 0;
|
||||
let numCreditOnlyUnsignedAccounts = 0;
|
||||
|
||||
const programIds = [];
|
||||
|
||||
this.instructions.forEach(instruction => {
|
||||
instruction.keys.forEach(keySignerPair => {
|
||||
const keyStr = keySignerPair.pubkey.toString();
|
||||
|
@ -183,6 +185,12 @@ export class Transaction {
|
|||
});
|
||||
|
||||
const programId = instruction.programId.toString();
|
||||
if (!programIds.includes(programId)) {
|
||||
programIds.push(programId);
|
||||
}
|
||||
});
|
||||
|
||||
programIds.forEach(programId => {
|
||||
if (!keys.includes(programId)) {
|
||||
keys.push(programId);
|
||||
numCreditOnlyUnsignedAccounts += 1;
|
||||
|
|
|
@ -15,7 +15,8 @@ test('pay', () => {
|
|||
to.publicKey,
|
||||
123,
|
||||
);
|
||||
expect(transaction.keys).toHaveLength(2);
|
||||
expect(transaction.instructions[0].keys).toHaveLength(2);
|
||||
expect(transaction.instructions[1].keys).toHaveLength(2);
|
||||
// TODO: Validate transaction contents more
|
||||
|
||||
transaction = BudgetProgram.pay(
|
||||
|
@ -25,7 +26,8 @@ test('pay', () => {
|
|||
123,
|
||||
BudgetProgram.signatureCondition(from.publicKey),
|
||||
);
|
||||
expect(transaction.keys).toHaveLength(3);
|
||||
expect(transaction.instructions[0].keys).toHaveLength(2);
|
||||
expect(transaction.instructions[1].keys).toHaveLength(1);
|
||||
// TODO: Validate transaction contents more
|
||||
|
||||
transaction = BudgetProgram.pay(
|
||||
|
@ -36,7 +38,8 @@ test('pay', () => {
|
|||
BudgetProgram.signatureCondition(from.publicKey),
|
||||
BudgetProgram.timestampCondition(from.publicKey, new Date()),
|
||||
);
|
||||
expect(transaction.keys).toHaveLength(3);
|
||||
expect(transaction.instructions[0].keys).toHaveLength(2);
|
||||
expect(transaction.instructions[1].keys).toHaveLength(1);
|
||||
// TODO: Validate transaction contents more
|
||||
|
||||
transaction = BudgetProgram.payOnBoth(
|
||||
|
@ -47,7 +50,8 @@ test('pay', () => {
|
|||
BudgetProgram.signatureCondition(from.publicKey),
|
||||
BudgetProgram.timestampCondition(from.publicKey, new Date()),
|
||||
);
|
||||
expect(transaction.keys).toHaveLength(3);
|
||||
expect(transaction.instructions[0].keys).toHaveLength(2);
|
||||
expect(transaction.instructions[1].keys).toHaveLength(1);
|
||||
// TODO: Validate transaction contents more
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue