solana-options
Version:
Minting of options contract NFTs on the Solana blockchain
238 lines (237 loc) • 17.1 kB
JavaScript
"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;
}