UNPKG

solana-options

Version:

Minting of options contract NFTs on the Solana blockchain

238 lines (237 loc) 17.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const spl_token_1 = require("@solana/spl-token"); // import { SystemProgram } from "@solana/web3.js"; const web3_js_1 = require("@solana/web3.js"); const web3_js_2 = require("@solana/web3.js"); const __1 = require(".."); const utils_1 = require("../utils"); const assert_1 = __importDefault(require("assert")); const fs_1 = __importDefault(require("fs")); function get_private_key(filename) { let rawdata = fs_1.default.readFileSync(filename); return JSON.parse(rawdata.toString()); } const connection = new web3_js_2.Connection("http://localhost:8899", 'singleGossip'); const programId = "4ktt843KtvduVq7yDdSXoTpt8uev4tBFue2pot7NdiRR"; const bob_private_key = get_private_key("src/e2e_live_tests/keypairs/bob.json"); const alice_private_key = get_private_key("src/e2e_live_tests/keypairs/alice.json"); const [, toka, tokb, alice_a, alice_b] = fs_1.default.readFileSync("src/e2e_live_tests/alice.txt").toString().split("\n"); const [toka_key, tokb_key, alice_a_key, alice_b_key] = [new web3_js_2.PublicKey(toka), new web3_js_2.PublicKey(tokb), new web3_js_2.PublicKey(alice_a), new web3_js_2.PublicKey(alice_b)]; const [, , , bob_a, bob_b] = fs_1.default.readFileSync("src/e2e_live_tests/bob.txt").toString().split("\n"); const [bob_a_key, bob_b_key] = [new web3_js_2.PublicKey(bob_a), new web3_js_2.PublicKey(bob_b)]; let alice_acc = web3_js_1.Keypair.fromSecretKey(new Uint8Array(alice_private_key)); let bob_acc = web3_js_1.Keypair.fromSecretKey(new Uint8Array(bob_private_key)); describe("create option then exercise", function () { this.timeout(600000); // tests can take up to 10 mins it("creating an nft contract and buying it back should not change balance", async function () { let strike = 5; let expiry = Date.now() / 1000 + 600; let multiple = 5; return (0, __1.create_call)(connection, strike, expiry, multiple, alice_acc, null, tokb_key, null, alice_b_key).then(async ([s, contract]) => { console.log(contract, (0, utils_1.print_contract)(contract)); await connection.confirmTransaction(s, "finalized"); // wait a while to confirm the transactions let option_layout = await (0, __1.get_contract_from_blockchain)(connection, contract.account_id); console.log(option_layout); (0, utils_1.verify_contract)(contract, option_layout); // verify the contract matches what is on the blockchain // try to exercise the contract // for this test, the buyer is the same as creator let buyer_acc = alice_acc; let buyer_send_acc = alice_b_key; const [buyer_nft_acc,] = await web3_js_2.PublicKey.findProgramAddress([ buyer_acc.publicKey.toBytes(), // ASSOCIATED_TOKEN_PROGRAM_ID.toBuffer(), spl_token_1.TOKEN_PROGRAM_ID.toBytes(), contract.nft_id.toBytes() ], spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID); // because we supplied instrument = null in the create call, a new mint is made // we find its address to receive const [buyer_receive_acc,] = await web3_js_2.PublicKey.findProgramAddress([ buyer_acc.publicKey.toBytes(), // ASSOCIATED_TOKEN_PROGRAM_ID.toBuffer(), spl_token_1.TOKEN_PROGRAM_ID.toBytes(), contract.instrument.toBytes() ], spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID); // confirm the nft ownership token was received let nft_bal = await connection.getTokenAccountBalance(buyer_nft_acc, "finalized"); console.log("nft token balance", nft_bal.value); assert_1.default.equal(nft_bal.value.amount, "1"); return (0, __1.exercise_call)(connection, contract, buyer_acc, buyer_nft_acc, buyer_receive_acc, buyer_send_acc).then(async (sig) => { console.log("tx signature", sig); await connection.confirmTransaction(sig, "finalized"); // let option_layout = await get_contract_from_blockchain(connection, contract.account_id) // Token.getAssociatedTokenAddress() // check that the ownership nft is burned let nft_bal = await connection.getTokenAccountBalance(buyer_nft_acc, "finalized"); console.log("nft token balance", nft_bal.value); assert_1.default.equal(nft_bal.value.amount, "0"); }).catch(); }).catch(); }); it("alice creates call then bob buys and exercises", async function () { let strike = 2; let expiry = Date.now() / 1000 + 600; let multiple = 5; let [s, contract] = await (0, __1.create_call)(connection, strike, expiry, multiple, alice_acc, toka_key, tokb_key, alice_a_key, alice_b_key); console.log(contract, (0, utils_1.print_contract)(contract)); await connection.confirmTransaction(s, "finalized"); // wait a while to confirm the transactions let option_layout = await (0, __1.get_contract_from_blockchain)(connection, contract.account_id); console.log(option_layout); (0, utils_1.verify_contract)(contract, option_layout); // verify the contract matches what is on the blockchain // try to exercise the contract // for this test, the buyer is the same as creator let buyer_acc = bob_acc; let buyer_send_acc = bob_b_key; let buyer_receive_acc = bob_a_key; // find the address for the ownership nft const buyer_nft_acc = await spl_token_1.Token.getAssociatedTokenAddress(spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID, spl_token_1.TOKEN_PROGRAM_ID, contract.nft_id, buyer_acc.publicKey); let create_acc_ix = await spl_token_1.Token.createAssociatedTokenAccountInstruction(spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID, spl_token_1.TOKEN_PROGRAM_ID, contract.nft_id, buyer_nft_acc, buyer_acc.publicKey, buyer_acc.publicKey); // send the ownership token to bob console.log("transfering ownership to bob"); let sell_ix = spl_token_1.Token.createTransferInstruction(spl_token_1.TOKEN_PROGRAM_ID, new web3_js_2.PublicKey(contract.nft_account), buyer_nft_acc, alice_acc.publicKey, [alice_acc], 1); var tx = new web3_js_2.Transaction(); tx.add(create_acc_ix, sell_ix); let sig_sell = await connection.sendTransaction(tx, [alice_acc, bob_acc], { skipPreflight: false, preflightCommitment: 'finalized' }); await connection.confirmTransaction(sig_sell, "finalized"); // confirm the nft ownership token was received let nft_bal_before = await connection.getTokenAccountBalance(buyer_nft_acc, "finalized"); assert_1.default.equal(nft_bal_before.value.amount, "1"); let send_bal_before = await connection.getTokenAccountBalance(buyer_send_acc, "finalized"); let recv_bal_before = await connection.getTokenAccountBalance(buyer_receive_acc, "finalized"); console.log("exercising call"); let sig = await (0, __1.exercise_call)(connection, contract, buyer_acc, buyer_nft_acc, buyer_receive_acc, buyer_send_acc); await connection.confirmTransaction(sig, "finalized"); // check that the ownership nft is burned let nft_bal_after = await connection.getTokenAccountBalance(buyer_nft_acc, "finalized"); assert_1.default.equal(nft_bal_after.value.amount, "0"); // check amounts are correct after exercise let send_bal_after = await connection.getTokenAccountBalance(buyer_send_acc, "finalized"); let recv_bal_after = await connection.getTokenAccountBalance(buyer_receive_acc, "finalized"); assert_1.default.equal(send_bal_after.value.uiAmount, send_bal_before.value.uiAmount - (strike * multiple)); assert_1.default.equal(recv_bal_after.value.uiAmount, recv_bal_before.value.uiAmount + multiple); }); it("alice creates put then bob buys and exercises", async function () { let strike = 2; let expiry = Date.now() / 1000 + 600; let multiple = 5; let [s, contract] = await (0, __1.create_put)(connection, strike, expiry, multiple, alice_acc, toka_key, tokb_key, alice_a_key, alice_b_key); console.log(contract, (0, utils_1.print_contract)(contract)); await connection.confirmTransaction(s, "finalized"); // wait a while to confirm the transactions let option_layout = await (0, __1.get_contract_from_blockchain)(connection, contract.account_id); console.log(option_layout); (0, utils_1.verify_contract)(contract, option_layout); // verify the contract matches what is on the blockchain // try to exercise the contract let buyer_acc = bob_acc; let buyer_send_acc = bob_a_key; let buyer_receive_acc = bob_b_key; // find the address for the ownership nft const buyer_nft_acc = await spl_token_1.Token.getAssociatedTokenAddress(spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID, spl_token_1.TOKEN_PROGRAM_ID, contract.nft_id, buyer_acc.publicKey); let create_acc_ix = await spl_token_1.Token.createAssociatedTokenAccountInstruction(spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID, spl_token_1.TOKEN_PROGRAM_ID, contract.nft_id, buyer_nft_acc, buyer_acc.publicKey, buyer_acc.publicKey); // send the ownership token to bob console.log("transfering ownership to bob"); let sell_ix = spl_token_1.Token.createTransferInstruction(spl_token_1.TOKEN_PROGRAM_ID, new web3_js_2.PublicKey(contract.nft_account), buyer_nft_acc, alice_acc.publicKey, [alice_acc], 1); var tx = new web3_js_2.Transaction(); tx.add(create_acc_ix, sell_ix); let sig_sell = await connection.sendTransaction(tx, [alice_acc, bob_acc], { skipPreflight: false, preflightCommitment: 'finalized' }); await connection.confirmTransaction(sig_sell, "finalized"); // confirm the nft ownership token let nft_bal_before = await connection.getTokenAccountBalance(buyer_nft_acc, "finalized"); assert_1.default.equal(nft_bal_before.value.amount, "1"); let send_bal_before = await connection.getTokenAccountBalance(buyer_send_acc, "finalized"); let recv_bal_before = await connection.getTokenAccountBalance(buyer_receive_acc, "finalized"); console.log("exercising call"); let sig = await (0, __1.exercise_put)(connection, contract, buyer_acc, buyer_nft_acc, buyer_receive_acc, buyer_send_acc); await connection.confirmTransaction(sig, "finalized"); // check that the ownership nft is burned let nft_bal_after = await connection.getTokenAccountBalance(buyer_nft_acc, "finalized"); assert_1.default.equal(nft_bal_after.value.amount, "0"); // check amounts are correct after exercise let send_bal_after = await connection.getTokenAccountBalance(buyer_send_acc, "finalized"); let recv_bal_after = await connection.getTokenAccountBalance(buyer_receive_acc, "finalized"); assert_1.default.equal(send_bal_after.value.uiAmount, send_bal_before.value.uiAmount - multiple); assert_1.default.equal(recv_bal_after.value.uiAmount, recv_bal_before.value.uiAmount + (strike * multiple)); }); it("bob tries to exercise a contract he doesnt own", async function () { let strike = 2; let expiry = Date.now() / 1000 + 600; let multiple = 5; let [s, contract] = await (0, __1.create_call)(connection, strike, expiry, multiple, alice_acc, toka_key, tokb_key, alice_a_key, alice_b_key); console.log(contract, (0, utils_1.print_contract)(contract)); await connection.confirmTransaction(s, "finalized"); // wait a while to confirm the transactions let option_layout = await (0, __1.get_contract_from_blockchain)(connection, contract.account_id); console.log(option_layout); (0, utils_1.verify_contract)(contract, option_layout); // verify the contract matches what is on the blockchain // try to exercise the contract let buyer_acc = bob_acc; let buyer_send_acc = bob_b_key; let buyer_receive_acc = bob_a_key; // find the address for the ownership nft const buyer_nft_acc = await spl_token_1.Token.getAssociatedTokenAddress(spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID, spl_token_1.TOKEN_PROGRAM_ID, contract.nft_id, buyer_acc.publicKey); let create_acc_ix = await spl_token_1.Token.createAssociatedTokenAccountInstruction(spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID, spl_token_1.TOKEN_PROGRAM_ID, contract.nft_id, buyer_nft_acc, buyer_acc.publicKey, buyer_acc.publicKey); var tx = new web3_js_2.Transaction(); tx.add(create_acc_ix); let sig_sell = await connection.sendTransaction(tx, [alice_acc, bob_acc], { skipPreflight: false, preflightCommitment: 'finalized' }); await connection.confirmTransaction(sig_sell, "finalized"); // confirm the nft ownership token wasn't received let nft_bal_before = await connection.getTokenAccountBalance(buyer_nft_acc, "finalized"); assert_1.default.equal(nft_bal_before.value.amount, "0"); let send_bal_before = await connection.getTokenAccountBalance(buyer_send_acc, "finalized"); let recv_bal_before = await connection.getTokenAccountBalance(buyer_receive_acc, "finalized"); console.log("exercising call"); // expect(await exercise_call(connection, contract, buyer_acc, buyer_nft_acc, buyer_receive_acc, buyer_send_acc)).to.throw() await assert_1.default.rejects((0, __1.exercise_call)(connection, contract, buyer_acc, buyer_nft_acc, buyer_receive_acc, buyer_send_acc)); // check amounts are correct after exercise let send_bal_after = await connection.getTokenAccountBalance(buyer_send_acc, "finalized"); let recv_bal_after = await connection.getTokenAccountBalance(buyer_receive_acc, "finalized"); // confirm bobs account is left untouched assert_1.default.equal(send_bal_after.value.uiAmount, send_bal_before.value.uiAmount); assert_1.default.equal(recv_bal_after.value.uiAmount, recv_bal_before.value.uiAmount); }); }); describe("create option then close", function () { this.timeout(600000); // tests can take up to 10 mins it("alice creates call then closes", async function () { let strike = 2; let expiry = Date.now() / 1000; let multiple = 5; let [s, contract] = await (0, __1.create_call)(connection, strike, expiry, multiple, alice_acc, toka_key, tokb_key, alice_a_key, alice_b_key); console.log(contract, (0, utils_1.print_contract)(contract)); await connection.confirmTransaction(s, "finalized"); // wait a while to confirm the transactions let option_layout = await (0, __1.get_contract_from_blockchain)(connection, contract.account_id); console.log(option_layout); (0, utils_1.verify_contract)(contract, option_layout); // verify the contract matches what is on the blockchain console.log("waiting 180s for contract to expire. Test will fail if validators are not current"); await new Promise(resolve => setTimeout(resolve, 180000)); // wait for contract to expire console.log("close call"); let sig = await (0, __1.close_option)(connection, contract, alice_acc, alice_a_key); await connection.confirmTransaction(sig, "finalized"); }); it("alice creates call then tries to close before expiry", async function () { let strike = 2; let expiry = Date.now() / 1000 + 600; let multiple = 5; let [s, contract] = await (0, __1.create_call)(connection, strike, expiry, multiple, alice_acc, toka_key, tokb_key, alice_a_key, alice_b_key); console.log(contract, (0, utils_1.print_contract)(contract)); await connection.confirmTransaction(s, "finalized"); // wait a while to confirm the transactions let option_layout = await (0, __1.get_contract_from_blockchain)(connection, contract.account_id); console.log(option_layout); (0, utils_1.verify_contract)(contract, option_layout); // verify the contract matches what is on the blockchain console.log("close call"); await assert_1.default.rejects((0, __1.close_option)(connection, contract, alice_acc, alice_a_key)); }); }); async function create_and_init_token_acc(mint, acc, owner, tx) { let create_acc_ix = web3_js_2.SystemProgram.createAccount({ programId: spl_token_1.TOKEN_PROGRAM_ID, space: spl_token_1.AccountLayout.span, lamports: await connection.getMinimumBalanceForRentExemption(spl_token_1.AccountLayout.span, 'confirmed'), fromPubkey: owner, newAccountPubkey: acc }); let init_acc_ix = spl_token_1.Token.createInitAccountInstruction(spl_token_1.TOKEN_PROGRAM_ID, mint, acc, owner); tx.add(create_acc_ix, init_acc_ix); return tx; }