refactor: employ prettier
This commit is contained in:
@ -40,4 +40,3 @@ export class Account {
|
||||
return this._keypair.secretKey;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,9 @@ export class BpfLoader {
|
||||
* Public key that identifies the BpfLoader
|
||||
*/
|
||||
static get programId(): PublicKey {
|
||||
return new PublicKey('0x8000000000000000000000000000000000000000000000000000000000000000');
|
||||
return new PublicKey(
|
||||
'0x8000000000000000000000000000000000000000000000000000000000000000',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -35,7 +37,9 @@ export class BpfLoader {
|
||||
const programAccount = new Account();
|
||||
|
||||
const elf = elfy.parse(elfBytes);
|
||||
const section = elf.body.sections.find(section => section.name === '.text.entrypoint');
|
||||
const section = elf.body.sections.find(
|
||||
section => section.name === '.text.entrypoint',
|
||||
);
|
||||
|
||||
const transaction = SystemProgram.createAccount(
|
||||
owner.publicKey,
|
||||
|
@ -15,8 +15,8 @@ import * as Layout from './layout';
|
||||
* @property {PublicKey} from Public key from which `applySignature()` will be accepted from
|
||||
*/
|
||||
export type SignatureCondition = {
|
||||
type: 'signature';
|
||||
from: PublicKey;
|
||||
type: 'signature',
|
||||
from: PublicKey,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -29,9 +29,9 @@ export type SignatureCondition = {
|
||||
* @property {Date} when The timestamp that was observed
|
||||
*/
|
||||
export type TimestampCondition = {
|
||||
type: 'timestamp';
|
||||
from: PublicKey;
|
||||
when: Date;
|
||||
type: 'timestamp',
|
||||
from: PublicKey,
|
||||
when: Date,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -42,9 +42,9 @@ export type TimestampCondition = {
|
||||
* @property {PublicKey} to Public key of the recipient
|
||||
*/
|
||||
export type Payment = {
|
||||
amount: number;
|
||||
to: PublicKey;
|
||||
}
|
||||
amount: number,
|
||||
to: PublicKey,
|
||||
};
|
||||
|
||||
/**
|
||||
* A condition that can unlock a payment
|
||||
@ -67,9 +67,7 @@ function serializePayment(payment: Payment): Buffer {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function serializeDate(
|
||||
when: Date
|
||||
): Buffer {
|
||||
function serializeDate(when: Date): Buffer {
|
||||
const userdata = Buffer.alloc(8 + 20);
|
||||
userdata.writeUInt32LE(20, 0); // size of timestamp as u64
|
||||
|
||||
@ -81,13 +79,20 @@ function serializeDate(
|
||||
return number;
|
||||
}
|
||||
|
||||
return date.getUTCFullYear() +
|
||||
'-' + pad(date.getUTCMonth() + 1) +
|
||||
'-' + pad(date.getUTCDate()) +
|
||||
'T' + pad(date.getUTCHours()) +
|
||||
':' + pad(date.getUTCMinutes()) +
|
||||
':' + pad(date.getUTCSeconds()) +
|
||||
'Z';
|
||||
return (
|
||||
date.getUTCFullYear() +
|
||||
'-' +
|
||||
pad(date.getUTCMonth() + 1) +
|
||||
'-' +
|
||||
pad(date.getUTCDate()) +
|
||||
'T' +
|
||||
pad(date.getUTCHours()) +
|
||||
':' +
|
||||
pad(date.getUTCMinutes()) +
|
||||
':' +
|
||||
pad(date.getUTCSeconds()) +
|
||||
'Z'
|
||||
);
|
||||
}
|
||||
userdata.write(iso(when), 8);
|
||||
return userdata;
|
||||
@ -97,52 +102,50 @@ function serializeDate(
|
||||
* @private
|
||||
*/
|
||||
function serializeCondition(condition: BudgetCondition) {
|
||||
switch(condition.type) {
|
||||
case 'timestamp':
|
||||
{
|
||||
const date = serializeDate(condition.when);
|
||||
const from = condition.from.toBuffer();
|
||||
switch (condition.type) {
|
||||
case 'timestamp': {
|
||||
const date = serializeDate(condition.when);
|
||||
const from = condition.from.toBuffer();
|
||||
|
||||
const userdata = Buffer.alloc(4 + date.length + from.length);
|
||||
userdata.writeUInt32LE(0, 0); // Condition enum = Timestamp
|
||||
date.copy(userdata, 4);
|
||||
from.copy(userdata, 4 + date.length);
|
||||
return userdata;
|
||||
}
|
||||
case 'signature':
|
||||
{
|
||||
const userdataLayout = BufferLayout.struct([
|
||||
BufferLayout.u32('condition'),
|
||||
Layout.publicKey('from'),
|
||||
]);
|
||||
const userdata = Buffer.alloc(4 + date.length + from.length);
|
||||
userdata.writeUInt32LE(0, 0); // Condition enum = Timestamp
|
||||
date.copy(userdata, 4);
|
||||
from.copy(userdata, 4 + date.length);
|
||||
return userdata;
|
||||
}
|
||||
case 'signature': {
|
||||
const userdataLayout = BufferLayout.struct([
|
||||
BufferLayout.u32('condition'),
|
||||
Layout.publicKey('from'),
|
||||
]);
|
||||
|
||||
const from = condition.from.toBuffer();
|
||||
const userdata = Buffer.alloc(4 + from.length);
|
||||
userdataLayout.encode(
|
||||
{
|
||||
instruction: 1, // Signature
|
||||
from
|
||||
},
|
||||
userdata,
|
||||
);
|
||||
return userdata;
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unknown condition type: ${condition.type}`);
|
||||
const from = condition.from.toBuffer();
|
||||
const userdata = Buffer.alloc(4 + from.length);
|
||||
userdataLayout.encode(
|
||||
{
|
||||
instruction: 1, // Signature
|
||||
from,
|
||||
},
|
||||
userdata,
|
||||
);
|
||||
return userdata;
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unknown condition type: ${condition.type}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Factory class for transactions to interact with the Budget program
|
||||
*/
|
||||
export class BudgetProgram {
|
||||
|
||||
/**
|
||||
* Public key that identifies the Budget program
|
||||
*/
|
||||
static get programId(): PublicKey {
|
||||
return new PublicKey('0x8100000000000000000000000000000000000000000000000000000000000000');
|
||||
return new PublicKey(
|
||||
'0x8100000000000000000000000000000000000000000000000000000000000000',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -152,11 +155,10 @@ export class BudgetProgram {
|
||||
return 128;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a timestamp condition
|
||||
*/
|
||||
static timestampCondition(from: PublicKey, when: Date) : TimestampCondition {
|
||||
static timestampCondition(from: PublicKey, when: Date): TimestampCondition {
|
||||
return {
|
||||
type: 'timestamp',
|
||||
from,
|
||||
@ -167,7 +169,7 @@ export class BudgetProgram {
|
||||
/**
|
||||
* Creates a signature condition
|
||||
*/
|
||||
static signatureCondition(from: PublicKey) : SignatureCondition {
|
||||
static signatureCondition(from: PublicKey): SignatureCondition {
|
||||
return {
|
||||
type: 'signature',
|
||||
from,
|
||||
@ -190,64 +192,68 @@ export class BudgetProgram {
|
||||
pos += 4;
|
||||
|
||||
switch (conditions.length) {
|
||||
case 0:
|
||||
userdata.writeUInt32LE(0, pos); // Budget enum = Pay
|
||||
pos += 4;
|
||||
case 0:
|
||||
userdata.writeUInt32LE(0, pos); // Budget enum = Pay
|
||||
pos += 4;
|
||||
|
||||
{
|
||||
const payment = serializePayment({amount, to});
|
||||
payment.copy(userdata, pos);
|
||||
pos += payment.length;
|
||||
}
|
||||
{
|
||||
const payment = serializePayment({amount, to});
|
||||
payment.copy(userdata, pos);
|
||||
pos += payment.length;
|
||||
}
|
||||
|
||||
return new Transaction().add({
|
||||
keys: [from, to],
|
||||
programId: this.programId,
|
||||
userdata: userdata.slice(0, pos),
|
||||
});
|
||||
case 1:
|
||||
userdata.writeUInt32LE(1, pos); // Budget enum = After
|
||||
pos += 4;
|
||||
{
|
||||
const condition = conditions[0];
|
||||
return new Transaction().add({
|
||||
keys: [from, to],
|
||||
programId: this.programId,
|
||||
userdata: userdata.slice(0, pos),
|
||||
});
|
||||
case 1:
|
||||
userdata.writeUInt32LE(1, pos); // Budget enum = After
|
||||
pos += 4;
|
||||
{
|
||||
const condition = conditions[0];
|
||||
|
||||
const conditionData = serializeCondition(condition);
|
||||
conditionData.copy(userdata, pos);
|
||||
pos += conditionData.length;
|
||||
const conditionData = serializeCondition(condition);
|
||||
conditionData.copy(userdata, pos);
|
||||
pos += conditionData.length;
|
||||
|
||||
const paymentData = serializePayment({amount, to});
|
||||
paymentData.copy(userdata, pos);
|
||||
pos += paymentData.length;
|
||||
}
|
||||
const paymentData = serializePayment({amount, to});
|
||||
paymentData.copy(userdata, pos);
|
||||
pos += paymentData.length;
|
||||
}
|
||||
|
||||
return new Transaction().add({
|
||||
keys: [from, program, to],
|
||||
programId: this.programId,
|
||||
userdata: userdata.slice(0, pos),
|
||||
});
|
||||
return new Transaction().add({
|
||||
keys: [from, program, to],
|
||||
programId: this.programId,
|
||||
userdata: userdata.slice(0, pos),
|
||||
});
|
||||
|
||||
case 2:
|
||||
userdata.writeUInt32LE(2, pos); // Budget enum = Or
|
||||
pos += 4;
|
||||
case 2:
|
||||
userdata.writeUInt32LE(2, pos); // Budget enum = Or
|
||||
pos += 4;
|
||||
|
||||
for (let condition of conditions) {
|
||||
const conditionData = serializeCondition(condition);
|
||||
conditionData.copy(userdata, pos);
|
||||
pos += conditionData.length;
|
||||
for (let condition of conditions) {
|
||||
const conditionData = serializeCondition(condition);
|
||||
conditionData.copy(userdata, pos);
|
||||
pos += conditionData.length;
|
||||
|
||||
const paymentData = serializePayment({amount, to});
|
||||
paymentData.copy(userdata, pos);
|
||||
pos += paymentData.length;
|
||||
}
|
||||
const paymentData = serializePayment({amount, to});
|
||||
paymentData.copy(userdata, pos);
|
||||
pos += paymentData.length;
|
||||
}
|
||||
|
||||
return new Transaction().add({
|
||||
keys: [from, program, to],
|
||||
programId: this.programId,
|
||||
userdata: userdata.slice(0, pos),
|
||||
});
|
||||
return new Transaction().add({
|
||||
keys: [from, program, to],
|
||||
programId: this.programId,
|
||||
userdata: userdata.slice(0, pos),
|
||||
});
|
||||
|
||||
default:
|
||||
throw new Error(`A maximum of two conditions are support: ${conditions.length} provided`);
|
||||
default:
|
||||
throw new Error(
|
||||
`A maximum of two conditions are support: ${
|
||||
conditions.length
|
||||
} provided`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -287,12 +293,16 @@ export class BudgetProgram {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates a transaction that applies a timestamp, which could enable a
|
||||
* pending payment to proceed.
|
||||
*/
|
||||
static applyTimestamp(from: PublicKey, program: PublicKey, to: PublicKey, when: Date): Transaction {
|
||||
static applyTimestamp(
|
||||
from: PublicKey,
|
||||
program: PublicKey,
|
||||
to: PublicKey,
|
||||
when: Date,
|
||||
): Transaction {
|
||||
const whenData = serializeDate(when);
|
||||
const userdata = Buffer.alloc(4 + whenData.length);
|
||||
|
||||
@ -310,7 +320,11 @@ export class BudgetProgram {
|
||||
* Generates a transaction that applies a signature, which could enable a
|
||||
* pending payment to proceed.
|
||||
*/
|
||||
static applySignature(from: PublicKey, program: PublicKey, to: PublicKey): Transaction {
|
||||
static applySignature(
|
||||
from: PublicKey,
|
||||
program: PublicKey,
|
||||
to: PublicKey,
|
||||
): Transaction {
|
||||
const userdataLayout = BufferLayout.struct([
|
||||
BufferLayout.u32('instruction'),
|
||||
]);
|
||||
|
@ -1,10 +1,7 @@
|
||||
// @flow
|
||||
|
||||
import assert from 'assert';
|
||||
import {
|
||||
parse as urlParse,
|
||||
format as urlFormat,
|
||||
} from 'url';
|
||||
import {parse as urlParse, format as urlFormat} from 'url';
|
||||
import fetch from 'node-fetch';
|
||||
import jayson from 'jayson/lib/client/browser';
|
||||
import {struct} from 'superstruct';
|
||||
@ -16,29 +13,26 @@ import {sleep} from './util/sleep';
|
||||
import type {Account} from './account';
|
||||
import type {TransactionSignature, TransactionId} from './transaction';
|
||||
|
||||
|
||||
type RpcRequest = (methodName: string, args: Array<any>) => any;
|
||||
|
||||
function createRpcRequest(url): RpcRequest {
|
||||
const server = jayson(
|
||||
async (request, callback) => {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
body: request,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
};
|
||||
const server = jayson(async (request, callback) => {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
body: request,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await fetch(url, options);
|
||||
const text = await res.text();
|
||||
callback(null, text);
|
||||
} catch (err) {
|
||||
callback(err);
|
||||
}
|
||||
try {
|
||||
const res = await fetch(url, options);
|
||||
const text = await res.text();
|
||||
callback(null, text);
|
||||
} catch (err) {
|
||||
callback(err);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return (method, args) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -53,7 +47,6 @@ function createRpcRequest(url): RpcRequest {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Expected JSON RPC response for the "getBalance" message
|
||||
*/
|
||||
@ -64,7 +57,6 @@ const GetBalanceRpcResult = struct({
|
||||
result: 'number?',
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@ -74,7 +66,7 @@ function jsonRpcResult(resultDescription: any) {
|
||||
struct({
|
||||
jsonrpc: jsonRpcVersion,
|
||||
id: 'string',
|
||||
error: 'any'
|
||||
error: 'any',
|
||||
}),
|
||||
struct({
|
||||
jsonrpc: jsonRpcVersion,
|
||||
@ -117,13 +109,15 @@ const ConfirmTransactionRpcResult = jsonRpcResult('boolean');
|
||||
/**
|
||||
* Expected JSON RPC response for the "getSignatureStatus" message
|
||||
*/
|
||||
const GetSignatureStatusRpcResult = jsonRpcResult(struct.enum([
|
||||
'AccountInUse',
|
||||
'Confirmed',
|
||||
'GenericFailure',
|
||||
'ProgramRuntimeError',
|
||||
'SignatureNotFound',
|
||||
]));
|
||||
const GetSignatureStatusRpcResult = jsonRpcResult(
|
||||
struct.enum([
|
||||
'AccountInUse',
|
||||
'Confirmed',
|
||||
'GenericFailure',
|
||||
'ProgramRuntimeError',
|
||||
'SignatureNotFound',
|
||||
]),
|
||||
);
|
||||
|
||||
/**
|
||||
* Expected JSON RPC response for the "getTransactionCount" message
|
||||
@ -159,11 +153,11 @@ const SendTokensRpcResult = jsonRpcResult('string');
|
||||
* @property {?Buffer} userdata Optional userdata assigned to the account
|
||||
*/
|
||||
type AccountInfo = {
|
||||
executable: boolean;
|
||||
executable: boolean,
|
||||
programId: PublicKey,
|
||||
tokens: number,
|
||||
userdata: Buffer,
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback function for account change notifications
|
||||
@ -174,17 +168,18 @@ export type AccountChangeCallback = (accountInfo: AccountInfo) => void;
|
||||
* @private
|
||||
*/
|
||||
type AccountSubscriptionInfo = {
|
||||
publicKey: string; // PublicKey of the account as a base 58 string
|
||||
publicKey: string, // PublicKey of the account as a base 58 string
|
||||
callback: AccountChangeCallback,
|
||||
subscriptionId: null | number; // null when there's no current server subscription id
|
||||
}
|
||||
subscriptionId: null | number, // null when there's no current server subscription id
|
||||
};
|
||||
|
||||
/**
|
||||
* Possible signature status values
|
||||
*
|
||||
* @typedef {string} SignatureStatus
|
||||
*/
|
||||
export type SignatureStatus = 'Confirmed'
|
||||
export type SignatureStatus =
|
||||
| 'Confirmed'
|
||||
| 'AccountInUse'
|
||||
| 'SignatureNotFound'
|
||||
| 'ProgramRuntimeError'
|
||||
@ -203,7 +198,7 @@ export class Connection {
|
||||
seconds: number,
|
||||
transactionSignatures: Array<string>,
|
||||
};
|
||||
_disableLastIdCaching: boolean = false
|
||||
_disableLastIdCaching: boolean = false;
|
||||
_accountChangeSubscriptions: {[number]: AccountSubscriptionInfo} = {};
|
||||
_accountChangeSubscriptionCounter: number = 0;
|
||||
|
||||
@ -228,27 +223,26 @@ export class Connection {
|
||||
if (url.port === '1') {
|
||||
url.port = url.protocol === 'wss:' ? '8901' : '8900';
|
||||
}
|
||||
this._rpcWebSocket = new RpcWebSocketClient(
|
||||
urlFormat(url),
|
||||
{
|
||||
autoconnect: false,
|
||||
max_reconnects: Infinity,
|
||||
}
|
||||
);
|
||||
this._rpcWebSocket = new RpcWebSocketClient(urlFormat(url), {
|
||||
autoconnect: false,
|
||||
max_reconnects: Infinity,
|
||||
});
|
||||
this._rpcWebSocket.on('open', this._wsOnOpen.bind(this));
|
||||
this._rpcWebSocket.on('error', this._wsOnError.bind(this));
|
||||
this._rpcWebSocket.on('close', this._wsOnClose.bind(this));
|
||||
this._rpcWebSocket.on('accountNotification', this._wsOnAccountNotification.bind(this));
|
||||
this._rpcWebSocket.on(
|
||||
'accountNotification',
|
||||
this._wsOnAccountNotification.bind(this),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the balance for the specified public key
|
||||
*/
|
||||
async getBalance(publicKey: PublicKey): Promise<number> {
|
||||
const unsafeRes = await this._rpcRequest(
|
||||
'getBalance',
|
||||
[publicKey.toBase58()]
|
||||
);
|
||||
const unsafeRes = await this._rpcRequest('getBalance', [
|
||||
publicKey.toBase58(),
|
||||
]);
|
||||
const res = GetBalanceRpcResult(unsafeRes);
|
||||
if (res.error) {
|
||||
throw new Error(res.error.message);
|
||||
@ -261,10 +255,9 @@ export class Connection {
|
||||
* Fetch all the account info for the specified public key
|
||||
*/
|
||||
async getAccountInfo(publicKey: PublicKey): Promise<AccountInfo> {
|
||||
const unsafeRes = await this._rpcRequest(
|
||||
'getAccountInfo',
|
||||
[publicKey.toBase58()]
|
||||
);
|
||||
const unsafeRes = await this._rpcRequest('getAccountInfo', [
|
||||
publicKey.toBase58(),
|
||||
]);
|
||||
const res = GetAccountInfoRpcResult(unsafeRes);
|
||||
if (res.error) {
|
||||
throw new Error(res.error.message);
|
||||
@ -286,10 +279,7 @@ export class Connection {
|
||||
* Confirm the transaction identified by the specified signature
|
||||
*/
|
||||
async confirmTransaction(signature: TransactionSignature): Promise<boolean> {
|
||||
const unsafeRes = await this._rpcRequest(
|
||||
'confirmTransaction',
|
||||
[signature]
|
||||
);
|
||||
const unsafeRes = await this._rpcRequest('confirmTransaction', [signature]);
|
||||
const res = ConfirmTransactionRpcResult(unsafeRes);
|
||||
if (res.error) {
|
||||
throw new Error(res.error.message);
|
||||
@ -301,7 +291,9 @@ export class Connection {
|
||||
/**
|
||||
* Fetch the current transaction count of the network
|
||||
*/
|
||||
async getSignatureStatus(signature: TransactionSignature): Promise<SignatureStatus> {
|
||||
async getSignatureStatus(
|
||||
signature: TransactionSignature,
|
||||
): Promise<SignatureStatus> {
|
||||
const unsafeRes = await this._rpcRequest('getSignatureStatus', [signature]);
|
||||
const res = GetSignatureStatusRpcResult(unsafeRes);
|
||||
if (res.error) {
|
||||
@ -311,7 +303,6 @@ export class Connection {
|
||||
return res.result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetch the current transaction count of the network
|
||||
*/
|
||||
@ -354,8 +345,14 @@ export class Connection {
|
||||
/**
|
||||
* Request an allocation of tokens to the specified account
|
||||
*/
|
||||
async requestAirdrop(to: PublicKey, amount: number): Promise<TransactionSignature> {
|
||||
const unsafeRes = await this._rpcRequest('requestAirdrop', [to.toBase58(), amount]);
|
||||
async requestAirdrop(
|
||||
to: PublicKey,
|
||||
amount: number,
|
||||
): Promise<TransactionSignature> {
|
||||
const unsafeRes = await this._rpcRequest('requestAirdrop', [
|
||||
to.toBase58(),
|
||||
amount,
|
||||
]);
|
||||
const res = RequestAirdropRpcResult(unsafeRes);
|
||||
if (res.error) {
|
||||
throw new Error(res.error.message);
|
||||
@ -367,13 +364,17 @@ export class Connection {
|
||||
/**
|
||||
* Sign and send a transaction
|
||||
*/
|
||||
async sendTransaction(from: Account, transaction: Transaction): Promise<TransactionSignature> {
|
||||
async sendTransaction(
|
||||
from: Account,
|
||||
transaction: Transaction,
|
||||
): Promise<TransactionSignature> {
|
||||
for (;;) {
|
||||
// Attempt to use the previous last id for up to 1 second
|
||||
const seconds = (new Date()).getSeconds();
|
||||
if ( (this._lastIdInfo.lastId != null) &&
|
||||
(this._lastIdInfo.seconds === seconds) ) {
|
||||
|
||||
const seconds = new Date().getSeconds();
|
||||
if (
|
||||
this._lastIdInfo.lastId != null &&
|
||||
this._lastIdInfo.seconds === seconds
|
||||
) {
|
||||
transaction.lastId = this._lastIdInfo.lastId;
|
||||
transaction.sign(from);
|
||||
if (!transaction.signature) {
|
||||
@ -401,13 +402,15 @@ export class Connection {
|
||||
if (this._lastIdInfo.lastId != lastId) {
|
||||
this._lastIdInfo = {
|
||||
lastId,
|
||||
seconds: (new Date()).getSeconds(),
|
||||
seconds: new Date().getSeconds(),
|
||||
transactionSignatures: [],
|
||||
};
|
||||
break;
|
||||
}
|
||||
if (attempts === 8) {
|
||||
throw new Error(`Unable to obtain a new last id after ${Date.now() - startTime}ms`);
|
||||
throw new Error(
|
||||
`Unable to obtain a new last id after ${Date.now() - startTime}ms`,
|
||||
);
|
||||
}
|
||||
await sleep(250);
|
||||
++attempts;
|
||||
@ -415,7 +418,9 @@ export class Connection {
|
||||
}
|
||||
|
||||
const wireTransaction = transaction.serialize();
|
||||
const unsafeRes = await this._rpcRequest('sendTransaction', [[...wireTransaction]]);
|
||||
const unsafeRes = await this._rpcRequest('sendTransaction', [
|
||||
[...wireTransaction],
|
||||
]);
|
||||
const res = SendTokensRpcResult(unsafeRes);
|
||||
if (res.error) {
|
||||
throw new Error(res.error.message);
|
||||
@ -501,13 +506,15 @@ export class Connection {
|
||||
const {subscriptionId, publicKey} = this._accountChangeSubscriptions[id];
|
||||
if (subscriptionId === null) {
|
||||
try {
|
||||
this._accountChangeSubscriptions[id].subscriptionId =
|
||||
await this._rpcWebSocket.call(
|
||||
'accountSubscribe',
|
||||
[publicKey]
|
||||
);
|
||||
this._accountChangeSubscriptions[
|
||||
id
|
||||
].subscriptionId = await this._rpcWebSocket.call('accountSubscribe', [
|
||||
publicKey,
|
||||
]);
|
||||
} catch (err) {
|
||||
console.log(`accountSubscribe error for ${publicKey}: ${err.message}`);
|
||||
console.log(
|
||||
`accountSubscribe error for ${publicKey}: ${err.message}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -520,12 +527,15 @@ export class Connection {
|
||||
* @param callback Function to invoke whenever the account is changed
|
||||
* @return subscription id
|
||||
*/
|
||||
onAccountChange(publicKey: PublicKey, callback: AccountChangeCallback): number {
|
||||
onAccountChange(
|
||||
publicKey: PublicKey,
|
||||
callback: AccountChangeCallback,
|
||||
): number {
|
||||
const id = ++this._accountChangeSubscriptionCounter;
|
||||
this._accountChangeSubscriptions[id] = {
|
||||
publicKey: publicKey.toBase58(),
|
||||
callback,
|
||||
subscriptionId: null
|
||||
subscriptionId: null,
|
||||
};
|
||||
this._updateSubscriptions();
|
||||
return id;
|
||||
@ -552,5 +562,4 @@ export class Connection {
|
||||
throw new Error(`Unknown account change id: ${id}`);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
import * as BufferLayout from 'buffer-layout';
|
||||
|
||||
|
||||
/**
|
||||
* Layout for a public key
|
||||
*/
|
||||
@ -17,7 +16,6 @@ export const uint64 = (property: string = 'uint64'): Object => {
|
||||
return BufferLayout.blob(8, property);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Layout for a Rust String type
|
||||
*/
|
||||
@ -26,10 +24,7 @@ export const rustString = (property: string = 'string') => {
|
||||
[
|
||||
BufferLayout.u32('length'),
|
||||
BufferLayout.u32('lengthPadding'),
|
||||
BufferLayout.blob(
|
||||
BufferLayout.offset(BufferLayout.u32(), -8),
|
||||
'chars',
|
||||
),
|
||||
BufferLayout.blob(BufferLayout.offset(BufferLayout.u32(), -8), 'chars'),
|
||||
],
|
||||
property,
|
||||
);
|
||||
|
@ -45,7 +45,9 @@ export class Loader {
|
||||
BufferLayout.u32('bytesLengthPadding'),
|
||||
BufferLayout.seq(
|
||||
BufferLayout.u8('byte'),
|
||||
BufferLayout.offset(BufferLayout.u32(), -8), 'bytes'),
|
||||
BufferLayout.offset(BufferLayout.u32(), -8),
|
||||
'bytes',
|
||||
),
|
||||
]);
|
||||
|
||||
const chunkSize = 256;
|
||||
@ -69,7 +71,9 @@ export class Loader {
|
||||
programId: this.programId,
|
||||
userdata,
|
||||
});
|
||||
transactions.push(sendAndConfirmTransaction(this.connection, program, transaction));
|
||||
transactions.push(
|
||||
sendAndConfirmTransaction(this.connection, program, transaction),
|
||||
);
|
||||
|
||||
// Run up to 8 Loads in parallel to prevent too many parallel transactions from
|
||||
// getting rejected with AccountInUse.
|
||||
|
@ -15,7 +15,9 @@ export class NativeLoader {
|
||||
* Public key that identifies the NativeLoader
|
||||
*/
|
||||
static get programId(): PublicKey {
|
||||
return new PublicKey('0x100000000000000000000000000000000000000000000000000000000000000');
|
||||
return new PublicKey(
|
||||
'0x100000000000000000000000000000000000000000000000000000000000000',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -72,4 +72,3 @@ export class PublicKey {
|
||||
return this.toBase58();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,9 @@ export class SystemProgram {
|
||||
* Public key that identifies the System program
|
||||
*/
|
||||
static get programId(): PublicKey {
|
||||
return new PublicKey('0x000000000000000000000000000000000000000000000000000000000000000');
|
||||
return new PublicKey(
|
||||
'0x000000000000000000000000000000000000000000000000000000000000000',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -25,9 +27,8 @@ export class SystemProgram {
|
||||
newAccount: PublicKey,
|
||||
tokens: number,
|
||||
space: number,
|
||||
programId: PublicKey
|
||||
programId: PublicKey,
|
||||
): Transaction {
|
||||
|
||||
const userdataLayout = BufferLayout.struct([
|
||||
BufferLayout.u32('instruction'),
|
||||
BufferLayout.ns64('tokens'),
|
||||
|
@ -40,13 +40,15 @@ export class TokenAmount extends BN {
|
||||
static fromBuffer(buffer: Buffer): TokenAmount {
|
||||
assert(buffer.length === 8, `Invalid buffer length: ${buffer.length}`);
|
||||
return new BN(
|
||||
[...buffer].reverse().map(i => `00${i.toString(16)}`.slice(-2)).join(''),
|
||||
16
|
||||
[...buffer]
|
||||
.reverse()
|
||||
.map(i => `00${i.toString(16)}`.slice(-2))
|
||||
.join(''),
|
||||
16,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Information about a token
|
||||
*/
|
||||
@ -129,20 +131,19 @@ const TokenAccountInfoLayout = BufferLayout.struct([
|
||||
Layout.uint64('originalAmount'),
|
||||
]);
|
||||
|
||||
|
||||
type TokenAndPublicKey = [Token, PublicKey]; // This type exists to workaround an esdoc parse error
|
||||
|
||||
|
||||
/**
|
||||
* The built-in token program
|
||||
*/
|
||||
export const SYSTEM_TOKEN_PROGRAM_ID = new PublicKey('0x8300000000000000000000000000000000000000000000000000000000000000');
|
||||
export const SYSTEM_TOKEN_PROGRAM_ID = new PublicKey(
|
||||
'0x8300000000000000000000000000000000000000000000000000000000000000',
|
||||
);
|
||||
|
||||
/**
|
||||
* An ERC20-like Token
|
||||
*/
|
||||
export class Token {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@ -165,7 +166,11 @@ export class Token {
|
||||
* @param token Public key of the token
|
||||
* @param programId Optional token programId, uses the system programId by default
|
||||
*/
|
||||
constructor(connection: Connection, token: PublicKey, programId: PublicKey = SYSTEM_TOKEN_PROGRAM_ID) {
|
||||
constructor(
|
||||
connection: Connection,
|
||||
token: PublicKey,
|
||||
programId: PublicKey = SYSTEM_TOKEN_PROGRAM_ID,
|
||||
) {
|
||||
Object.assign(this, {connection, token, programId});
|
||||
}
|
||||
|
||||
@ -249,7 +254,10 @@ export class Token {
|
||||
* may transfer tokens from this `source` account
|
||||
* @return Public key of the new empty token account
|
||||
*/
|
||||
async newAccount(owner: Account, source: null | PublicKey = null): Promise<PublicKey> {
|
||||
async newAccount(
|
||||
owner: Account,
|
||||
source: null | PublicKey = null,
|
||||
): Promise<PublicKey> {
|
||||
const tokenAccount = new Account();
|
||||
let transaction;
|
||||
|
||||
@ -297,7 +305,9 @@ export class Token {
|
||||
async tokenInfo(): Promise<TokenInfo> {
|
||||
const accountInfo = await this.connection.getAccountInfo(this.token);
|
||||
if (!accountInfo.programId.equals(this.programId)) {
|
||||
throw new Error(`Invalid token programId: ${JSON.stringify(accountInfo.programId)}`);
|
||||
throw new Error(
|
||||
`Invalid token programId: ${JSON.stringify(accountInfo.programId)}`,
|
||||
);
|
||||
}
|
||||
|
||||
const userdata = Buffer.from(accountInfo.userdata);
|
||||
@ -310,7 +320,6 @@ export class Token {
|
||||
return tokenInfo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve account information
|
||||
*
|
||||
@ -336,12 +345,16 @@ export class Token {
|
||||
tokenAccountInfo.originalAmount = new TokenAmount();
|
||||
} else {
|
||||
tokenAccountInfo.source = new PublicKey(tokenAccountInfo.source);
|
||||
tokenAccountInfo.originalAmount = TokenAmount.fromBuffer(tokenAccountInfo.originalAmount);
|
||||
tokenAccountInfo.originalAmount = TokenAmount.fromBuffer(
|
||||
tokenAccountInfo.originalAmount,
|
||||
);
|
||||
}
|
||||
|
||||
if (!tokenAccountInfo.token.equals(this.token)) {
|
||||
throw new Error(
|
||||
`Invalid token account token: ${JSON.stringify(tokenAccountInfo.token)} !== ${JSON.stringify(this.token)}`
|
||||
`Invalid token account token: ${JSON.stringify(
|
||||
tokenAccountInfo.token,
|
||||
)} !== ${JSON.stringify(this.token)}`,
|
||||
);
|
||||
}
|
||||
return tokenAccountInfo;
|
||||
@ -370,7 +383,7 @@ export class Token {
|
||||
source,
|
||||
destination,
|
||||
amount,
|
||||
)
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -387,18 +400,13 @@ export class Token {
|
||||
owner: Account,
|
||||
account: PublicKey,
|
||||
delegate: PublicKey,
|
||||
amount: number | TokenAmount
|
||||
amount: number | TokenAmount,
|
||||
): Promise<void> {
|
||||
await sendAndConfirmTransaction(
|
||||
this.connection,
|
||||
owner,
|
||||
new Transaction().add(
|
||||
this.approveInstruction(
|
||||
owner.publicKey,
|
||||
account,
|
||||
delegate,
|
||||
amount,
|
||||
)
|
||||
this.approveInstruction(owner.publicKey, account, delegate, amount),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -413,7 +421,7 @@ export class Token {
|
||||
revoke(
|
||||
owner: Account,
|
||||
account: PublicKey,
|
||||
delegate: PublicKey
|
||||
delegate: PublicKey,
|
||||
): Promise<void> {
|
||||
return this.approve(owner, account, delegate, 0);
|
||||
}
|
||||
@ -434,11 +442,7 @@ export class Token {
|
||||
this.connection,
|
||||
owner,
|
||||
new Transaction().add(
|
||||
this.setOwnerInstruction(
|
||||
owner.publicKey,
|
||||
account,
|
||||
newOwner
|
||||
)
|
||||
this.setOwnerInstruction(owner.publicKey, account, newOwner),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -471,7 +475,7 @@ export class Token {
|
||||
userdataLayout.encode(
|
||||
{
|
||||
instruction: 2, // Transfer instruction
|
||||
amount: (new TokenAmount(amount)).toBuffer(),
|
||||
amount: new TokenAmount(amount).toBuffer(),
|
||||
},
|
||||
userdata,
|
||||
);
|
||||
@ -499,7 +503,7 @@ export class Token {
|
||||
owner: PublicKey,
|
||||
account: PublicKey,
|
||||
delegate: PublicKey,
|
||||
amount: number | TokenAmount
|
||||
amount: number | TokenAmount,
|
||||
): TransactionInstruction {
|
||||
const userdataLayout = BufferLayout.struct([
|
||||
BufferLayout.u32('instruction'),
|
||||
@ -510,7 +514,7 @@ export class Token {
|
||||
userdataLayout.encode(
|
||||
{
|
||||
instruction: 3, // Approve instruction
|
||||
amount: (new TokenAmount(amount)).toBuffer(),
|
||||
amount: new TokenAmount(amount).toBuffer(),
|
||||
},
|
||||
userdata,
|
||||
);
|
||||
@ -568,5 +572,3 @@ export class Token {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -28,12 +28,11 @@ export type TransactionId = string;
|
||||
* @property {?Buffer} userdata
|
||||
*/
|
||||
type TransactionInstructionCtorFields = {|
|
||||
keys?: Array<PublicKey>;
|
||||
programId?: PublicKey;
|
||||
userdata?: Buffer;
|
||||
keys?: Array<PublicKey>,
|
||||
programId?: PublicKey,
|
||||
userdata?: Buffer,
|
||||
|};
|
||||
|
||||
|
||||
/**
|
||||
* Transaction Instruction class
|
||||
*/
|
||||
@ -58,7 +57,6 @@ export class TransactionInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* List of Transaction object fields that may be initialized at construction
|
||||
*
|
||||
@ -70,21 +68,19 @@ export class TransactionInstruction {
|
||||
* @property {?Buffer} userdata
|
||||
*/
|
||||
type TransactionCtorFields = {|
|
||||
fee?: number;
|
||||
fee?: number,
|
||||
|};
|
||||
|
||||
/**
|
||||
* Transaction class
|
||||
*/
|
||||
export class Transaction {
|
||||
|
||||
/**
|
||||
* Current signature of the transaction. Typically created by invoking the
|
||||
* `sign()` method
|
||||
*/
|
||||
signature: ?Buffer;
|
||||
|
||||
|
||||
/**
|
||||
* The instructions to atomically execute
|
||||
*/
|
||||
@ -140,9 +136,7 @@ export class Transaction {
|
||||
programIds.push(programId);
|
||||
}
|
||||
|
||||
instruction.keys
|
||||
.map(key => key.toString())
|
||||
.forEach(key => {
|
||||
instruction.keys.map(key => key.toString()).forEach(key => {
|
||||
if (!keys.includes(key)) {
|
||||
keys.push(key);
|
||||
}
|
||||
@ -171,14 +165,14 @@ export class Transaction {
|
||||
BufferLayout.seq(
|
||||
BufferLayout.u8('keyIndex'),
|
||||
BufferLayout.offset(BufferLayout.u32(), -8),
|
||||
'keyIndices'
|
||||
'keyIndices',
|
||||
),
|
||||
BufferLayout.u32('userdataLength'),
|
||||
BufferLayout.u32('userdataLengthPadding'),
|
||||
BufferLayout.seq(
|
||||
BufferLayout.u8('userdatum'),
|
||||
BufferLayout.offset(BufferLayout.u32(), -8),
|
||||
'userdata'
|
||||
'userdata',
|
||||
),
|
||||
]);
|
||||
|
||||
@ -188,7 +182,7 @@ export class Transaction {
|
||||
BufferLayout.seq(
|
||||
Layout.publicKey('key'),
|
||||
BufferLayout.offset(BufferLayout.u32(), -8),
|
||||
'keys'
|
||||
'keys',
|
||||
),
|
||||
Layout.publicKey('lastId'),
|
||||
BufferLayout.ns64('fee'),
|
||||
@ -198,7 +192,7 @@ export class Transaction {
|
||||
BufferLayout.seq(
|
||||
Layout.publicKey('programId'),
|
||||
BufferLayout.offset(BufferLayout.u32(), -8),
|
||||
'programIds'
|
||||
'programIds',
|
||||
),
|
||||
|
||||
BufferLayout.u32('instructionsLength'),
|
||||
@ -206,15 +200,17 @@ export class Transaction {
|
||||
BufferLayout.seq(
|
||||
instructionLayout,
|
||||
BufferLayout.offset(BufferLayout.u32(), -8),
|
||||
'instructions'
|
||||
'instructions',
|
||||
),
|
||||
]);
|
||||
|
||||
const transaction = {
|
||||
keys: keys.map((key) => (new PublicKey(key)).toBuffer()),
|
||||
keys: keys.map(key => new PublicKey(key).toBuffer()),
|
||||
lastId: Buffer.from(bs58.decode(lastId)),
|
||||
fee: this.fee,
|
||||
programIds: programIds.map(programId => (new PublicKey(programId)).toBuffer()),
|
||||
programIds: programIds.map(programId =>
|
||||
new PublicKey(programId).toBuffer(),
|
||||
),
|
||||
instructions,
|
||||
};
|
||||
|
||||
@ -248,9 +244,7 @@ export class Transaction {
|
||||
}
|
||||
|
||||
const signData = this._getSignData();
|
||||
const wireTransaction = Buffer.alloc(
|
||||
signature.length + signData.length
|
||||
);
|
||||
const wireTransaction = Buffer.alloc(signature.length + signData.length);
|
||||
|
||||
Buffer.from(signature).copy(wireTransaction, 0);
|
||||
signData.copy(wireTransaction, signature.length);
|
||||
@ -283,6 +277,4 @@ export class Transaction {
|
||||
assert(this.instructions.length === 1);
|
||||
return this.instructions[0].userdata;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -13,9 +13,8 @@ export async function sendAndConfirmTransaction(
|
||||
connection: Connection,
|
||||
from: Account,
|
||||
transaction: Transaction,
|
||||
runtimeErrorOk: boolean = false
|
||||
runtimeErrorOk: boolean = false,
|
||||
): Promise<?TransactionSignature> {
|
||||
|
||||
let sendRetries = 10;
|
||||
let signature;
|
||||
for (;;) {
|
||||
@ -34,12 +33,18 @@ export async function sendAndConfirmTransaction(
|
||||
await sleep(500);
|
||||
if (--statusRetries <= 0) {
|
||||
const duration = (Date.now() - start) / 1000;
|
||||
throw new Error(`Transaction '${signature}' was not confirmed in ${duration.toFixed(2)} seconds (${status})`);
|
||||
throw new Error(
|
||||
`Transaction '${signature}' was not confirmed in ${duration.toFixed(
|
||||
2,
|
||||
)} seconds (${status})`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( (status === 'Confirmed') ||
|
||||
(status === 'ProgramRuntimeError' && runtimeErrorOk) ) {
|
||||
if (
|
||||
status === 'Confirmed' ||
|
||||
(status === 'ProgramRuntimeError' && runtimeErrorOk)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
|
||||
@ -53,4 +58,3 @@ export async function sendAndConfirmTransaction(
|
||||
|
||||
return signature;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user