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

View File

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

View File

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