Skip to content

Commit

Permalink
feat: port addTokensToVault action to SDK (#128)
Browse files Browse the repository at this point in the history
  • Loading branch information
tbiedukhin authored Jan 5, 2022
1 parent 292d5c7 commit d5c8983
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 0 deletions.
101 changes: 101 additions & 0 deletions src/actions/addTokensToVault.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import BN from 'bn.js';
import { AccountLayout } from '@solana/spl-token';
import {
Vault,
SafetyDepositBox,
AddTokenToInactiveVault,
} from '@metaplex-foundation/mpl-token-vault';
import { Connection, TransactionSignature, PublicKey, Keypair } from '@solana/web3.js';

import { Wallet } from '../wallet';
import { createApproveTxs } from './shared';
import { sendTransaction } from './transactions';
import { CreateTokenAccount } from '../programs';
import { TransactionsBatch } from '../utils/transactions-batch';

interface Token2Add {
tokenAccount: PublicKey;
tokenMint: PublicKey;
amount: BN;
}

interface SafetyDepositTokenStore {
txId: TransactionSignature;
tokenAccount: PublicKey;
tokenStoreAccount: PublicKey;
tokenMint: PublicKey;
}

interface AddTokensToVaultParams {
connection: Connection;
wallet: Wallet;
vault: PublicKey;
nfts: Token2Add[];
}

interface AddTokensToVaultResponse {
safetyDepositTokenStores: SafetyDepositTokenStore[];
}

export const addTokensToVault = async ({
connection,
wallet,
vault,
nfts,
}: AddTokensToVaultParams): Promise<AddTokensToVaultResponse> => {
const txOptions = { feePayer: wallet.publicKey };
const safetyDepositTokenStores: SafetyDepositTokenStore[] = [];

const vaultAuthority = await Vault.getPDA(vault);
const accountRent = await connection.getMinimumBalanceForRentExemption(AccountLayout.span);

for (const nft of nfts) {
const tokenTxBatch = new TransactionsBatch({ transactions: [] });
const safetyDepositBox = await SafetyDepositBox.getPDA(vault, nft.tokenMint);

const tokenStoreAccount = Keypair.generate();
const tokenStoreAccountTx = new CreateTokenAccount(txOptions, {
newAccountPubkey: tokenStoreAccount.publicKey,
lamports: accountRent,
mint: nft.tokenMint,
owner: vaultAuthority,
});
tokenTxBatch.addTransaction(tokenStoreAccountTx);
tokenTxBatch.addSigner(tokenStoreAccount);

const { authority: transferAuthority, createApproveTx } = createApproveTxs({
account: nft.tokenAccount,
owner: wallet.publicKey,
amount: nft.amount.toNumber(),
});
tokenTxBatch.addTransaction(createApproveTx);
tokenTxBatch.addSigner(transferAuthority);

const addTokenTx = new AddTokenToInactiveVault(txOptions, {
vault,
vaultAuthority: wallet.publicKey,
tokenAccount: nft.tokenAccount,
tokenStoreAccount: tokenStoreAccount.publicKey,
transferAuthority: transferAuthority.publicKey,
safetyDepositBox: safetyDepositBox,
amount: nft.amount,
});
tokenTxBatch.addTransaction(addTokenTx);

const txId = await sendTransaction({
connection,
wallet,
txs: tokenTxBatch.transactions,
signers: tokenTxBatch.signers,
});

safetyDepositTokenStores.push({
txId,
tokenStoreAccount: tokenStoreAccount.publicKey,
tokenMint: nft.tokenMint,
tokenAccount: nft.tokenAccount,
});
}

return { safetyDepositTokenStores };
};
1 change: 1 addition & 0 deletions src/actions/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './addTokensToVault';
export * from './transactions';
export * from './initStore';
export * from './initStoreV2';
Expand Down
66 changes: 66 additions & 0 deletions test/actions/addTokensToVault.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import BN from 'bn.js';
import { Transaction } from '@metaplex-foundation/mpl-core';
import { airdrop, LOCALHOST } from '@metaplex-foundation/amman';
import { Keypair, sendAndConfirmTransaction } from '@solana/web3.js';

import { Connection, NodeWallet } from '../../src';
import {
addTokensToVault,
createExternalPriceAccount,
createVault,
prepareTokenAccountAndMintTxs,
} from '../../src/actions';

describe('addTokensToVault action', () => {
test('creation and adding of multiple mint tokens to newly created vault', async () => {
const payer = Keypair.generate();
const wallet = new NodeWallet(payer);
const connection = new Connection(LOCALHOST, 'confirmed');
await airdrop(connection, payer.publicKey, 10);

const TOKEN_AMOUNT = 2;
const externalPriceAccountData = await createExternalPriceAccount({ connection, wallet });

const { vault } = await createVault({
connection,
wallet,
...externalPriceAccountData,
});

const testNfts = [];

for (let i = 0; i < TOKEN_AMOUNT; i++) {
const {
mint,
recipient: tokenAccount,
createAssociatedTokenAccountTx,
createMintTx,
mintToTx,
} = await prepareTokenAccountAndMintTxs(connection, wallet.publicKey);

await sendAndConfirmTransaction(
connection,
Transaction.fromCombined([createMintTx, createAssociatedTokenAccountTx, mintToTx]),
[payer, mint, wallet.payer],
);

testNfts.push({
tokenAccount,
tokenMint: mint.publicKey,
amount: new BN(1),
});
}

const { safetyDepositTokenStores } = await addTokensToVault({
connection,
wallet,
vault,
nfts: testNfts,
});

expect(safetyDepositTokenStores.length).toEqual(testNfts.length);
expect(safetyDepositTokenStores.map(({ tokenMint }) => tokenMint).join(',')).toEqual(
testNfts.map(({ tokenMint }) => tokenMint).join(','),
);
});
});

0 comments on commit d5c8983

Please sign in to comment.