Skip to content

Commit

Permalink
feat: wip transactions & borsh refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
vecheslav committed Sep 21, 2021
1 parent 17f592f commit 45db5e5
Show file tree
Hide file tree
Showing 42 changed files with 1,219 additions and 687 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ For example, a transaction to pay for file storage can be created easily with a
```ts
// ...
const files: File[] = [artwork, metadata];
const fileHashes = await Promise.all(files.map((file) => Utils.crypto.getFileHash(file)));
const fileHashes = await Promise.all(files.map((file) => Utils.Crypto.getFileHash(file)));
const lamports = await storage.getAssetCostToStore(files, rates[0].rate, rates[1].rate);

const payForFilesTx = new PayForFiles(
Expand Down
142 changes: 94 additions & 48 deletions api/src/programs/auction/accouns/Auction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { AccountInfo, Connection, PublicKey } from '@solana/web3.js';
import BN from 'bn.js';
import { ERROR_INVALID_OWNER } from '@metaplex/errors';
import { AnyPublicKey, StringPublicKey } from '@metaplex/types';
import { borsh } from '@metaplex/utils';
import { Account } from '../../../Account';
import { AuctionProgram } from '../AuctionProgram';
import { BidderMetadata } from './BidderMetadata';
import { BidderPot } from './BidderPot';
import { Buffer } from 'buffer';
import { Borsh } from '@metaplex/utils';

export enum AuctionState {
Created = 0,
Expand All @@ -26,59 +26,120 @@ export enum PriceFloorType {
BlindedPrice = 2,
}

export interface Bid {
type BidArgs = { key: StringPublicKey; amount: BN };
export class Bid extends Borsh.Data<BidArgs> {
static readonly SCHEMA = this.struct([
['key', 'pubkeyAsString'],
['amount', 'u64'],
]);

key: StringPublicKey;
amount: BN;
}

const bidStruct = borsh.struct<Bid>([
['key', 'pubkeyAsString'],
['amount', 'u64'],
]);
type BidStateArgs = { type: BidStateType; bids: Bid[]; max: BN };
export class BidState extends Borsh.Data<BidStateArgs> {
static readonly SCHEMA = new Map([
...Bid.SCHEMA,
...this.struct([
['type', 'u8'],
['bids', [Bid]],
['max', 'u64'],
]),
]);

export interface BidState {
type: BidStateType;
bids: Bid[];
max: BN;

getWinnerAt(winnerIndex: number): StringPublicKey | null {
const convertedIndex = this.bids.length - winnerIndex - 1;

if (convertedIndex >= 0 && convertedIndex < this.bids.length) {
return this.bids[convertedIndex].key;
} else {
return null;
}
}

getAmountAt(winnerIndex: number): BN | null {
const convertedIndex = this.bids.length - winnerIndex - 1;

if (convertedIndex >= 0 && convertedIndex < this.bids.length) {
return this.bids[convertedIndex].amount;
} else {
return null;
}
}

getWinnerIndex(bidder: StringPublicKey): number | null {
if (!this.bids) return null;

const index = this.bids.findIndex((b) => b.key === bidder);
// auction stores data in reverse order
if (index !== -1) {
const zeroBased = this.bids.length - index - 1;
return zeroBased < this.max.toNumber() ? zeroBased : null;
} else return null;
}
}

const bidStateStruct = borsh.struct<BidState>(
[
type PriceFloorArgs = { type: PriceFloorType; hash?: Uint8Array; minPrice?: BN };
export class PriceFloor extends Borsh.Data {
static readonly SCHEMA = this.struct([
['type', 'u8'],
['bids', [bidStruct.type]],
['max', 'u64'],
],
[bidStruct],
);
['hash', [32]],
]);

export interface PriceFloor {
type: PriceFloorType;
// It's an array of 32 u8s, when minimum, only first 8 are used (a u64), when blinded price, the entire
// thing is a hash and not actually a public key, and none is all zeroes
hash: Uint8Array;
minPrice?: BN;
}

const priceFloorStruct = borsh.struct<PriceFloor>(
[
['type', 'u8'],
['hash', [32]],
],
[],
(data) => {
if (!data.hash) data.hash = new Uint8Array(32);
if (data.type === PriceFloorType.Minimum) {
if (data.minPrice) {
data.hash.set(data.minPrice.toArrayLike(Buffer, 'le', 8), 0);
constructor(args: PriceFloorArgs) {
super();
this.type = args.type;
this.hash = args.hash || new Uint8Array(32);
if (this.type === PriceFloorType.Minimum) {
if (args.minPrice) {
this.hash.set(args.minPrice.toArrayLike(Buffer, 'le', 8), 0);
} else {
data.minPrice = new BN((data.hash || new Uint8Array(0)).slice(0, 8), 'le');
this.minPrice = new BN((args.hash || new Uint8Array(0)).slice(0, 8), 'le');
}
}
return data;
},
);
}
}

type Args = {
authority: StringPublicKey;
tokenMint: StringPublicKey;
lastBid: BN | null;
endedAt: BN | null;
endAuctionAt: BN | null;
auctionGap: BN | null;
priceFloor: PriceFloor;
state: AuctionState;
bidState: BidState;
totalUncancelledBids: BN;
};
export class AuctionData extends Borsh.Data<Args> {
static readonly SCHEMA = new Map([
...BidState.SCHEMA,
...PriceFloor.SCHEMA,
...this.struct([
['authority', 'pubkeyAsString'],
['tokenMint', 'pubkeyAsString'],
['lastBid', { kind: 'option', type: 'u64' }],
['endedAt', { kind: 'option', type: 'u64' }],
['endAuctionAt', { kind: 'option', type: 'u64' }],
['auctionGap', { kind: 'option', type: 'u64' }],
['priceFloor', PriceFloor],
['state', 'u8'],
['bidState', BidState],
]),
]);

export interface AuctionData {
/// Pubkey of the authority with permission to modify this auction.
authority: StringPublicKey;
/// Token mint for the SPL token being used to bid
Expand All @@ -101,21 +162,6 @@ export interface AuctionData {
bidRedemptionKey?: StringPublicKey;
}

const auctionDataStruct = borsh.struct<AuctionData>(
[
['authority', 'pubkeyAsString'],
['tokenMint', 'pubkeyAsString'],
['lastBid', { kind: 'option', type: 'u64' }],
['endedAt', { kind: 'option', type: 'u64' }],
['endAuctionAt', { kind: 'option', type: 'u64' }],
['auctionGap', { kind: 'option', type: 'u64' }],
['priceFloor', priceFloorStruct.type],
['state', 'u8'],
['bidState', bidStateStruct.type],
],
[priceFloorStruct, bidStateStruct],
);

export class Auction extends Account<AuctionData> {
static readonly EXTENDED_DATA_SIZE = 8 + 9 + 2 + 200;

Expand All @@ -126,7 +172,7 @@ export class Auction extends Account<AuctionData> {
throw ERROR_INVALID_OWNER();
}

this.data = auctionDataStruct.deserialize(this.info.data);
this.data = AuctionData.deserialize(this.info.data);
}

static getPDA(vault: AnyPublicKey) {
Expand Down
23 changes: 14 additions & 9 deletions api/src/programs/auction/accouns/AuctionExtended.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,27 @@ import { AccountInfo, PublicKey } from '@solana/web3.js';
import BN from 'bn.js';
import { ERROR_INVALID_ACCOUNT_DATA, ERROR_INVALID_OWNER } from '@metaplex/errors';
import { AnyPublicKey } from '@metaplex/types';
import { borsh } from '@metaplex/utils';
import { Borsh } from '@metaplex/utils';
import { Account } from '../../../Account';
import { AuctionProgram } from '../AuctionProgram';
import { Buffer } from 'buffer';

export interface AuctionDataExtended {
type Args = {
totalUncancelledBids: BN;
tickSize: BN | null;
gapTickSizePercentage: number | null;
}
};
export class AuctionDataExtended extends Borsh.Data<Args> {
static readonly SCHEMA = this.struct([
['totalUncancelledBids', 'u64'],
['tickSize', { kind: 'option', type: 'u64' }],
['gapTickSizePercentage', { kind: 'option', type: 'u8' }],
]);

const auctionDataExtendedStruct = borsh.struct<AuctionDataExtended>([
['totalUncancelledBids', 'u64'],
['tickSize', { kind: 'option', type: 'u64' }],
['gapTickSizePercentage', { kind: 'option', type: 'u8' }],
]);
totalUncancelledBids: BN;
tickSize: BN | null;
gapTickSizePercentage: number | null;
}

export class AuctionExtended extends Account<AuctionDataExtended> {
static readonly DATA_SIZE = 8 + 9 + 2 + 200;
Expand All @@ -33,7 +38,7 @@ export class AuctionExtended extends Account<AuctionDataExtended> {
throw ERROR_INVALID_ACCOUNT_DATA();
}

this.data = auctionDataExtendedStruct.deserialize(this.info.data);
this.data = AuctionDataExtended.deserialize(this.info.data);
}

static isCompatible(data: Buffer) {
Expand Down
29 changes: 18 additions & 11 deletions api/src/programs/auction/accouns/BidderMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,27 @@ import { AccountInfo } from '@solana/web3.js';
import BN from 'bn.js';
import { Account } from '../../../Account';
import { AnyPublicKey, StringPublicKey } from '@metaplex/types';
import { borsh } from '@metaplex/utils';
import { Borsh } from '@metaplex/utils';
import { AuctionProgram } from '../AuctionProgram';
import { ERROR_INVALID_ACCOUNT_DATA, ERROR_INVALID_OWNER } from '@metaplex/errors';
import { Buffer } from 'buffer';

export interface BidderMetadataData {
type Args = {
bidderPubkey: StringPublicKey;
auctionPubkey: StringPublicKey;
lastBid: BN;
lastBidTimestamp: BN;
cancelled: boolean;
};
export class BidderMetadataData extends Borsh.Data<Args> {
static readonly SCHEMA = this.struct([
['bidderPubkey', 'pubkeyAsString'],
['auctionPubkey', 'pubkeyAsString'],
['lastBid', 'u64'],
['lastBidTimestamp', 'u64'],
['cancelled', 'u8'],
]);

// Relationship with the bidder who's metadata this covers.
bidderPubkey: StringPublicKey;
// Relationship with the auction this bid was placed on.
Expand All @@ -21,14 +36,6 @@ export interface BidderMetadataData {
cancelled: boolean;
}

const bidderMetadataStruct = borsh.struct<BidderMetadataData>([
['bidderPubkey', 'pubkeyAsString'],
['auctionPubkey', 'pubkeyAsString'],
['lastBid', 'u64'],
['lastBidTimestamp', 'u64'],
['cancelled', 'u8'],
]);

export class BidderMetadata extends Account<BidderMetadataData> {
static readonly DATA_SIZE = 32 + 32 + 8 + 8 + 1;

Expand All @@ -43,7 +50,7 @@ export class BidderMetadata extends Account<BidderMetadataData> {
throw ERROR_INVALID_ACCOUNT_DATA();
}

this.data = bidderMetadataStruct.deserialize(this.info.data);
this.data = BidderMetadataData.deserialize(this.info.data);
}

static isCompatible(data: Buffer) {
Expand Down
28 changes: 17 additions & 11 deletions api/src/programs/auction/accouns/BidderPot.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
import { borsh } from '@metaplex/utils';
import { Borsh } from '@metaplex/utils';
import { AnyPublicKey, StringPublicKey } from '@metaplex/types';
import { AuctionProgram } from '../AuctionProgram';
import { AccountInfo } from '@solana/web3.js';
import { Account } from '../../../Account';
import { ERROR_INVALID_ACCOUNT_DATA, ERROR_INVALID_OWNER } from '@metaplex/errors';
import { Buffer } from 'buffer';

export interface BiddePotData {
type Args = {
bidderPot: StringPublicKey;
bidderAct: StringPublicKey;
auctionAct: StringPublicKey;
emptied: boolean;
};
export class BidderPotData extends Borsh.Data<Args> {
static readonly SCHEMA = this.struct([
['bidderPot', 'pubkeyAsString'],
['bidderAct', 'pubkeyAsString'],
['auctionAct', 'pubkeyAsString'],
['emptied', 'u8'],
]);

/// Points at actual pot that is a token account
bidderPot: StringPublicKey;
bidderAct: StringPublicKey;
auctionAct: StringPublicKey;
emptied: boolean;
}

const bidderPotStruct = borsh.struct<BiddePotData>([
['bidderPot', 'pubkeyAsString'],
['bidderAct', 'pubkeyAsString'],
['auctionAct', 'pubkeyAsString'],
['emptied', 'u8'],
]);

export class BidderPot extends Account<BiddePotData> {
export class BidderPot extends Account<BidderPotData> {
static readonly DATA_SIZE = 32 + 32 + 32 + 1;

constructor(key: AnyPublicKey, info: AccountInfo<Buffer>) {
Expand All @@ -35,7 +41,7 @@ export class BidderPot extends Account<BiddePotData> {
throw ERROR_INVALID_ACCOUNT_DATA();
}

this.data = bidderPotStruct.deserialize(this.info.data);
this.data = BidderPotData.deserialize(this.info.data);
}

static isCompatible(data: Buffer) {
Expand Down
Loading

0 comments on commit 45db5e5

Please sign in to comment.