@solana/spl-token#ASSOCIATED_TOKEN_PROGRAM_ID TypeScript Examples

The following examples show how to use @solana/spl-token#ASSOCIATED_TOKEN_PROGRAM_ID. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.ts    From zo-client with Apache License 2.0 6 votes vote down vote up
export async function findAssociatedTokenAddress(
  walletAddress: PublicKey,
  tokenMintAddress: PublicKey,
): Promise<PublicKey> {
  return (
    await PublicKey.findProgramAddress(
      [
        walletAddress.toBuffer(),
        TOKEN_PROGRAM_ID.toBuffer(),
        tokenMintAddress.toBuffer(),
      ],
      ASSOCIATED_TOKEN_PROGRAM_ID,
    )
  )[0];
}
Example #2
Source File: model.spec.ts    From kin-node with MIT License 6 votes vote down vote up
test("parseTransaction create without close auth", async() => {
    const [subsidizer, wallet, mint] = generateKeys(3);

    const [createInstructions, addr] = await generateCreateInstructions(subsidizer, wallet, mint);
    const assoc = await Token.getAssociatedTokenAddress(ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, mint, wallet);
    const createAssocInstruction = Token.createAssociatedTokenAccountInstruction(
        ASSOCIATED_TOKEN_PROGRAM_ID,
        TOKEN_PROGRAM_ID,
        mint,
        assoc,
        wallet,
        subsidizer,
    );

    const txs = [
        new solanaweb3.Transaction({
            feePayer: subsidizer,
        }).add(
            ...createInstructions.slice(0, 2)
        ),
        new solanaweb3.Transaction({
            feePayer: subsidizer,
        }).add(
            createAssocInstruction,
        ),
    ];

    for (let i = 0; i < txs.length; i++) {
        try {
            parseTransaction(txs[i]);
            fail();
        } catch (error) {
            expect(error.toString()).toContain('missing SplToken::SetAuthority(Close) instruction');
        }
    }
});
Example #3
Source File: model.spec.ts    From kin-node with MIT License 6 votes vote down vote up
async function generateCreateInstructions(subsidizer: solanaweb3.PublicKey, wallet: solanaweb3.PublicKey, mint: solanaweb3.PublicKey): Promise<[solanaweb3.TransactionInstruction[], solanaweb3.PublicKey]> {
    const addr = await Token.getAssociatedTokenAddress(ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, mint, wallet);
    const pub = PrivateKey.random().publicKey().solanaKey();
    const instructions = [
        solanaweb3.SystemProgram.createAccount({
            fromPubkey: subsidizer,
            newAccountPubkey: addr,
            lamports: 10,
            space: AccountSize,
            programId: TOKEN_PROGRAM_ID,
        }),
        Token.createInitAccountInstruction(
            TOKEN_PROGRAM_ID,
            mint,
            addr,
            pub,
        ),
        Token.createSetAuthorityInstruction(
            TOKEN_PROGRAM_ID,
            addr,
            subsidizer,
            'CloseAccount',
            pub,
            [],
        ),
        Token.createSetAuthorityInstruction(
            TOKEN_PROGRAM_ID,
            addr,
            wallet,
            'AccountOwner',
            pub,
            [],
        )
    ];
    return [instructions, addr];
}
Example #4
Source File: initializeMarket.ts    From psyoptions with Apache License 2.0 6 votes vote down vote up
getOrAddAssociatedTokenAccountTx = async (
  associatedAddress: PublicKey,
  token: Token,
  payer: PublicKey,
  owner: PublicKey = FEE_OWNER_KEY,
) => {
  // This is the optimum logic, considering TX fee, client-side computation,
  // RPC roundtrips and guaranteed idempotent.
  // Sadly we can't do this atomically;
  try {
    await token.getAccountInfo(associatedAddress);
    return null;
  } catch (err) {
    // INVALID_ACCOUNT_OWNER can be possible if the associatedAddress has
    // already been received some lamports (= became system accounts).
    // Assuming program derived addressing is safe, this is the only case
    // for the INVALID_ACCOUNT_OWNER in this code-path
    if (
      err.message === 'Failed to find account' ||
      err.message === 'Invalid account owner'
    ) {
      // as this isn't atomic, it's possible others can create associated
      // accounts meanwhile
      return Token.createAssociatedTokenAccountInstruction(
        ASSOCIATED_TOKEN_PROGRAM_ID,
        TOKEN_PROGRAM_ID,
        token.publicKey,
        associatedAddress,
        owner,
        payer,
      );
    } else {
      throw err;
    }
  }
}
Example #5
Source File: helpers.ts    From psyoptions with Apache License 2.0 6 votes vote down vote up
initOptionMarket = async (
  program: anchor.Program<PsyAmerican>,
  payer: Keypair,
  optionMarket: OptionMarketV2,
  remainingAccounts: AccountMeta[],
  instructions: TransactionInstruction[]
) => {
  await program.rpc.initializeMarket(
    optionMarket.underlyingAmountPerContract,
    optionMarket.quoteAmountPerContract,
    optionMarket.expirationUnixTimestamp,
    optionMarket.bumpSeed,
    {
      accounts: {
        authority: payer.publicKey,
        underlyingAssetMint: optionMarket.underlyingAssetMint,
        quoteAssetMint: optionMarket.quoteAssetMint,
        optionMint: optionMarket.optionMint,
        writerTokenMint: optionMarket.writerTokenMint,
        quoteAssetPool: optionMarket.quoteAssetPool,
        underlyingAssetPool: optionMarket.underlyingAssetPool,
        optionMarket: optionMarket.key,
        feeOwner: FEE_OWNER_KEY,
        tokenProgram: TOKEN_PROGRAM_ID,
        associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
        rent: SYSVAR_RENT_PUBKEY,
        systemProgram: SystemProgram.programId,
        clock: SYSVAR_CLOCK_PUBKEY,
      },
      remainingAccounts,
      signers: [payer],
      instructions,
    }
  );
}
Example #6
Source File: token-program.ts    From kin-node with MIT License 6 votes vote down vote up
static decodeCreateAssociatedAccount(instruction: TransactionInstruction): CreateAssociatedAccountParams {
        this.checkProgramId(instruction.programId, ASSOCIATED_TOKEN_PROGRAM_ID);
        this.checkKeyLength(instruction.keys, 7);
        
        if (instruction.data.length !== 0) {
            throw new Error(`invalid instruction data size: ${instruction.data.length}`);
        }
        if (!instruction.keys[4].pubkey.equals(SystemProgram.programId)) {
            throw new Error('system program key mismatch');
        }
        if (!instruction.keys[5].pubkey.equals(TOKEN_PROGRAM_ID)) {
            throw new Error('token progrma key mismatch');
        }
        if (!instruction.keys[6].pubkey.equals(SYSVAR_RENT_PUBKEY)) {
            throw new Error('rent sys var mismatch');
        }

        return {
            subsidizer: instruction.keys[0].pubkey,
            address: instruction.keys[1].pubkey,
            owner: instruction.keys[2].pubkey,
            mint: instruction.keys[3].pubkey,
        };
    }
Example #7
Source File: Swap.tsx    From swap-ui with Apache License 2.0 6 votes vote down vote up
export function useReferral(fromMarket?: Market): PublicKey | undefined {
  const { referral } = useSwapContext();
  const asyncReferral = useAsync(async () => {
    if (!referral) {
      return undefined;
    }
    if (!fromMarket) {
      return undefined;
    }
    if (
      !fromMarket.quoteMintAddress.equals(USDC_MINT) &&
      !fromMarket.quoteMintAddress.equals(USDT_MINT)
    ) {
      return undefined;
    }

    return Token.getAssociatedTokenAddress(
      ASSOCIATED_TOKEN_PROGRAM_ID,
      TOKEN_PROGRAM_ID,
      fromMarket.quoteMintAddress,
      referral
    );
  }, [fromMarket]);

  if (!asyncReferral.result) {
    return undefined;
  }
  return asyncReferral.result;
}
Example #8
Source File: associated-token-account.ts    From easy-spl with Apache License 2.0 6 votes vote down vote up
createAssociatedTokenAccountRawInstructions = (
  mint: web3.PublicKey,
  address: web3.PublicKey,
  owner: web3.PublicKey,
  sender: web3.PublicKey,
): web3.TransactionInstruction[] => {
  return [Token.createAssociatedTokenAccountInstruction(
    ASSOCIATED_TOKEN_PROGRAM_ID,
    TOKEN_PROGRAM_ID,
    mint,
    address,
    owner,
    sender
  )]
}
Example #9
Source File: associatedToken.ts    From jet-engine with GNU Affero General Public License v3.0 6 votes vote down vote up
/**
   * Get the address for the associated token account
   * @static
   * @param {Address} mint Token mint account
   * @param {Address} owner Owner of the new account
   * @returns {Promise<PublicKey>} Public key of the associated token account
   * @memberof AssociatedToken
   */
  static derive(mint: Address, owner: Address): PublicKey {
    const mintAddress = translateAddress(mint)
    const ownerAddress = translateAddress(owner)
    return findDerivedAccount(ASSOCIATED_TOKEN_PROGRAM_ID, ownerAddress, TOKEN_PROGRAM_ID, mintAddress)
  }
Example #10
Source File: mockUSDCFaucet.ts    From protocol-v1 with Apache License 2.0 6 votes vote down vote up
public async getAssosciatedMockUSDMintAddress(props: {
		userPubKey: PublicKey;
	}): Promise<anchor.web3.PublicKey> {
		const state: any = await this.fetchState();

		return Token.getAssociatedTokenAddress(
			ASSOCIATED_TOKEN_PROGRAM_ID,
			TOKEN_PROGRAM_ID,
			state.mint,
			props.userPubKey
		);
	}
Example #11
Source File: mockUSDCFaucet.ts    From protocol-v1 with Apache License 2.0 6 votes vote down vote up
public async createAssociatedTokenAccountAndMintToInstructions(
		userPublicKey: PublicKey,
		amount: BN
	): Promise<[PublicKey, TransactionInstruction, TransactionInstruction]> {
		const state: any = await this.fetchState();

		const associateTokenPublicKey = await this.getAssosciatedMockUSDMintAddress(
			{ userPubKey: userPublicKey }
		);

		const createAssociatedAccountIx =
			Token.createAssociatedTokenAccountInstruction(
				ASSOCIATED_TOKEN_PROGRAM_ID,
				TOKEN_PROGRAM_ID,
				state.mint,
				associateTokenPublicKey,
				userPublicKey,
				this.wallet.publicKey
			);

		const mintToIx = await this.program.instruction.mintToUser(amount, {
			accounts: {
				mockUsdcFaucetState: await this.getMockUSDCFaucetStatePublicKey(),
				mintAccount: state.mint,
				userTokenAccount: associateTokenPublicKey,
				mintAuthority: state.mintAuthority,
				tokenProgram: TOKEN_PROGRAM_ID,
			},
		});

		return [associateTokenPublicKey, createAssociatedAccountIx, mintToIx];
	}
Example #12
Source File: cli.ts    From protocol-v1 with Apache License 2.0 6 votes vote down vote up
commandWithDefaultOption('deposit')
	.argument('<amount>', 'The amount to deposit')
	.action(async (amount, options: OptionValues) => {
		await wrapActionInUserSubscribeUnsubscribe(
			options,
			async (user: ClearingHouseUser) => {
				log.info(`amount: ${amount}`);
				amount = new BN(amount);

				const associatedTokenPublicKey = await Token.getAssociatedTokenAddress(
					ASSOCIATED_TOKEN_PROGRAM_ID,
					TOKEN_PROGRAM_ID,
					user.clearingHouse.getStateAccount().collateralMint,
					user.authority
				);

				await user.clearingHouse.depositCollateral(
					amount,
					associatedTokenPublicKey
				);
			}
		);
	});
Example #13
Source File: tokens.ts    From swap-ui with Apache License 2.0 5 votes vote down vote up
export async function getOwnedAssociatedTokenAccounts(
  connection: Connection,
  publicKey: PublicKey
) {
  let filters = getOwnedAccountsFilters(publicKey);
  // @ts-ignore
  let resp = await connection.getProgramAccounts(TOKEN_PROGRAM_ID, {
    commitment: connection.commitment,
    filters,
  });

  const accs = resp
    .map(({ pubkey, account: { data, executable, owner, lamports } }: any) => ({
      publicKey: new PublicKey(pubkey),
      accountInfo: {
        data,
        executable,
        owner: new PublicKey(owner),
        lamports,
      },
    }))
    .map(({ publicKey, accountInfo }: any) => {
      return { publicKey, account: parseTokenAccountData(accountInfo.data) };
    });

  return (
    (
      await Promise.all(
        accs
          // @ts-ignore
          .map(async (ta) => {
            const ata = await Token.getAssociatedTokenAddress(
              ASSOCIATED_TOKEN_PROGRAM_ID,
              TOKEN_PROGRAM_ID,
              ta.account.mint,
              publicKey
            );
            return [ta, ata];
          })
      )
    )
      // @ts-ignore
      .filter(([ta, ata]) => ta.publicKey.equals(ata))
      // @ts-ignore
      .map(([ta]) => ta)
  );
}
Example #14
Source File: mintOption.ts    From psyoptions with Apache License 2.0 5 votes vote down vote up
mintOptionsTx = async (
  program: Program<PsyAmerican>,
  minter: Keypair,
  minterOptionAcct: Keypair,
  minterWriterAcct: Keypair,
  minterUnderlyingAccount: Keypair,
  size: anchor.BN,
  optionMarket: OptionMarketV2,
) => {
  let mintFeeKey: PublicKey,
    remainingAccounts: AccountMeta[] = [];
  const mintFee = feeAmountPerContract(
    optionMarket.underlyingAmountPerContract,
  );
  if (mintFee.gtn(0)) {
    mintFeeKey = await Token.getAssociatedTokenAddress(
      ASSOCIATED_TOKEN_PROGRAM_ID,
      TOKEN_PROGRAM_ID,
      optionMarket.underlyingAssetMint,
      FEE_OWNER_KEY,
    );
    remainingAccounts.push({
      pubkey: mintFeeKey,
      isWritable: true,
      isSigner: false,
    });
  }
  await program.rpc.mintOption(size, {
    accounts: {
      userAuthority: minter.publicKey,
      underlyingAssetMint: optionMarket.underlyingAssetMint,
      underlyingAssetPool: optionMarket.underlyingAssetPool,
      underlyingAssetSrc: minterUnderlyingAccount.publicKey,
      optionMint: optionMarket.optionMint,
      mintedOptionDest: minterOptionAcct.publicKey,
      writerTokenMint: optionMarket.writerTokenMint,
      mintedWriterTokenDest: minterWriterAcct.publicKey,
      optionMarket: optionMarket.key,
      feeOwner: FEE_OWNER_KEY,
      tokenProgram: TOKEN_PROGRAM_ID,
      associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
      clock: SYSVAR_CLOCK_PUBKEY,
      rent: SYSVAR_RENT_PUBKEY,
      systemProgram: SystemProgram.programId,
    },
    remainingAccounts,
    signers: [minter],
  });
}
Example #15
Source File: associated-token-account.ts    From easy-spl with Apache License 2.0 5 votes vote down vote up
getAssociatedTokenAddress = async (
  mint: web3.PublicKey,
  user: web3.PublicKey
): Promise<web3.PublicKey> => {
  return Token.getAssociatedTokenAddress(ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, mint, user)
}
Example #16
Source File: webhook.spec.ts    From kin-node with MIT License 5 votes vote down vote up
test("createAccountHandler rejection", async () => {
    const app = express();
    interface createResponse {
        signature: string
    }

    app.use("/create_account", express.json());
    app.use("/create_account", CreateAccountHandler(Environment.Test, (req: CreateAccountRequest, resp: CreateAccountResponse) => {
        resp.reject();
    }, WEBHOOK_SECRET));
    
    const recentBlockhash = PrivateKey.random().publicKey().solanaKey();
    const owner = PrivateKey.random().publicKey().solanaKey();
    const mint = PrivateKey.random().publicKey().solanaKey();
    const assoc = await Token.getAssociatedTokenAddress(
        ASSOCIATED_TOKEN_PROGRAM_ID,
        TOKEN_PROGRAM_ID,
        mint,
        owner,
    );
    const createTx = new Transaction({
        feePayer: subsidizer.publicKey().solanaKey(),
        recentBlockhash: recentBlockhash.toBase58(),
    }).add(
        Token.createAssociatedTokenAccountInstruction(
            ASSOCIATED_TOKEN_PROGRAM_ID,
            TOKEN_PROGRAM_ID,
            mint,
            assoc,
            owner,
            subsidizer.publicKey().solanaKey(),
        ),
        Token.createSetAuthorityInstruction(
            TOKEN_PROGRAM_ID,
            assoc,
            subsidizer.publicKey().solanaKey(),
            'CloseAccount',
            owner,
            [],
        ),
    );
    
    const req = {
        solana_transaction: createTx.serialize({
            verifySignatures: false,
            requireAllSignatures: false,
        }).toString('base64'),
    };
    
    const resp = await request(app)
        .post("/create_account")
        .set('Accept', 'application/json')
        .set(AGORA_HMAC_HEADER, getHmacHeader(req))
        .send(req)
        .expect(403);

    expect((<createResponse>resp.body).signature).toBeUndefined();
});
Example #17
Source File: index.ts    From zo-client with Apache License 2.0 5 votes vote down vote up
export function getAssociatedTokenTransactionWithPayer(
  tokenMintAddress: PublicKey,
  associatedTokenAddress: PublicKey,
  owner: PublicKey,
) {
  const keys = [
    {
      pubkey: owner,
      isSigner: true,
      isWritable: true,
    },
    {
      pubkey: associatedTokenAddress,
      isSigner: false,
      isWritable: true,
    },
    {
      pubkey: owner,
      isSigner: false,
      isWritable: false,
    },
    {
      pubkey: tokenMintAddress,
      isSigner: false,
      isWritable: false,
    },
    {
      pubkey: SystemProgram.programId,
      isSigner: false,
      isWritable: false,
    },
    {
      pubkey: TOKEN_PROGRAM_ID,
      isSigner: false,
      isWritable: false,
    },
    {
      pubkey: RENT_PROGRAM_ID,
      isSigner: false,
      isWritable: false,
    },
  ];

  return new TransactionInstruction({
    keys,
    programId: ASSOCIATED_TOKEN_PROGRAM_ID,
    data: Buffer.from([]),
  });
}
Example #18
Source File: webhook.spec.ts    From kin-node with MIT License 5 votes vote down vote up
test("createAccountHandler", async () => {
    const app = express();
    interface createResponse {
        signature: string
    }

    let actualUserId: string | undefined;
    let actualUserPasskey: string | undefined;

    app.use("/create_account", express.json());
    app.use("/create_account", CreateAccountHandler(Environment.Test, (req: CreateAccountRequest, resp: CreateAccountResponse) => {
        actualUserId = req.userId;
        actualUserPasskey = req.userPassKey;

        resp.sign(subsidizer);
    }, WEBHOOK_SECRET));

    const recentBlockhash = PrivateKey.random().publicKey().solanaKey();
    const owner = PrivateKey.random().publicKey().solanaKey();
    const mint = PrivateKey.random().publicKey().solanaKey();
    const assoc = await Token.getAssociatedTokenAddress(
        ASSOCIATED_TOKEN_PROGRAM_ID,
        TOKEN_PROGRAM_ID,
        mint,
        owner,
    );
    const createTx = new Transaction({
        feePayer: subsidizer.publicKey().solanaKey(),
        recentBlockhash: recentBlockhash.toBase58(),
    }).add(
        Token.createAssociatedTokenAccountInstruction(
            ASSOCIATED_TOKEN_PROGRAM_ID,
            TOKEN_PROGRAM_ID,
            mint,
            assoc,
            owner,
            subsidizer.publicKey().solanaKey(),
        ),
        Token.createSetAuthorityInstruction(
            TOKEN_PROGRAM_ID,
            assoc,
            subsidizer.publicKey().solanaKey(),
            'CloseAccount',
            owner,
            [],
        ),
    );
    
    const req = {
        solana_transaction: createTx.serialize({
            verifySignatures: false,
            requireAllSignatures: false,
        }).toString('base64'),
    };
    
    const resp = await request(app)
        .post("/create_account")
        .set('Accept', 'application/json')
        .set(AGORA_HMAC_HEADER, getHmacHeader(req))
        .set(AGORA_USER_ID_HEADER, "user_id")
        .set(AGORA_USER_PASSKEY_HEADER, "user_pass_key")
        .send(req)
        .expect(200);

    expect((<createResponse>resp.body).signature).toBeDefined();
    expect(actualUserId).toBe("user_id");
    expect(actualUserPasskey).toBe("user_pass_key");
});
Example #19
Source File: model.spec.ts    From kin-node with MIT License 5 votes vote down vote up
test("parseTransaction create without account holder auth", async() => {
    const [subsidizer, wallet, mint] = generateKeys(3);

    const [createInstructions, addr] = await generateCreateInstructions(subsidizer, wallet, mint);
    const assoc = await Token.getAssociatedTokenAddress(ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, mint, wallet);
    const createAssocInstruction = Token.createAssociatedTokenAccountInstruction(
        ASSOCIATED_TOKEN_PROGRAM_ID,
        TOKEN_PROGRAM_ID,
        mint,
        assoc,
        wallet,
        subsidizer,
    );

    const txs = [
        new solanaweb3.Transaction({
            feePayer: subsidizer,
        }).add(
            ...createInstructions.slice(0, 3)
        ),
        new solanaweb3.Transaction({
            feePayer: subsidizer,
        }).add(
            createAssocInstruction,
            Token.createSetAuthorityInstruction(
                TOKEN_PROGRAM_ID,
                assoc,
                subsidizer,
                'CloseAccount',
                assoc,
                [],
            ),
        ),
    ];

    for (let i = 0; i < txs.length; i++) {
        const [creations, payments] = parseTransaction(txs[i]);
        expect(creations.length).toEqual(1);
        expect(payments.length).toEqual(0);

        if (i === 0) {
            expect(creations[0].owner).toBeDefined();  // randomly generated
            expect(creations[0].address.solanaKey().equals(addr)).toBeTruthy();
        } else {
            expect(creations[0].owner.solanaKey().equals(wallet)).toBeTruthy();
            expect(creations[0].address.solanaKey().equals(assoc)).toBeTruthy();
        }
    }
});
Example #20
Source File: clearingHouse.ts    From protocol-v1 with Apache License 2.0 5 votes vote down vote up
async getInitializeUserInstructions(): Promise<
		[Keypair, PublicKey, TransactionInstruction, TransactionInstruction]
	> {
		const [userAccountPublicKey, userAccountNonce] =
			await getUserAccountPublicKeyAndNonce(
				this.program.programId,
				this.wallet.publicKey
			);

		const remainingAccounts = [];
		const optionalAccounts = {
			whitelistToken: false,
		};

		const state = this.getStateAccount();
		if (state.whitelistMint) {
			optionalAccounts.whitelistToken = true;
			const associatedTokenPublicKey = await Token.getAssociatedTokenAddress(
				ASSOCIATED_TOKEN_PROGRAM_ID,
				TOKEN_PROGRAM_ID,
				state.whitelistMint,
				this.wallet.publicKey
			);
			remainingAccounts.push({
				pubkey: associatedTokenPublicKey,
				isWritable: false,
				isSigner: false,
			});
		}

		const userPositions = new Keypair();
		const initializeUserAccountIx =
			await this.program.instruction.initializeUser(
				userAccountNonce,
				optionalAccounts,
				{
					accounts: {
						user: userAccountPublicKey,
						authority: this.wallet.publicKey,
						rent: anchor.web3.SYSVAR_RENT_PUBKEY,
						systemProgram: anchor.web3.SystemProgram.programId,
						userPositions: userPositions.publicKey,
						state: await this.getStatePublicKey(),
					},
					remainingAccounts: remainingAccounts,
				}
			);

		const initializeUserOrdersAccountIx =
			await this.getInitializeUserOrdersInstruction(userAccountPublicKey);

		return [
			userPositions,
			userAccountPublicKey,
			initializeUserAccountIx,
			initializeUserOrdersAccountIx,
		];
	}
Example #21
Source File: index.ts    From kin-node with MIT License 5 votes vote down vote up
function isSPLAssoc(tx: SolanaTransaction, index: number): boolean {
    return tx.instructions[index].programId.equals(ASSOCIATED_TOKEN_PROGRAM_ID);
}
Example #22
Source File: client.spec.ts    From kin-node with MIT License 5 votes vote down vote up
async function assertMergeTx(tx: Transaction, priv: PrivateKey, infos: accountpbv4.AccountInfo[], createAssoc: boolean, shouldClose: boolean, closeAuth?: Buffer, appSubsidizer?: PrivateKey) {
    let dest: SolanaPublicKey;
    let remainingAccounts: accountpbv4.AccountInfo[] = [];
    let i = 0;
    if (createAssoc) {
        const assoc = await Token.getAssociatedTokenAddress(
            ASSOCIATED_TOKEN_PROGRAM_ID,
            TOKEN_PROGRAM_ID,
            new SolanaPublicKey(token),
            priv.publicKey().solanaKey(),
        );

        // create, set auth, and [transfer, optional[close]] for each account
        if (shouldClose) {
            expect(tx.instructions.length).toEqual(2 * (infos.length + 1));
        } else {
            expect(tx.instructions.length).toEqual(2 + infos.length);
        }

        const createAssoc = TokenInstruction.decodeCreateAssociatedAccount(tx.instructions[i]);
        expect(createAssoc.subsidizer.toBuffer()).toEqual(appSubsidizer ? appSubsidizer.publicKey().buffer : subsidizer);
        expect(createAssoc.address.toBuffer()).toEqual(assoc.toBuffer());
        expect(createAssoc.owner.toBuffer()).toEqual(priv.publicKey().buffer);
        expect(createAssoc.mint.toBuffer()).toEqual(token);

        i++;
        const setAuth = TokenInstruction.decodeSetAuthority(tx.instructions[i]);
        expect(setAuth.account.toBuffer()).toEqual(assoc.toBuffer());
        expect(setAuth.currentAuthority.toBuffer()).toEqual(priv.publicKey().buffer);
        expect(setAuth.authorityType).toEqual('CloseAccount');
        expect(setAuth.newAuthority!.toBuffer()).toEqual(appSubsidizer ? appSubsidizer.publicKey().buffer : subsidizer);
        
        i++;
        dest = assoc;
        remainingAccounts = infos;
    } else {
        // [transfer, optional[close]] for all but one account
        if (shouldClose) {
            expect(tx.instructions.length).toEqual(2 * (infos.length - 1));
        } else {
            expect(tx.instructions.length).toEqual(infos.length - 1);
        }
        
        dest = new SolanaPublicKey(infos[0].getAccountId()!.getValue_asU8());
        remainingAccounts = infos.slice(1);
    }

    remainingAccounts.forEach(info => {
        const transfer = TokenInstruction.decodeTransfer(tx.instructions[i]);
        expect(transfer.source.toBuffer()).toEqual(Buffer.from(info.getAccountId()!.getValue_asU8()));
        expect(transfer.dest).toEqual(dest);
        expect(transfer.owner.toBuffer()).toEqual(priv.publicKey().buffer);
        expect(transfer.amount).toEqual(BigInt(new BigNumber(info.getBalance())));

        i++;

        if (shouldClose) {
            const close = TokenInstruction.decodeCloseAccount(tx.instructions[i]);
            expect(close.account.toBuffer()).toEqual(Buffer.from(info.getAccountId()!.getValue_asU8()));
            expect(close.destination.toBuffer()).toEqual(appSubsidizer ? appSubsidizer.publicKey().buffer : subsidizer);
            expect(close.owner.toBuffer()).toEqual(appSubsidizer ? appSubsidizer.publicKey().buffer : subsidizer);
            i++;
        }
    });
}
Example #23
Source File: internal.spec.ts    From kin-node with MIT License 5 votes vote down vote up
test('createAccount', async () => {
    const account = PrivateKey.random();
    const tokenAccount = await Token.getAssociatedTokenAddress(ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, new PublicKey(token).solanaKey(), account.publicKey().solanaKey());

    const env = newTestEnv();
    const [client, accountClientV4, txClientV4] = [env.client, env.accountClientV4, env.txClientV4];

    setGetServiceConfigResp(txClientV4);
    setGetRecentBlockhashResp(txClientV4);

    let created = false;
    when(accountClientV4.createAccount(anything(), anything(), anything()))
        .thenCall((req: accountpbv4.CreateAccountRequest, md: grpc.Metadata, callback) => {
            const err = validateHeaders(md);
            if (err != undefined) {
                callback(err, undefined);
                return;
            }

            const resp = new accountpbv4.CreateAccountResponse();
            if (created) {
                resp.setResult(accountpbv4.CreateAccountResponse.Result.EXISTS);
            } else {
                const tx = SolanaTransaction.from(req.getTransaction()!.getValue_asU8());
                expect(tx.signatures).toHaveLength(2);
                expect(tx.signatures[0].publicKey.toBuffer()).toEqual(subsidizer);
                expect(tx.signatures[0].signature).toBeNull();

                expect(tx.signatures[1].publicKey.toBuffer()).toEqual(account.publicKey().buffer);
                expect(account.kp.verify(tx.serializeMessage(), tx.signatures[1].signature!)).toBeTruthy();

                expect(tx.instructions).toHaveLength(2);

                const createAssocInstruction = TokenInstruction.decodeCreateAssociatedAccount(tx.instructions[0]);
                expect(createAssocInstruction.subsidizer.toBuffer()).toEqual(subsidizer);
                expect(createAssocInstruction.address).toEqual(tokenAccount);
                expect(createAssocInstruction.owner.toBuffer()).toEqual(account.publicKey().buffer);
                expect(createAssocInstruction.mint.toBuffer()).toEqual(token);

                const setAuthInstruction = TokenInstruction.decodeSetAuthority(tx.instructions[1]);
                expect(setAuthInstruction.account).toEqual(tokenAccount);
                expect(setAuthInstruction.currentAuthority.toBuffer()).toEqual(account.publicKey().buffer);
                expect(setAuthInstruction.newAuthority!.toBuffer()).toEqual(subsidizer);
                expect(setAuthInstruction.authorityType).toEqual('CloseAccount');

                resp.setResult(accountpbv4.CreateAccountResponse.Result.OK);
                created = true;
            }
            callback(undefined, resp);
        });

    await client.createAccount(account);
    expect(created).toBeTruthy();

    try {
        await client.createAccount(account);
        fail();
    } catch (err) {
        expect(err).toBeInstanceOf(AccountExists);
    }
});
Example #24
Source File: internal.spec.ts    From kin-node with MIT License 5 votes vote down vote up
test('createAccount no service subsidizer', async () => {
    const account = PrivateKey.random();
    const tokenAccount = await Token.getAssociatedTokenAddress(ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, new PublicKey(token).solanaKey(), account.publicKey().solanaKey());

    const appSubsidizer = PrivateKey.random();
    const env = newTestEnv();
    const [client, accountClientV4, txClientV4] = [env.client, env.accountClientV4, env.txClientV4];

    setGetServiceConfigRespNoSubsidizer(txClientV4);
    setGetRecentBlockhashResp(txClientV4);

    when(accountClientV4.createAccount(anything(), anything(), anything()))
        .thenCall((req: accountpbv4.CreateAccountRequest, md: grpc.Metadata, callback) => {
            const err = validateHeaders(md);
            if (err != undefined) {
                callback(err, undefined);
                return;
            }

            const resp = new accountpbv4.CreateAccountResponse();

            // This should only be reached if an app subsidizer was passed in
            const tx = SolanaTransaction.from(req.getTransaction()!.getValue_asU8());
            expect(tx.signatures).toHaveLength(2);
            expect(tx.signatures[0].publicKey.toBuffer()).toEqual(appSubsidizer.publicKey().buffer);
            expect(appSubsidizer.kp.verify(tx.serializeMessage(), tx.signatures[0].signature!)).toBeTruthy();

            expect(tx.signatures[1].publicKey.toBuffer()).toEqual(account.publicKey().buffer);
            expect(account.kp.verify(tx.serializeMessage(), tx.signatures[1].signature!)).toBeTruthy();

            expect(tx.instructions).toHaveLength(2);

            const createAssocInstruction = TokenInstruction.decodeCreateAssociatedAccount(tx.instructions[0]);
            expect(createAssocInstruction.subsidizer.toBuffer()).toEqual(appSubsidizer.publicKey().buffer);
            expect(createAssocInstruction.address).toEqual(tokenAccount);
            expect(createAssocInstruction.owner.toBuffer()).toEqual(account.publicKey().buffer);
            expect(createAssocInstruction.mint.toBuffer()).toEqual(token);

            const setAuthInstruction = TokenInstruction.decodeSetAuthority(tx.instructions[1]);
            expect(setAuthInstruction.account).toEqual(tokenAccount);
            expect(setAuthInstruction.currentAuthority.toBuffer()).toEqual(account.publicKey().buffer);
            expect(setAuthInstruction.newAuthority!.toBuffer()).toEqual(appSubsidizer.publicKey().buffer);
            expect(setAuthInstruction.authorityType).toEqual('CloseAccount');

            resp.setResult(accountpbv4.CreateAccountResponse.Result.OK);
            callback(undefined, resp);
        });

    // Don't pass in subsidizer
    try {
        await client.createAccount(account);
        fail();
    } catch (err) {
        expect(err).toBeInstanceOf(NoSubsidizerError);
    }

    // Pass in subsidizer
    await client.createAccount(account, undefined, appSubsidizer);
});
Example #25
Source File: mint_tests.ts    From psyoptions with Apache License 2.0 4 votes vote down vote up
describe("mintOption", () => {
  const payer = anchor.web3.Keypair.generate();
  const mintAuthority = anchor.web3.Keypair.generate();
  const program = anchor.workspace.PsyAmerican as Program<PsyAmerican>;
  const provider = program.provider;

  const minter = anchor.web3.Keypair.generate();

  let quoteToken: Token;
  let underlyingToken: Token;
  let optionToken: Token;
  let optionMarket: OptionMarketV2;
  let underlyingAmountPerContract: anchor.BN;
  let quoteAmountPerContract: anchor.BN;
  let expiration: anchor.BN;
  let optionMarketKey: PublicKey;
  let bumpSeed: number;
  let mintFeeKey: PublicKey | null;
  let exerciseFeeKey: PublicKey;
  let optionMintAccount: Keypair;
  let writerTokenMintAccount: Keypair;
  let underlyingAssetPoolAccount: Keypair;
  let quoteAssetPoolAccount: Keypair;
  let remainingAccounts: AccountMeta[] = [];
  let instructions: TransactionInstruction[] = [];

  let optionAccount: Keypair;
  let underlyingAccount: Keypair;
  let writerTokenAccount: Keypair;
  let size = new u64(2);

  beforeEach(async () => {
    await provider.connection.confirmTransaction(
      await provider.connection.requestAirdrop(payer.publicKey, 10_000_000_000),
      "confirmed"
    );
    await provider.connection.confirmTransaction(
      await provider.connection.requestAirdrop(
        minter.publicKey,
        10_000_000_000
      ),
      "confirmed"
    );
    size = new u64(2);
  });

  const mintOptionsTx = async (
    opts: {
      underlyingAssetPoolKey?: PublicKey;
      remainingAccounts?: AccountMeta[];
      feeOwner?: PublicKey;
    } = {}
  ) => {
    await program.rpc.mintOption(size, {
      accounts: {
        userAuthority: minter.publicKey,
        underlyingAssetMint: optionMarket?.underlyingAssetMint,
        underlyingAssetPool:
          opts.underlyingAssetPoolKey || optionMarket?.underlyingAssetPool,
        underlyingAssetSrc: underlyingAccount.publicKey,
        optionMint: optionMarket?.optionMint,
        mintedOptionDest: optionAccount.publicKey,
        writerTokenMint: optionMarket?.writerTokenMint,
        mintedWriterTokenDest: writerTokenAccount.publicKey,
        optionMarket: optionMarket?.key,
        feeOwner: opts.feeOwner || FEE_OWNER_KEY,
        tokenProgram: TOKEN_PROGRAM_ID,
        associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
        clock: SYSVAR_CLOCK_PUBKEY,
        rent: SYSVAR_RENT_PUBKEY,
        systemProgram: SystemProgram.programId,
      },
      remainingAccounts: opts.remainingAccounts
        ? opts.remainingAccounts
        : remainingAccounts,
      signers: [minter],
    });
  };

  describe("proper mint", () => {
    beforeEach(async () => {
      ({
        quoteToken,
        underlyingToken,
        optionToken,
        underlyingAmountPerContract,
        quoteAmountPerContract,
        expiration,
        optionMarketKey,
        bumpSeed,
        mintFeeKey,
        exerciseFeeKey,
        optionMarket,
        remainingAccounts,
        instructions,
      } = await initSetup(provider, payer, mintAuthority, program));
      await initOptionMarket(
        program,
        payer,
        optionMarket,
        remainingAccounts,
        instructions
      );
      ({ optionAccount, underlyingAccount, writerTokenAccount } =
        await createMinter(
          provider.connection,
          minter,
          mintAuthority,
          underlyingToken,
          size.mul(optionMarket.underlyingAmountPerContract).muln(2).toNumber(),
          optionMarket.optionMint,
          optionMarket.writerTokenMint,
          quoteToken
        ));
    });
    it("should mint size OptionTokens", async () => {
      try {
        await mintOptionsTx();
      } catch (err) {
        console.error((err as AnchorError).error.errorMessage);
        throw err;
      }
      const mintInfo = await optionToken.getMintInfo();
      assert.equal(mintInfo.supply.toString(), size.toString());
    });

    it("should mint size WriterTokens", async () => {
      try {
        await mintOptionsTx();
      } catch (err) {
        console.error((err as AnchorError).error.errorMessage);
        throw err;
      }
      const writerToken = new Token(
        provider.connection,
        optionMarket.writerTokenMint,
        TOKEN_PROGRAM_ID,
        payer
      );
      const mintInfo = await writerToken.getMintInfo();
      assert.equal(mintInfo.supply.toString(), size.toString());
    });

    it("should transfer the underlying from the minter to the pool and take a fee", async () => {
      if (!mintFeeKey) {
        throw new Error("mintFeeKey wasn't set when it should be");
      }
      const mintFeeAcctBefore = await underlyingToken.getAccountInfo(
        mintFeeKey
      );
      const underlyingPoolBefore = await underlyingToken.getAccountInfo(
        optionMarket.underlyingAssetPool
      );
      const minterUnderlyingBefore = await underlyingToken.getAccountInfo(
        underlyingAccount.publicKey
      );
      try {
        await mintOptionsTx();
      } catch (err) {
        console.error((err as AnchorError).error.errorMessage);
        throw err;
      }
      const expectedUnderlyingTransfered = size.mul(
        underlyingAmountPerContract
      );
      const mintFeeAmountPerContract = feeAmountPerContract(
        underlyingAmountPerContract
      );
      const mintFeeAmount = mintFeeAmountPerContract.mul(size);

      const underlyingPoolAfter = await underlyingToken.getAccountInfo(
        optionMarket.underlyingAssetPool
      );
      const poolDiff = underlyingPoolAfter.amount.sub(
        underlyingPoolBefore.amount
      );
      const mintFeeAcctAfter = await underlyingToken.getAccountInfo(mintFeeKey);
      assert.equal(
        poolDiff.toString(),
        expectedUnderlyingTransfered.toString()
      );

      const minterUnderlyingAfter = await underlyingToken.getAccountInfo(
        underlyingAccount.publicKey
      );
      const minterUnderlyingDiff = minterUnderlyingAfter.amount.sub(
        minterUnderlyingBefore.amount
      );
      assert.equal(
        expectedUnderlyingTransfered.add(mintFeeAmount).neg().toString(),
        minterUnderlyingDiff.toString()
      );
      const mintFeeAcctDiff = mintFeeAcctAfter.amount.sub(
        mintFeeAcctBefore.amount
      );
      assert.equal(mintFeeAcctDiff.toString(), mintFeeAmount.toString());
    });
  });

  describe("OptionMarket expired", () => {
    beforeEach(async () => {
      ({
        quoteToken,
        underlyingToken,
        underlyingAmountPerContract,
        quoteAmountPerContract,
        expiration,
        optionMarketKey,
        bumpSeed,
        mintFeeKey,
        exerciseFeeKey,
        optionMarket,
        remainingAccounts,
        instructions,
      } = await initSetup(provider, payer, mintAuthority, program, {
        // set expiration to 2 seconds from now
        expiration: new anchor.BN(new Date().getTime() / 1000 + 2),
      }));
      await initOptionMarket(
        program,
        payer,
        optionMarket,
        remainingAccounts,
        instructions
      );
      ({ optionAccount, underlyingAccount, writerTokenAccount } =
        await createMinter(
          provider.connection,
          minter,
          mintAuthority,
          underlyingToken,
          optionMarket.underlyingAmountPerContract.muln(2).toNumber(),
          optionMarket.optionMint,
          optionMarket.writerTokenMint,
          quoteToken
        ));
    });
    it("should error", async () => {
      try {
        await wait(2000);
        await mintOptionsTx();
        assert.ok(false);
      } catch (err) {
        const errMsg = "OptionMarket is expired, can't mint";
        assert.equal((err as AnchorError).error.errorMessage, errMsg);
      }
    });
  });

  describe("Underlying pool key differs from option market", () => {
    beforeEach(async () => {
      ({
        quoteToken,
        underlyingToken,
        underlyingAmountPerContract,
        quoteAmountPerContract,
        expiration,
        optionMarketKey,
        bumpSeed,
        mintFeeKey,
        exerciseFeeKey,
        optionMarket,
        remainingAccounts,
        instructions,
      } = await initSetup(provider, payer, mintAuthority, program));
      await initOptionMarket(
        program,
        payer,
        optionMarket,
        remainingAccounts,
        instructions
      );
      ({ optionAccount, underlyingAccount, writerTokenAccount } =
        await createMinter(
          provider.connection,
          minter,
          mintAuthority,
          underlyingToken,
          size.mul(optionMarket.underlyingAmountPerContract.muln(2)).toNumber(),
          optionMarket.optionMint,
          optionMarket.writerTokenMint,
          quoteToken
        ));
      // Create a new token account and set it as the underlyingAssetPoolAccount
      const { tokenAccount } = await initNewTokenAccount(
        provider.connection,
        payer.publicKey,
        underlyingToken.publicKey,
        payer
      );
      optionMarket.underlyingAssetPool = tokenAccount.publicKey;
    });
    it("should error", async () => {
      try {
        await mintOptionsTx();
        assert.ok(false);
      } catch (err) {
        const errMsg =
          "Underlying pool account does not match the value on the OptionMarket";
        assert.equal((err as AnchorError).error.errorMessage, errMsg);
      }
    });
  });

  describe("OptionToken Mint key differs from option market", () => {
    beforeEach(async () => {
      ({
        quoteToken,
        underlyingToken,
        underlyingAmountPerContract,
        quoteAmountPerContract,
        expiration,
        optionMarketKey,
        bumpSeed,
        mintFeeKey,
        exerciseFeeKey,
        optionMarket,
        remainingAccounts,
        instructions,
      } = await initSetup(provider, payer, mintAuthority, program));
      await initOptionMarket(
        program,
        payer,
        optionMarket,
        remainingAccounts,
        instructions
      );
      // Create a new token mint and set it as the optionMintAccount
      const { mintAccount } = await initNewTokenMint(
        provider.connection,
        payer.publicKey,
        payer
      );
      optionMarket.optionMint = mintAccount.publicKey;
      ({ optionAccount, underlyingAccount, writerTokenAccount } =
        await createMinter(
          provider.connection,
          minter,
          mintAuthority,
          underlyingToken,
          size.mul(optionMarket.underlyingAmountPerContract.muln(2)).toNumber(),
          optionMarket.optionMint,
          optionMarket.writerTokenMint,
          quoteToken
        ));
    });
    it("should error", async () => {
      try {
        await mintOptionsTx();
        assert.ok(false);
      } catch (err) {
        const errMsg =
          "OptionToken mint does not match the value on the OptionMarket";
        assert.equal((err as AnchorError).error.errorMessage, errMsg);
      }
    });
  });

  describe("WriterToken Mint key differs from option market", () => {
    beforeEach(async () => {
      ({
        quoteToken,
        underlyingToken,
        underlyingAmountPerContract,
        quoteAmountPerContract,
        expiration,
        optionMarketKey,
        bumpSeed,
        mintFeeKey,
        exerciseFeeKey,
        optionMarket,
        remainingAccounts,
        instructions,
      } = await initSetup(provider, payer, mintAuthority, program));
      await initOptionMarket(
        program,
        payer,
        optionMarket,
        remainingAccounts,
        instructions
      );
      // Create a new token mint and set it as the optionMintAccount
      const { mintAccount } = await initNewTokenMint(
        provider.connection,
        payer.publicKey,
        payer
      );
      optionMarket.writerTokenMint = mintAccount.publicKey;
      ({ optionAccount, underlyingAccount, writerTokenAccount } =
        await createMinter(
          provider.connection,
          minter,
          mintAuthority,
          underlyingToken,
          size.mul(optionMarket.underlyingAmountPerContract.muln(2)).toNumber(),
          optionMarket.optionMint,
          optionMarket.writerTokenMint,
          quoteToken
        ));
    });
    it("should error", async () => {
      try {
        await mintOptionsTx();
        assert.ok(false);
      } catch (err) {
        const errMsg =
          "WriterToken mint does not match the value on the OptionMarket";
        assert.equal((err as AnchorError).error.errorMessage, errMsg);
      }
    });
  });

  describe("MintFee account differs from option market", () => {
    let badMintFeeKey: PublicKey;
    beforeEach(async () => {
      ({
        quoteToken,
        underlyingToken,
        underlyingAmountPerContract,
        quoteAmountPerContract,
        expiration,
        optionMarketKey,
        bumpSeed,
        mintFeeKey,
        exerciseFeeKey,
        optionMarket,
        remainingAccounts,
        instructions,
      } = await initSetup(provider, payer, mintAuthority, program));
      await initOptionMarket(
        program,
        payer,
        optionMarket,
        remainingAccounts,
        instructions
      );
      // Create a new token account and set it as the mintFeeKey
      const { tokenAccount } = await initNewTokenAccount(
        provider.connection,
        FEE_OWNER_KEY,
        underlyingToken.publicKey,
        payer
      );
      badMintFeeKey = tokenAccount.publicKey;

      ({ optionAccount, underlyingAccount, writerTokenAccount } =
        await createMinter(
          provider.connection,
          minter,
          mintAuthority,
          underlyingToken,
          optionMarket.underlyingAmountPerContract.muln(2).toNumber(),
          optionMarket.optionMint,
          optionMarket.writerTokenMint,
          quoteToken
        ));
    });
    it("should error", async () => {
      try {
        await mintOptionsTx({
          remainingAccounts: [
            {
              pubkey: badMintFeeKey,
              isWritable: true,
              isSigner: false,
            },
          ],
        });
        assert.ok(false);
      } catch (err) {
        const errMsg =
          "MintFee key does not match the value on the OptionMarket";
        assert.equal((err as AnchorError).error.errorMessage, errMsg);
      }
    });
  });

  describe("Size <= 0", () => {
    beforeEach(async () => {
      ({
        quoteToken,
        underlyingToken,
        underlyingAmountPerContract,
        quoteAmountPerContract,
        expiration,
        optionMarketKey,
        bumpSeed,
        mintFeeKey,
        exerciseFeeKey,
        optionMarket,
        remainingAccounts,
        instructions,
      } = await initSetup(provider, payer, mintAuthority, program));
      await initOptionMarket(
        program,
        payer,
        optionMarket,
        remainingAccounts,
        instructions
      );
      ({ optionAccount, underlyingAccount, writerTokenAccount } =
        await createMinter(
          provider.connection,
          minter,
          mintAuthority,
          underlyingToken,
          optionMarket.underlyingAmountPerContract.muln(2).toNumber(),
          optionMarket.optionMint,
          optionMarket.writerTokenMint,
          quoteToken
        ));

      // Set the size to 0 to trigger an error
      size = new anchor.BN(0);
    });
    it("should error", async () => {
      try {
        await mintOptionsTx();
        assert.ok(false);
      } catch (err) {
        const errMsg = "The size argument must be > 0";
        assert.equal((err as AnchorError).error.errorMessage, errMsg);
      }
    });
  });
  describe("Fee owner is incorrect", () => {
    beforeEach(async () => {
      ({
        quoteToken,
        underlyingToken,
        underlyingAmountPerContract,
        quoteAmountPerContract,
        expiration,
        optionMarketKey,
        bumpSeed,
        mintFeeKey,
        exerciseFeeKey,
        optionMarket,
        remainingAccounts,
        instructions,
      } = await initSetup(provider, payer, mintAuthority, program));
      await initOptionMarket(
        program,
        payer,
        optionMarket,
        remainingAccounts,
        instructions
      );
      ({ optionAccount, underlyingAccount, writerTokenAccount } =
        await createMinter(
          provider.connection,
          minter,
          mintAuthority,
          underlyingToken,
          optionMarket.underlyingAmountPerContract.muln(2).toNumber(),
          optionMarket.optionMint,
          optionMarket.writerTokenMint,
          quoteToken
        ));
    });
    it("should error", async () => {
      try {
        await mintOptionsTx({
          feeOwner: new Keypair().publicKey,
        });
        assert.ok(false);
      } catch (err) {
        const errMsg = "Fee owner does not match the program's fee owner";
        assert.equal((err as AnchorError).error.errorMessage, errMsg);
      }
    });
  });
  describe("OptionMarket is for NFT", () => {
    beforeEach(async () => {
      ({
        quoteToken,
        underlyingToken,
        underlyingAmountPerContract,
        quoteAmountPerContract,
        expiration,
        optionMarketKey,
        bumpSeed,
        mintFeeKey,
        exerciseFeeKey,
        optionMarket,
        remainingAccounts,
        instructions,
      } = await initSetup(provider, payer, mintAuthority, program, {
        underlyingAmountPerContract: new anchor.BN("1"),
      }));
      await initOptionMarket(
        program,
        payer,
        optionMarket,
        remainingAccounts,
        instructions
      );
      ({ optionAccount, underlyingAccount, writerTokenAccount } =
        await createMinter(
          provider.connection,
          minter,
          mintAuthority,
          underlyingToken,
          optionMarket.underlyingAmountPerContract.muln(2).toNumber(),
          optionMarket.optionMint,
          optionMarket.writerTokenMint,
          quoteToken
        ));
    });
    it("should transfer enough lamports as required by the fee", async () => {
      const minterBefore = await provider.connection.getAccountInfo(
        minter.publicKey
      );
      const feeOwnerBefore =
        (await provider.connection.getAccountInfo(FEE_OWNER_KEY))?.lamports ||
        0;
      try {
        await mintOptionsTx();
      } catch (err) {
        console.error((err as AnchorError).error.errorMessage);
        throw err;
      }
      const minterAfter = await provider.connection.getAccountInfo(
        minter.publicKey
      );
      const feeOwnerAfter =
        (await provider.connection.getAccountInfo(FEE_OWNER_KEY))?.lamports ||
        0;
      if (!minterAfter?.lamports || !minterBefore?.lamports) {
        throw new Error("minter has no lamports");
      }
      const minterDiff = minterAfter?.lamports - minterBefore?.lamports;
      const feeOwnerDiff = feeOwnerAfter - feeOwnerBefore;
      assert.equal(-minterDiff, size.mul(new BN(NFT_MINT_LAMPORTS)));
      assert.equal(feeOwnerDiff, size.mul(new BN(NFT_MINT_LAMPORTS)));
    });
  });
});
Example #26
Source File: webhook.spec.ts    From kin-node with MIT License 4 votes vote down vote up
test("hmac header validation", async () => {
    await request(app)
        .post("/events")
        .set('Accept', 'application/json')
        .send([])
        .expect(401);

    const events: Event[] = [
        {
            transaction_event: {
                tx_id: Buffer.from(base58.decode('2nBhEBYYvfaAe16UMNqRHre4YNSskvuYgx3M6E4JP1oDYvZEJHvoPzyUidNgNX5r9sTyN1J9UxtbCXy2rqYcuyuv')).toString('base64'),
                solana_event: {
                    transaction: 'AVj7dxHlQ9IrvdYVIjuiRFs1jLaDMHixgrv+qtHBwz51L4/ImLZhszwiyEJDIp7xeBSpm/TX5B7mYzxa+fPOMw0BAAMFJMJVqLw+hJYheizSoYlLm53KzgT82cDVmazarqQKG2GQsLgiqktA+a+FDR4/7xnDX7rsusMwryYVUdixfz1B1Qan1RcZLwqvxvJl4/t3zHragsUp0L47E24tAFUgAAAABqfVFxjHdMkoVmOYaR1etoteuKObS21cc1VbIQAAAAAHYUgdNXR0u3xNdiTr072z2DVec9EQQ/wNo1OAAAAAAAtxOUhPBp2WSjUNJEgfvy70BbxI00fZyEPvFHNfxrtEAQQEAQIDADUCAAAAAQAAAAAAAACtAQAAAAAAAAdUE18R96XTJCe+YfRfUp6WP+YKCy/72ucOL8AoBFSpAA==',
                }
            }
        }
    ];

    await request(app)
        .post("/events")
        .set('Accept', 'application/json')
        .set(AGORA_HMAC_HEADER, "blah")
        .send(events)
        .expect(401);

    await request(app)
        .post("/sign_transaction")
        .set('Accept', 'application/json')
        .set(AGORA_HMAC_HEADER, "blah")
        .send(events)
        .expect(401);

    await request(app)
        .post("/events")
        .set('Accept', 'application/json')
        .set(AGORA_HMAC_HEADER, getHmacHeader(events))
        .send(events)
        .expect(200);

    const sender = PrivateKey.random().publicKey();
    const destination = PrivateKey.random().publicKey();
    const recentBlockhash = PrivateKey.random().publicKey();

    const tx = new Transaction({
        feePayer: subsidizer.publicKey().solanaKey(),
        recentBlockhash: recentBlockhash.toBase58(),
    }).add(
        Token.createTransferInstruction(
            TOKEN_PROGRAM_ID,
            sender.solanaKey(),
            destination.solanaKey(),
            sender.solanaKey(),
            [],
            100,
        )
    );

    const signRequest = {
        solana_transaction: tx.serialize({
            verifySignatures: false,
            requireAllSignatures: false,
        }).toString('base64'),
    };

    await request(app)
        .post("/sign_transaction")
        .set('Accept', 'application/json')
        .set(AGORA_HMAC_HEADER, getHmacHeader(signRequest))
        .send(signRequest)
        .expect(200);

    const mint = PrivateKey.random().publicKey().solanaKey();
    const assoc = await Token.getAssociatedTokenAddress(
        ASSOCIATED_TOKEN_PROGRAM_ID,
        TOKEN_PROGRAM_ID,
        mint,
        sender.solanaKey(),
    );
    const createTx = new Transaction({
        feePayer: subsidizer.publicKey().solanaKey(),
        recentBlockhash: recentBlockhash.toBase58(),
    }).add(
        Token.createAssociatedTokenAccountInstruction(
            ASSOCIATED_TOKEN_PROGRAM_ID,
            TOKEN_PROGRAM_ID,
            mint,
            assoc,
            sender.solanaKey(),
            subsidizer.publicKey().solanaKey(),
        ),
        Token.createSetAuthorityInstruction(
            TOKEN_PROGRAM_ID,
            assoc,
            subsidizer.publicKey().solanaKey(),
            'CloseAccount',
            sender.solanaKey(),
            [],
        ),
    );
    
    const createRequest = {
        solana_transaction: createTx.serialize({
            verifySignatures: false,
            requireAllSignatures: false,
        }).toString('base64'),
    };
    
    await request(app)
        .post("/create_account")
        .set('Accept', 'application/json')
        .set(AGORA_HMAC_HEADER, getHmacHeader(createRequest))
        .send(createRequest)
        .expect(200);
});
Example #27
Source File: client.ts    From kin-node with MIT License 4 votes vote down vote up
async mergeTokenAccounts(key: PrivateKey, createAssociatedAccount: boolean, commitment: Commitment = this.defaultCommitment, subsidizer?: PrivateKey): Promise<Buffer|undefined> {
        const existingAccounts = await this.internal.resolveTokenAccounts(key.publicKey(), true);
        const owner = key.publicKey().solanaKey();
        
        if (existingAccounts.length === 0 || (!createAssociatedAccount && existingAccounts.length === 1)) {
            return Promise.resolve(undefined);
        }

        let dest = new SolanaPublicKey(Buffer.from(existingAccounts[0].getAccountId()!.getValue_asU8()));

        const signers = [key];
        let subsidizerId: SolanaPublicKey;
        
        const config = await this.internal.getServiceConfig();
        if (!config.getSubsidizerAccount() && !subsidizer) {
            return Promise.reject(new NoSubsidizerError());
        }

        const mint = new SolanaPublicKey(Buffer.from(config.getToken()!.getValue_asU8()));
        if (subsidizer) {
            subsidizerId = subsidizer.publicKey().solanaKey();
            signers.push(subsidizer);
        } else {
            subsidizerId = new SolanaPublicKey(Buffer.from(config.getSubsidizerAccount()!.getValue_asU8()));
        }

        const instructions = [];
        if (createAssociatedAccount) {
            const assoc = await Token.getAssociatedTokenAddress(
                ASSOCIATED_TOKEN_PROGRAM_ID,
                TOKEN_PROGRAM_ID,
                mint,
                owner,
            );
            
            if (!Buffer.from(existingAccounts[0].getAccountId()!.getValue_asU8()).equals(assoc.toBuffer())) {
                instructions.push(Token.createAssociatedTokenAccountInstruction(
                    ASSOCIATED_TOKEN_PROGRAM_ID,
                    TOKEN_PROGRAM_ID,
                    mint,
                    assoc,
                    owner,
                    subsidizerId
                ));
                instructions.push(Token.createSetAuthorityInstruction(
                    TOKEN_PROGRAM_ID,
                    assoc,
                    subsidizerId,
                    'CloseAccount',
                    owner,
                    [],
                ));
                dest = assoc;
            } else if (existingAccounts.length === 1) {
                return Promise.resolve(undefined);
            }
        }

        for(let i = 0; i < existingAccounts.length; i++) {
            const existingAccount = existingAccounts[i];
            if (Buffer.from(existingAccount.getAccountId()!.getValue_asU8()).equals(dest.toBuffer())) {
                continue;
            }

            instructions.push(Token.createTransferInstruction(
                TOKEN_PROGRAM_ID,
                new SolanaPublicKey(existingAccount.getAccountId()!.getValue_asU8()),
                dest,
                owner,
                [],
                bigNumberToU64(new BigNumber(existingAccount.getBalance()))),
            );

            // If no close authority is set, it likely means we do not know it and can't make any assumptions
            const closeAuth = existingAccount.getCloseAuthority();
            if (!closeAuth) {
                continue;
            }

            const closeableAuths = [key.publicKey().buffer, subsidizerId.toBuffer()];
            let shouldClose = false;
            for(let i = 0; i < closeableAuths.length; i++) {
                if (closeableAuths[i].equals(Buffer.from(closeAuth.getValue_asU8()))) {
                    shouldClose = true;
                    break;
                }
            }

            if (shouldClose) {
                instructions.push(Token.createCloseAccountInstruction(
                    TOKEN_PROGRAM_ID,
                    new SolanaPublicKey(existingAccount.getAccountId()!.getValue_asU8()),
                    new SolanaPublicKey(closeAuth.getValue_asU8()),
                    new SolanaPublicKey(closeAuth.getValue_asU8()),
                    [],
                ));
            }
        }

        const tx = new Transaction({
            feePayer: subsidizerId,
        }).add(...instructions);

        const result = await this.signAndSubmitTx(signers, tx, commitment);
        if (result.Errors && result.Errors?.TxError) {
            return Promise.reject(result.Errors.TxError);
        }

        return result.TxId;
    }
Example #28
Source File: market.ts    From raydium-ui with GNU General Public License v3.0 4 votes vote down vote up
async function initAmm(
  conn: any,
  wallet: any,
  market: any,
  ammProgramId: PublicKey,
  dexProgramId: PublicKey,
  // ammKeypair: PublicKey,
  ammKeys: any,
  userInputBaseValue: number,
  userInputQuoteValue: number,
  poolCoinTokenAccount: PublicKey,
  poolPcTokenAccount: PublicKey,
  lpMintAddress: PublicKey,
  startTime: number
) {
  const baseMintDecimals = new BigNumber(await getMintDecimals(conn, market.baseMintAddress as PublicKey))
  const quoteMintDecimals = new BigNumber(await getMintDecimals(conn, market.quoteMintAddress as PublicKey))

  const coinVol = new BigNumber(10).exponentiatedBy(baseMintDecimals).multipliedBy(userInputBaseValue)
  const pcVol = new BigNumber(10).exponentiatedBy(quoteMintDecimals).multipliedBy(userInputQuoteValue)

  const transaction = new Transaction()
  const signers: any = []
  const owner = wallet.publicKey
  const baseTokenAccount = await getFilteredTokenAccountsByOwner(conn, owner, market.baseMintAddress)
  const quoteTokenAccount = await getFilteredTokenAccountsByOwner(conn, owner, market.quoteMintAddress)
  const baseTokenList: any = baseTokenAccount.value.map((item: any) => {
    if (item.account.data.parsed.info.tokenAmount.amount >= getBigNumber(coinVol)) {
      return item.pubkey
    }
    return null
  })
  const quoteTokenList: any = quoteTokenAccount.value.map((item: any) => {
    if (item.account.data.parsed.info.tokenAmount.amount >= getBigNumber(pcVol)) {
      return item.pubkey
    }
    return null
  })
  let baseToken: string | null = null
  for (const item of baseTokenList) {
    if (item !== null) {
      baseToken = item
    }
  }
  let quoteToken: string | null = null
  for (const item of quoteTokenList) {
    if (item !== null) {
      quoteToken = item
    }
  }
  if (
    (baseToken === null && market.baseMintAddress.toString() !== TOKENS.WSOL.mintAddress) ||
    (quoteToken === null && market.quoteMintAddress.toString() !== TOKENS.WSOL.mintAddress)
  ) {
    throw new Error('no money')
  }

  const destLpToken = await findAssociatedTokenAddress(owner, lpMintAddress)
  const destLpTokenInfo = await conn.getAccountInfo(destLpToken)
  if (!destLpTokenInfo) {
    transaction.add(
      Token.createAssociatedTokenAccountInstruction(
        ASSOCIATED_TOKEN_PROGRAM_ID,
        TOKEN_PROGRAM_ID,
        lpMintAddress,
        destLpToken,
        owner,
        owner
      )
    )
  }

  if (market.baseMintAddress.toString() === TOKENS.WSOL.mintAddress) {
    const newAccount = new Account()
    transaction.add(
      SystemProgram.createAccount({
        fromPubkey: owner,
        newAccountPubkey: newAccount.publicKey,
        lamports: parseInt(coinVol.toFixed()) + 1e7,
        space: ACCOUNT_LAYOUT.span,
        programId: TOKEN_PROGRAM_ID
      })
    )
    transaction.add(
      initializeAccount({
        account: newAccount.publicKey,
        mint: new PublicKey(TOKENS.WSOL.mintAddress),
        owner
      })
    )

    transaction.add(transfer(newAccount.publicKey, poolCoinTokenAccount, owner, parseInt(coinVol.toFixed())))

    transaction.add(
      closeAccount({
        source: newAccount.publicKey,
        destination: owner,
        owner
      })
    )

    signers.push(newAccount)
  } else {
    transaction.add(
      // @ts-ignore
      transfer(new PublicKey(baseToken), poolCoinTokenAccount, owner, parseInt(coinVol.toFixed()))
    )
  }
  if (market.quoteMintAddress.toString() === TOKENS.WSOL.mintAddress) {
    const newAccount = new Account()
    transaction.add(
      SystemProgram.createAccount({
        fromPubkey: owner,
        newAccountPubkey: newAccount.publicKey,
        lamports: parseInt(pcVol.toFixed()) + 1e7,
        space: ACCOUNT_LAYOUT.span,
        programId: TOKEN_PROGRAM_ID
      })
    )
    transaction.add(
      initializeAccount({
        account: newAccount.publicKey,
        mint: new PublicKey(TOKENS.WSOL.mintAddress),
        owner
      })
    )

    transaction.add(transfer(newAccount.publicKey, poolPcTokenAccount, owner, parseInt(pcVol.toFixed())))
    transaction.add(
      closeAccount({
        source: newAccount.publicKey,
        destination: owner,
        owner
      })
    )
    signers.push(newAccount)
  } else {
    // @ts-ignore
    transaction.add(transfer(new PublicKey(quoteToken), poolPcTokenAccount, owner, parseInt(pcVol.toFixed())))
  }

  transaction.add(
    initialize(
      ammProgramId,
      ammKeys.ammId,
      ammKeys.ammAuthority,
      ammKeys.ammOpenOrders,
      ammKeys.lpMintAddress,
      market.baseMintAddress,
      market.quoteMintAddress,
      ammKeys.poolCoinTokenAccount,
      ammKeys.poolPcTokenAccount,
      ammKeys.poolWithdrawQueue,
      ammKeys.ammTargetOrders,
      destLpToken,
      ammKeys.poolTempLpTokenAccount,
      dexProgramId,
      market.address,

      owner,

      ammKeys.nonce,
      startTime
    )
  )

  const txid = await sendTransaction(conn, wallet, transaction, signers)

  console.log('txid3', txid)
  let txidSuccessFlag = 0
  await conn.onSignature(txid, function (_signatureResult: any, _context: any) {
    if (_signatureResult.err) {
      txidSuccessFlag = -1
    } else {
      txidSuccessFlag = 1
    }
  })

  const timeAwait = new Date().getTime()
  let outOfWhile = false
  while (!outOfWhile) {
    console.log('txid3', outOfWhile, txidSuccessFlag, (new Date().getTime() - timeAwait) / 1000)
    if (txidSuccessFlag !== 0) {
      outOfWhile = true
    }
    await new Promise((resolve) => setTimeout(resolve, 1000))
  }
  if (txidSuccessFlag !== 1) {
    throw new Error('Transaction failed')
  }

  clearLocal()
}
Example #29
Source File: execute-sell.ts    From sdk with MIT License 4 votes vote down vote up
export async function getAuctionHouseExecuteSellInstructions(
	request: IActionHouseExecuteSellRequest
): Promise<ITransactionPreparedInstructions> {
	const anchorProgram = await loadAuctionHouseProgram(request.connection, request.signer)
	const auctionHouseObj = await anchorProgram.account.auctionHouse.fetch(request.auctionHouse)

	const isNative = auctionHouseObj.treasuryMint.equals(WRAPPED_SOL_MINT)
	const buyPriceAdjusted = new BN(
		await getPriceWithMantissa(
			request.price,
			auctionHouseObj.treasuryMint,
			request.signer,
			anchorProgram,
		),
	)

	const tokenSizeAdjusted = new BN(
		await getPriceWithMantissa(
			request.tokensAmount,
			request.mint,
			request.signer,
			anchorProgram,
		),
	)

	const tokenAccountKey = (await getAssociatedTokenAccountForMint(request.mint, request.sellerWallet))[0]

	const buyerTradeState = (
		await getAuctionHouseTradeState(
			request.auctionHouse,
			request.buyerWallet,
			tokenAccountKey,
			//@ts-ignore
			auctionHouseObj.treasuryMint,
			request.mint,
			tokenSizeAdjusted,
			buyPriceAdjusted,
		)
	)[0]

	const sellerTradeState = (
		await getAuctionHouseTradeState(
			request.auctionHouse,
			request.sellerWallet,
			tokenAccountKey,
			//@ts-ignore
			auctionHouseObj.treasuryMint,
			request.mint,
			tokenSizeAdjusted,
			buyPriceAdjusted,
		)
	)[0]

	const [freeTradeState, freeTradeStateBump] = await getAuctionHouseTradeState(
		request.auctionHouse,
		request.sellerWallet,
		tokenAccountKey,
		//@ts-ignore
		auctionHouseObj.treasuryMint,
		request.mint,
		tokenSizeAdjusted,
		new BN(0),
	)

	const [escrowPaymentAccount, escrowBump] = await getAuctionHouseBuyerEscrow(
		request.auctionHouse,
		request.buyerWallet,
	)
	const [programAsSigner, programAsSignerBump] = await getAuctionHouseProgramAsSigner()
	const metadata = await getMetadata(request.mint)

	const metadataObj = await anchorProgram.provider.connection.getAccountInfo(
		metadata,
	)

	if (!metadataObj) {
		throw new Error("Account info doesn't fetched")
	}
	const metadataDecoded: Metadata = decodeMetadata(
		Buffer.from(metadataObj.data),
	)

	const remainingAccounts = []

	if (metadataDecoded.data.creators) {
		for (let i = 0; i < metadataDecoded.data.creators.length; i++) {
			remainingAccounts.push({
				pubkey: new web3.PublicKey(metadataDecoded.data.creators[i].address),
				isWritable: true,
				isSigner: false,
			})
			if (!isNative) {
				remainingAccounts.push({
					pubkey: (
						await getAssociatedTokenAccountForMint(
							//@ts-ignore
							auctionHouseObj.treasuryMint,
							remainingAccounts[remainingAccounts.length - 1].pubkey,
						)
					)[0],
					isWritable: true,
					isSigner: false,
				})
			}
		}
	}
	const signers: any[] = []

	const tMint = auctionHouseObj.treasuryMint

	/*const instruction = AuctionHouseProgram.instructions.createExecuteSaleInstruction(
		{
			buyer: request.buyerWallet,
			seller: request.sellerWallet,
			tokenAccount: tokenAccountKey,
			tokenMint: request.mint,
			metadata: metadata,
			treasuryMint: tMint,
			escrowPaymentAccount: escrowPaymentAccount,
			sellerPaymentReceiptAccount: isNative ? request.sellerWallet :
			(await getAssociatedTokenAccountForMint(tMint, request.sellerWallet))[0],
			buyerReceiptTokenAccount: (await getAssociatedTokenAccountForMint(request.mint, request.buyerWallet))[0],
			authority: auctionHouseObj.authority,
			auctionHouse: request.auctionHouse,
			auctionHouseFeeAccount: auctionHouseObj.auctionHouseFeeAccount,
			auctionHouseTreasury: auctionHouseObj.auctionHouseTreasury,
			buyerTradeState: buyerTradeState,
			sellerTradeState: sellerTradeState,
			freeTradeState: freeTradeState,
			programAsSigner: programAsSigner,
		},
		{
			escrowPaymentBump: escrowBump,
			freeTradeStateBump: freeTradeStateBump,
			programAsSignerBump: programAsSignerBump,
			buyerPrice: buyPriceAdjusted,
			tokenSize: tokenSizeAdjusted,
		}
	)*/

	const instruction = await anchorProgram.instruction.executeSale(
		escrowBump,
		freeTradeStateBump,
		programAsSignerBump,
		buyPriceAdjusted,
		tokenSizeAdjusted,
		{
			accounts: {
				buyer: request.buyerWallet,
				seller: request.sellerWallet,
				metadata,
				tokenAccount: tokenAccountKey,
				tokenMint: request.mint,
				escrowPaymentAccount,
				treasuryMint: tMint,
				sellerPaymentReceiptAccount: isNative
					? request.sellerWallet
					: (await getAssociatedTokenAccountForMint(tMint, request.sellerWallet))[0],
				buyerReceiptTokenAccount: (await getAssociatedTokenAccountForMint(request.mint, request.buyerWallet))[0],
				authority: auctionHouseObj.authority,
				auctionHouse: request.auctionHouse,
				auctionHouseFeeAccount: auctionHouseObj.auctionHouseFeeAccount,
				auctionHouseTreasury: auctionHouseObj.auctionHouseTreasury,
				sellerTradeState,
				buyerTradeState,
				tokenProgram: TOKEN_PROGRAM_ID,
				systemProgram: web3.SystemProgram.programId,
				ataProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
				programAsSigner,
				rent: web3.SYSVAR_RENT_PUBKEY,
				freeTradeState,
			},
			remainingAccounts,
			signers,
		}
	)

	instruction.keys
		.filter(k => k.pubkey.equals(request.signer.publicKey))
		.map(k => (k.isSigner = true))

	return { instructions: [instruction], signers }
}