Intermediate commit, working through auction manager super create

This commit is contained in:
Jordan Prince 2021-04-24 13:21:35 -05:00
parent 85be78d001
commit 87bb0870c6
8 changed files with 307 additions and 38 deletions

View File

@ -1,2 +1,3 @@
export * from './account'; export * from './account';
export * from './metadata'; export * from './metadata';
export * from './vault';

View File

@ -21,7 +21,7 @@ export const MAX_METADATA_LEN =
export const MAX_NAME_SYMBOL_LEN = 1 + 32 + 8; export const MAX_NAME_SYMBOL_LEN = 1 + 32 + 8;
export const MAX_MASTER_EDITION_KEN = 1 + 9 + 8 + 32; export const MAX_MASTER_EDITION_KEN = 1 + 9 + 8 + 32;
export enum Key { export enum MetadataKey {
MetadataV1 = 0, MetadataV1 = 0,
NameSymbolTupleV1 = 1, NameSymbolTupleV1 = 1,
EditionV1 = 2, EditionV1 = 2,
@ -48,27 +48,27 @@ export interface IMetadataExtension {
} }
export class MasterEdition { export class MasterEdition {
key: Key; key: MetadataKey;
supply: BN; supply: BN;
maxSupply?: BN; maxSupply?: BN;
/// Can be used to mint tokens that give one-time permission to mint a single limited edition. /// Can be used to mint tokens that give one-time permission to mint a single limited edition.
masterMint: PublicKey; masterMint: PublicKey;
constructor(args: { constructor(args: {
key: Key; key: MetadataKey;
supply: BN; supply: BN;
maxSupply?: BN; maxSupply?: BN;
/// Can be used to mint tokens that give one-time permission to mint a single limited edition. /// Can be used to mint tokens that give one-time permission to mint a single limited edition.
masterMint: PublicKey; masterMint: PublicKey;
}) { }) {
this.key = Key.MasterEditionV1; this.key = MetadataKey.MasterEditionV1;
this.supply = args.supply; this.supply = args.supply;
this.maxSupply = args.maxSupply; this.maxSupply = args.maxSupply;
this.masterMint = args.masterMint; this.masterMint = args.masterMint;
} }
} }
export class Metadata { export class Metadata {
key: Key; key: MetadataKey;
nonUniqueSpecificUpdateAuthority?: PublicKey; nonUniqueSpecificUpdateAuthority?: PublicKey;
mint: PublicKey; mint: PublicKey;
@ -85,7 +85,7 @@ export class Metadata {
symbol: string; symbol: string;
uri: string; uri: string;
}) { }) {
this.key = Key.MetadataV1; this.key = MetadataKey.MetadataV1;
this.nonUniqueSpecificUpdateAuthority = this.nonUniqueSpecificUpdateAuthority =
args.nonUniqueSpecificUpdateAuthority; args.nonUniqueSpecificUpdateAuthority;
this.mint = args.mint; this.mint = args.mint;
@ -96,12 +96,12 @@ export class Metadata {
} }
export class NameSymbolTuple { export class NameSymbolTuple {
key: Key; key: MetadataKey;
updateAuthority: PublicKey; updateAuthority: PublicKey;
metadata: PublicKey; metadata: PublicKey;
constructor(args: { updateAuthority: Buffer; metadata: Buffer }) { constructor(args: { updateAuthority: Buffer; metadata: Buffer }) {
this.key = Key.NameSymbolTupleV1; this.key = MetadataKey.NameSymbolTupleV1;
this.updateAuthority = new PublicKey(args.updateAuthority); this.updateAuthority = new PublicKey(args.updateAuthority);
this.metadata = new PublicKey(args.metadata); this.metadata = new PublicKey(args.metadata);
} }
@ -156,7 +156,7 @@ class CreateMasterEditionArgs {
} }
} }
export const SCHEMA = new Map<any, any>([ export const METADATA_SCHEMA = new Map<any, any>([
[ [
CreateMetadataArgs, CreateMetadataArgs,
{ {
@ -244,7 +244,7 @@ export const SCHEMA = new Map<any, any>([
]); ]);
export const decodeMetadata = (buffer: Buffer) => { export const decodeMetadata = (buffer: Buffer) => {
return deserializeBorsh(SCHEMA, Metadata, buffer) as Metadata; return deserializeBorsh(METADATA_SCHEMA, Metadata, buffer) as Metadata;
}; };
export async function transferUpdateAuthority( export async function transferUpdateAuthority(
@ -256,7 +256,7 @@ export async function transferUpdateAuthority(
const metadataProgramId = programIds().metadata; const metadataProgramId = programIds().metadata;
const data = Buffer.from( const data = Buffer.from(
serialize(SCHEMA, new TransferUpdateAuthorityArgs()), serialize(METADATA_SCHEMA, new TransferUpdateAuthorityArgs()),
); );
const keys = [ const keys = [
@ -331,7 +331,7 @@ export async function updateMetadata(
? undefined ? undefined
: newNonUniqueSpecificUpdateAuthority, : newNonUniqueSpecificUpdateAuthority,
}); });
const data = Buffer.from(serialize(SCHEMA, value)); const data = Buffer.from(serialize(METADATA_SCHEMA, value));
const keys = [ const keys = [
{ {
pubkey: metadataAccount, pubkey: metadataAccount,
@ -397,7 +397,7 @@ export async function createMetadata(
)[0]; )[0];
const value = new CreateMetadataArgs({ name, symbol, uri, allowDuplicates }); const value = new CreateMetadataArgs({ name, symbol, uri, allowDuplicates });
const data = Buffer.from(serialize(SCHEMA, value)); const data = Buffer.from(serialize(METADATA_SCHEMA, value));
const keys = [ const keys = [
{ {
@ -501,7 +501,7 @@ export async function createMasterEdition(
)[0]; )[0];
const value = new CreateMasterEditionArgs({ maxSupply }); const value = new CreateMasterEditionArgs({ maxSupply });
const data = Buffer.from(serialize(SCHEMA, value)); const data = Buffer.from(serialize(METADATA_SCHEMA, value));
const keys = [ const keys = [
{ {

View File

@ -9,8 +9,8 @@ import { deserializeBorsh } from './../utils/borsh';
import { serialize } from 'borsh'; import { serialize } from 'borsh';
import BN from 'bn.js'; import BN from 'bn.js';
const PREFIX = 'vault'; export const VAULT_PREFIX = 'vault';
export enum Key { export enum VaultKey {
VaultV1 = 0, VaultV1 = 0,
SafetyDepositBoxV1 = 1, SafetyDepositBoxV1 = 1,
ExternalPriceAccountV1 = 2, ExternalPriceAccountV1 = 2,
@ -23,8 +23,12 @@ export enum VaultState {
Deactivated = 3, Deactivated = 3,
} }
export const MAX_VAULT_SIZE =
1 + 32 + 32 + 32 + 32 + 1 + 32 + 1 + 32 + 1 + 1 + 8;
export const MAX_EXTERNAL_ACCOUNT_SIZE = 1 + 8 + 32 + 1;
export class Vault { export class Vault {
key: Key; key: VaultKey;
/// Store token program used /// Store token program used
tokenProgram: PublicKey; tokenProgram: PublicKey;
/// Mint that produces the fractional shares /// Mint that produces the fractional shares
@ -65,7 +69,7 @@ export class Vault {
state: VaultState; state: VaultState;
lockedPricePerShare: BN; lockedPricePerShare: BN;
}) { }) {
this.key = Key.VaultV1; this.key = VaultKey.VaultV1;
this.tokenProgram = args.tokenProgram; this.tokenProgram = args.tokenProgram;
this.fractionMint = args.fractionMint; this.fractionMint = args.fractionMint;
this.authority = args.authority; this.authority = args.authority;
@ -80,8 +84,8 @@ export class Vault {
} }
export class SafetyDepositBox { export class SafetyDepositBox {
/// Each token type in a vault has it's own box that contains it's mint and a look-back /// Each token type in a vault has it's own box that contains it's mint and a look-back
key: Key; key: VaultKey;
/// Key pointing to the parent vault /// VaultKey pointing to the parent vault
vault: PublicKey; vault: PublicKey;
/// This particular token's mint /// This particular token's mint
tokenMint: PublicKey; tokenMint: PublicKey;
@ -96,7 +100,7 @@ export class SafetyDepositBox {
store: PublicKey; store: PublicKey;
order: number; order: number;
}) { }) {
this.key = Key.SafetyDepositBoxV1; this.key = VaultKey.SafetyDepositBoxV1;
this.vault = args.vault; this.vault = args.vault;
this.tokenMint = args.tokenMint; this.tokenMint = args.tokenMint;
this.store = args.store; this.store = args.store;
@ -105,7 +109,7 @@ export class SafetyDepositBox {
} }
export class ExternalPriceAccount { export class ExternalPriceAccount {
key: Key; key: VaultKey;
pricePerShare: BN; pricePerShare: BN;
/// Mint of the currency we are pricing the shares against, should be same as redeem_treasury. /// Mint of the currency we are pricing the shares against, should be same as redeem_treasury.
/// Most likely will be USDC mint most of the time. /// Most likely will be USDC mint most of the time.
@ -118,7 +122,7 @@ export class ExternalPriceAccount {
priceMint: PublicKey; priceMint: PublicKey;
allowedToCombine: boolean; allowedToCombine: boolean;
}) { }) {
this.key = Key.ExternalPriceAccountV1; this.key = VaultKey.ExternalPriceAccountV1;
this.pricePerShare = args.pricePerShare; this.pricePerShare = args.pricePerShare;
this.priceMint = args.priceMint; this.priceMint = args.priceMint;
this.allowedToCombine = args.allowedToCombine; this.allowedToCombine = args.allowedToCombine;
@ -163,7 +167,7 @@ class UpdateExternalPriceAccountArgs {
} }
} }
export const SCHEMA = new Map<any, any>([ export const VAULT_SCHEMA = new Map<any, any>([
[ [
InitVaultArgs, InitVaultArgs,
{ {
@ -254,7 +258,7 @@ export const SCHEMA = new Map<any, any>([
]); ]);
export const decodeVault = (buffer: Buffer) => { export const decodeVault = (buffer: Buffer) => {
return deserializeBorsh(SCHEMA, Vault, buffer) as Vault; return deserializeBorsh(VAULT_SCHEMA, Vault, buffer) as Vault;
}; };
export async function initVault( export async function initVault(
@ -270,7 +274,7 @@ export async function initVault(
const vaultProgramId = programIds().vault; const vaultProgramId = programIds().vault;
const data = Buffer.from( const data = Buffer.from(
serialize(SCHEMA, new InitVaultArgs({ allowFurtherShareCreation })), serialize(VAULT_SCHEMA, new InitVaultArgs({ allowFurtherShareCreation })),
); );
const keys = [ const keys = [
@ -340,7 +344,7 @@ export async function addTokenToInactiveVault(
const safetyDepositBox: PublicKey = ( const safetyDepositBox: PublicKey = (
await PublicKey.findProgramAddress( await PublicKey.findProgramAddress(
[Buffer.from(PREFIX), vault.toBuffer(), tokenMint.toBuffer()], [Buffer.from(VAULT_PREFIX), vault.toBuffer(), tokenMint.toBuffer()],
vaultProgramId, vaultProgramId,
) )
)[0]; )[0];
@ -350,7 +354,7 @@ export async function addTokenToInactiveVault(
amount, amount,
}); });
const data = Buffer.from(serialize(SCHEMA, value)); const data = Buffer.from(serialize(VAULT_SCHEMA, value));
const keys = [ const keys = [
{ {
pubkey: safetyDepositBox, pubkey: safetyDepositBox,
@ -424,13 +428,13 @@ export async function activateVault(
const fractionMintAuthority = ( const fractionMintAuthority = (
await PublicKey.findProgramAddress( await PublicKey.findProgramAddress(
[Buffer.from(PREFIX), vaultProgramId.toBuffer()], [Buffer.from(VAULT_PREFIX), vaultProgramId.toBuffer()],
vaultProgramId, vaultProgramId,
) )
)[0]; )[0];
const value = new NumberOfShareArgs({ instruction: 2, numberOfShares }); const value = new NumberOfShareArgs({ instruction: 2, numberOfShares });
const data = Buffer.from(serialize(SCHEMA, value)); const data = Buffer.from(serialize(VAULT_SCHEMA, value));
const keys = [ const keys = [
{ {
@ -490,7 +494,7 @@ export async function combineVault(
const burnAuthority = ( const burnAuthority = (
await PublicKey.findProgramAddress( await PublicKey.findProgramAddress(
[Buffer.from(PREFIX), vaultProgramId.toBuffer()], [Buffer.from(VAULT_PREFIX), vaultProgramId.toBuffer()],
vaultProgramId, vaultProgramId,
) )
)[0]; )[0];
@ -582,13 +586,13 @@ export async function withdrawTokenFromSafetyDepositBox(
const transferAuthority = ( const transferAuthority = (
await PublicKey.findProgramAddress( await PublicKey.findProgramAddress(
[Buffer.from(PREFIX), vaultProgramId.toBuffer()], [Buffer.from(VAULT_PREFIX), vaultProgramId.toBuffer()],
vaultProgramId, vaultProgramId,
) )
)[0]; )[0];
const value = new AmountArgs({ instruction: 5, amount }); const value = new AmountArgs({ instruction: 5, amount });
const data = Buffer.from(serialize(SCHEMA, value)); const data = Buffer.from(serialize(VAULT_SCHEMA, value));
const keys = [ const keys = [
{ {
@ -654,7 +658,7 @@ export async function updateExternalPriceAccount(
const vaultProgramId = programIds().vault; const vaultProgramId = programIds().vault;
const value = new UpdateExternalPriceAccountArgs({ externalPriceAccount }); const value = new UpdateExternalPriceAccountArgs({ externalPriceAccount });
const data = Buffer.from(serialize(SCHEMA, value)); const data = Buffer.from(serialize(VAULT_SCHEMA, value));
const keys = [ const keys = [
{ {

View File

@ -0,0 +1,110 @@
import {
Account,
Connection,
PublicKey,
TransactionInstruction,
} from '@solana/web3.js';
import { contexts, utils, actions, models } from '@oyster/common';
import { AccountLayout } from '@solana/spl-token';
import BN from 'bn.js';
const {
createTokenAccount,
activateVault,
addTokenToInactiveVault,
VAULT_PREFIX,
} = actions;
const { approve } = models;
const BATCH_SIZE = 4;
// This command batches out adding tokens to a vault using a prefilled payer account, and then activates
// the vault for use. It issues a series of transaction instructions and signers for the sendTransactions batch.
export async function addTokensToVault(
connection: Connection,
wallet: any,
vault: PublicKey,
fractionMint: PublicKey,
fractionTreasury: PublicKey,
nfts: { tokenAccount: PublicKey; tokenMint: PublicKey; amount: BN }[],
): Promise<{
instructions: Array<TransactionInstruction[]>;
signers: Array<Account[]>;
}> {
const PROGRAM_IDS = utils.programIds();
const accountRentExempt = await connection.getMinimumBalanceForRentExemption(
AccountLayout.span,
);
const vaultAuthority = (
await PublicKey.findProgramAddress(
[Buffer.from(VAULT_PREFIX), PROGRAM_IDS.vault.toBuffer()],
PROGRAM_IDS.vault,
)
)[0];
let batchCounter = 0;
let signers: Array<Account[]> = [];
let instructions: Array<TransactionInstruction[]> = [];
let currSigners: Account[] = [];
let currInstructions: TransactionInstruction[] = [];
nfts.forEach(nft => {
const newStoreAccount = createTokenAccount(
currInstructions,
wallet.publicKey,
accountRentExempt,
nft.tokenMint,
vaultAuthority,
currSigners,
);
const transferAuthority = approve(
currInstructions,
[],
nft.tokenAccount,
wallet.publicKey,
nft.amount.toNumber(),
);
currSigners.push(transferAuthority);
addTokenToInactiveVault(
nft.amount,
nft.tokenMint,
nft.tokenAccount,
newStoreAccount,
vault,
vaultAuthority,
wallet.publicKey,
transferAuthority.publicKey,
currInstructions,
);
if (batchCounter == BATCH_SIZE) {
signers.push(currSigners);
instructions.push(currInstructions);
batchCounter = 0;
currSigners = [];
currInstructions = [];
}
});
currSigners = [];
currInstructions = [];
activateVault(
new BN(0),
vault,
fractionMint,
fractionTreasury,
vaultAuthority,
currInstructions,
);
signers.push(currSigners);
instructions.push(currInstructions);
return { signers, instructions };
}

View File

@ -0,0 +1,157 @@
import {
Account,
Connection,
PublicKey,
SystemProgram,
TransactionInstruction,
} from '@solana/web3.js';
import { contexts, utils, actions, createMint } from '@oyster/common';
import { AccountLayout, MintLayout } from '@solana/spl-token';
import BN from 'bn.js';
const {
createTokenAccount,
initVault,
updateExternalPriceAccount,
ExternalPriceAccount,
MAX_VAULT_SIZE,
VAULT_PREFIX,
MAX_EXTERNAL_ACCOUNT_SIZE,
} = actions;
// This command creates the external pricing oracle a vault
// This gets the vault ready for adding the tokens.
export async function createVault(
connection: Connection,
wallet: any,
): Promise<{
vault: PublicKey;
externalPriceAccount: PublicKey;
fractionalMint: PublicKey;
redeemTreasury: PublicKey;
fractionTreasury: PublicKey;
priceMint: PublicKey;
instructions: TransactionInstruction[];
signers: Account[];
}> {
const PROGRAM_IDS = utils.programIds();
let signers: Account[] = [];
let instructions: TransactionInstruction[] = [];
const accountRentExempt = await connection.getMinimumBalanceForRentExemption(
AccountLayout.span,
);
const mintRentExempt = await connection.getMinimumBalanceForRentExemption(
MintLayout.span,
);
const vaultRentExempt = await connection.getMinimumBalanceForRentExemption(
MAX_VAULT_SIZE,
);
const epaRentExempt = await connection.getMinimumBalanceForRentExemption(
MAX_EXTERNAL_ACCOUNT_SIZE,
);
let vault = new Account();
let externalPriceAccount = new Account();
const vaultAuthority = (
await PublicKey.findProgramAddress(
[Buffer.from(VAULT_PREFIX), PROGRAM_IDS.vault.toBuffer()],
PROGRAM_IDS.vault,
)
)[0];
const fractionalMint = createMint(
instructions,
wallet.publicKey,
mintRentExempt,
0,
vaultAuthority,
vaultAuthority,
signers,
);
const priceMint = createMint(
instructions,
wallet.publicKey,
mintRentExempt,
0,
vaultAuthority,
vaultAuthority,
signers,
);
let epaStruct = new ExternalPriceAccount({
pricePerShare: new BN(0),
priceMint: priceMint,
allowedToCombine: true,
});
const redeemTreasury = createTokenAccount(
instructions,
wallet.publicKey,
accountRentExempt,
priceMint,
vaultAuthority,
signers,
);
const fractionTreasury = createTokenAccount(
instructions,
wallet.publicKey,
accountRentExempt,
fractionalMint,
vaultAuthority,
signers,
);
const uninitializedVault = SystemProgram.createAccount({
fromPubkey: wallet.publicKey,
newAccountPubkey: vault.publicKey,
lamports: vaultRentExempt,
space: MAX_VAULT_SIZE,
programId: PROGRAM_IDS.vault,
});
const uninitializedEPA = SystemProgram.createAccount({
fromPubkey: wallet.publicKey,
newAccountPubkey: vault.publicKey,
lamports: epaRentExempt,
space: MAX_EXTERNAL_ACCOUNT_SIZE,
programId: PROGRAM_IDS.vault,
});
instructions.push(uninitializedVault);
instructions.push(uninitializedEPA);
signers.push(vault);
signers.push(externalPriceAccount);
await updateExternalPriceAccount(
externalPriceAccount.publicKey,
epaStruct,
instructions,
);
await initVault(
true,
fractionalMint,
redeemTreasury,
fractionTreasury,
vault.publicKey,
wallet.publicKey,
externalPriceAccount.publicKey,
instructions,
);
return {
vault: vault.publicKey,
externalPriceAccount: externalPriceAccount.publicKey,
fractionalMint,
redeemTreasury,
fractionTreasury,
priceMint,
signers,
instructions,
};
}

View File

@ -1,3 +1,3 @@
export * from './nft'; export * from './nft';
export * from './vault'; export * from './createVault';
export * from './auction'; export * from './auction';

View File

@ -1,3 +0,0 @@
export const createVault = () => {
// TODO:
};