solana-options
Version:
Minting of options contract NFTs on the Solana blockchain
338 lines (266 loc) • 17.1 kB
text/typescript
import { AccountLayout, Token, TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID } from "@solana/spl-token";
// import { SystemProgram } from "@solana/web3.js";
import { AccountInfo, Keypair } from "@solana/web3.js";
import { Account, Connection, PublicKey, SystemProgram, SYSVAR_RENT_PUBKEY, Transaction, TransactionInstruction } from "@solana/web3.js";
// import PrintClass from "jimp"
import { assert } from "chai";
// const expect = require('chai').expect;
import { AssertionError, expect, should } from "chai";
// var sinon = require("sinon");
// import {sinon} from "sinon"
import sinon from "sinon";
import rewire from "rewire";
let [sol_key, usd_key] = [new Keypair().publicKey.toString(), new Keypair().publicKey.toString()]
let sol_options = rewire("./");
import { close_option, create_call, create_put, exercise_call, OptionType } from ".";
import { create_doc_img } from "./doc";
import { print_contract } from "./utils";
import Jimp from "jimp";
import QRCode from "qrcode";
// import { create_call, excercise_call, OptionType } from pack;
// pack.cre
// let [create_call, excercise_call, OptionType] = [sol_options.create_call, sol_options.exercise_call,sol_options.OptionType]
const programId = "4ktt843KtvduVq7yDdSXoTpt8uev4tBFue2pot7NdiRR"
const bob_private_key = [58,61,83,12,252,164,114,167,16,61,196,241,213,70,111,76,253,145,3,249,205,251,23,52,
237,158,140,188,84,202,116,216,78,242,2,9,33,177,53,200,7,221,76,53,149,13,243,125,153,187,239,178,76,
197,203,88,218,186,174,108,71,19,254,203]
let creator_strike_instrument_acc = new PublicKey('8Tv1h2eNjhfQJXyNR3Ey8WwoHAF1qZW7s7aNH49oCMBP')
let creator_instrument_acc = new PublicKey("GsnpfUQTFnQBuokhttUajopkEeg4YNgwf7j2SSKhRigJ")
let creator_acc = Keypair.fromSecretKey(new Uint8Array(bob_private_key))
const connection = new Connection("http://localhost:8899", 'singleGossip');
// setup some stubs
let get_rent = sinon.fake()
sinon.replace(connection, "getMinimumBalanceForRentExemption", get_rent);
let confirm_tx = sinon.fake()
sinon.replace(connection, "confirmTransaction", confirm_tx);
let send_tx = sinon.fake()
sinon.replace(connection, "sendTransaction", send_tx);
const mocked_keypair = new Keypair()
let find_address_stub = sinon.stub()
sinon.replace(PublicKey, "findProgramAddress", find_address_stub)
find_address_stub.returns([mocked_keypair.publicKey, 42])
afterEach(function () {
sinon.resetHistory();
});
describe('create call', function() {
it('create call contract using existing public keys without error', async function() {
let strike =100
let expiry = Date.now()/1000 + 600
let multiple = 1000000000
let instrument = new PublicKey('D3KCKMTY8rgWtVmxKdCeugKEFbJMjSHk7iWiLMAKrrMf')
let strike_instrument = new PublicKey('6Uhbk6FwCLLQfsKJ8kn8uCsiF5iZNVQDw5QxMNJh9XiJ')
return create_call(
connection,strike, expiry, multiple, creator_acc, instrument, strike_instrument, creator_instrument_acc, creator_strike_instrument_acc
).then(([sig, contract])=>{
expect(contract.strike).to.equal(strike, "strike value")
expect(contract.expiry).to.equal(expiry)
expect(contract.multiple).to.equal(multiple)
expect(contract.instrument.toString()).to.equal(instrument.toString(), "inst value")
expect(contract.strike_instrument.toString()).to.equal(strike_instrument.toString(), "strike instr value")
// expect(contract.nft_id.toString()).to.equal(mocked_keypair.publicKey.toString(), "nft id")
expect(contract.nft_account.toString()).to.equal(mocked_keypair.publicKey.toString(), "nft account")
// expect(contract.account_id.toString()).to.equal(mocked_keypair.publicKey.toString(), "account id")
expect(contract.writer_recv_acc).to.equal(creator_strike_instrument_acc, "receiving acc")
expect(contract.kind).to.equal(OptionType.call)
expect(send_tx.callCount).to.equal(1, "tx sent only once")
expect(find_address_stub.callCount).to.equal(2, "find address used twice")
}).catch()
});
it('create call contract using minted nft instrument without error', async function() {
let strike = 101
let expiry = Date.now()/1000 + 600
let multiple = 1000000000
// let instrument = new PublicKey('D3KCKMTY8rgWtVmxKdCeugKEFbJMjSHk7iWiLMAKrrMf')
let strike_instrument = new PublicKey('6Uhbk6FwCLLQfsKJ8kn8uCsiF5iZNVQDw5QxMNJh9XiJ')
return create_call(
connection,strike, expiry, multiple, creator_acc, null, strike_instrument, null, creator_strike_instrument_acc
).then(([sig, contract])=>{
expect(contract.strike).to.equal(strike, "strike value")
expect(contract.expiry).to.equal(expiry)
expect(contract.multiple).to.equal(multiple)
// expect(contract.instrument.toString()).to.equal(instrument.toString(), "inst value")
expect(contract.instrument).to.be.a("object")
expect(contract.instrument).to.be.ok;
expect(contract.strike_instrument.toString()).to.equal(strike_instrument.toString(), "strike instr value")
expect(contract.nft_account.toString()).to.equal(mocked_keypair.publicKey.toString(), "nft account")
expect(contract.writer_recv_acc).to.equal(creator_strike_instrument_acc, "receiving acc")
expect(contract.kind).to.equal(OptionType.call)
expect(send_tx.callCount).to.equal(2, "tx sent only once")
expect(confirm_tx.callCount).to.equal(1, "nft mint confirmed")
expect(find_address_stub.callCount).to.equal(4, "find address used twice")
}).catch()
});
it('create call contract using symbols without error', async function() {
let strike = 101
let expiry = Date.now()/1000 + 600
let multiple = 1000000000
// let instrument = new PublicKey('D3KCKMTY8rgWtVmxKdCeugKEFbJMjSHk7iWiLMAKrrMf')
// let strike_instrument = new PublicKey('6Uhbk6FwCLLQfsKJ8kn8uCsiF5iZNVQDw5QxMNJh9XiJ')
let fake_tok_list = [{symbol: "SOL", address: sol_key}, {symbol: "USDC", address: usd_key }]
sol_options.__set__("TOKEN_LIST", fake_tok_list )
console.log("using token list", fake_tok_list)
let [create_call, excercise_call, OptionType] = [sol_options.create_call, sol_options.exercise_call,sol_options.OptionType]
return create_call(
connection,strike, expiry, multiple, creator_acc, "USDC", "SOL", creator_instrument_acc, creator_strike_instrument_acc
).then(([sig, contract])=>{
expect(contract.instrument.toString()).to.be.equal(usd_key, "use USD token")
expect(contract.strike_instrument.toString()).to.equal(sol_key, "use SOL strike instr value")
expect(contract.nft_account.toString()).to.equal(mocked_keypair.publicKey.toString(), "nft account")
expect(contract.kind).to.equal(OptionType.call)
expect(send_tx.callCount).to.equal(1, "tx sent only once")
expect(confirm_tx.callCount).to.equal(0, "nft mint confirmed")
expect(find_address_stub.callCount).to.equal(2, "find address used twice")
}).catch()
});
it('create call contract using minted nft instrument and symbol strike instrument without error', async function() {
let strike = 101
let expiry = Date.now()/1000 + 600
let multiple = 1000000000
let fake_tok_list = [{symbol: "SOL", address: sol_key}, {symbol: "USDC", address: usd_key }]
sol_options.__set__("TOKEN_LIST", fake_tok_list )
console.log("using token list", fake_tok_list)
let [create_call, excercise_call, OptionType] = [sol_options.create_call, sol_options.exercise_call,sol_options.OptionType]
return create_call(
connection,strike, expiry, multiple, creator_acc, null, "SOL", null, creator_strike_instrument_acc
).then(([sig, contract])=>{
expect(contract.strike).to.equal(strike, "strike value")
expect(contract.expiry).to.equal(expiry)
expect(contract.multiple).to.equal(multiple)
// expect(contract.instrument.toString()).to.equal(instrument.toString(), "inst value")
expect(contract.instrument).to.be.a("object")
expect(contract.instrument).to.be.ok;
expect(contract.strike_instrument.toString()).to.equal(sol_key.toString(), "strike instr value")
expect(contract.nft_account.toString()).to.equal(mocked_keypair.publicKey.toString(), "nft account")
expect(contract.writer_recv_acc).to.equal(creator_strike_instrument_acc, "receiving acc")
expect(contract.kind).to.equal(OptionType.call)
expect(send_tx.callCount).to.equal(2, "tx sent only once")
expect(confirm_tx.callCount).to.equal(1, "nft mint confirmed")
expect(find_address_stub.callCount).to.equal(4, "find address used twice")
}).catch()
});
it('create call contract then close it without error', async function() {
let strike =100
let expiry = Date.now()/1000 + 600
let multiple = 1000000000
let instrument = new PublicKey('D3KCKMTY8rgWtVmxKdCeugKEFbJMjSHk7iWiLMAKrrMf')
let strike_instrument = new PublicKey('6Uhbk6FwCLLQfsKJ8kn8uCsiF5iZNVQDw5QxMNJh9XiJ')
return create_call(
connection,strike, expiry, multiple, creator_acc, instrument, strike_instrument, creator_instrument_acc, creator_strike_instrument_acc
).then(([sig, contract])=>{
return close_option(connection, contract, creator_acc, creator_instrument_acc).then(sig=>{
expect(send_tx.callCount).to.equal(2, "tx sent only once")
expect(find_address_stub.callCount).to.equal(3, "find address used twice")
})
}).catch()
});
});
describe("create put", function(){
it('create put contract has correct receiving address', async function() {
let strike =100
let expiry = Date.now()/1000 + 600
let multiple = 1000000000
let instrument = new PublicKey('D3KCKMTY8rgWtVmxKdCeugKEFbJMjSHk7iWiLMAKrrMf')
let strike_instrument = new PublicKey('6Uhbk6FwCLLQfsKJ8kn8uCsiF5iZNVQDw5QxMNJh9XiJ')
return create_put(
connection,strike, expiry, multiple, creator_acc, instrument, strike_instrument, creator_instrument_acc, creator_strike_instrument_acc
).then(([sig, contract])=>{
expect(contract.strike).to.equal(strike, "strike value")
expect(contract.expiry).to.equal(expiry)
expect(contract.multiple).to.equal(multiple)
expect(contract.instrument.toString()).to.equal(instrument.toString(), "inst value")
expect(contract.strike_instrument.toString()).to.equal(strike_instrument.toString(), "strike instr value")
// expect(contract.nft_id.toString()).to.equal(mocked_keypair.publicKey.toString(), "nft id")
expect(contract.nft_account.toString()).to.equal(mocked_keypair.publicKey.toString(), "nft account")
// expect(contract.account_id.toString()).to.equal(mocked_keypair.publicKey.toString(), "account id")
expect(contract.writer_recv_acc).to.equal(creator_instrument_acc, "receiving acc")
expect(contract.kind).to.equal(OptionType.put, "contract type")
expect(send_tx.callCount).to.equal(1, "tx sent only once")
expect(find_address_stub.callCount).to.equal(2, "find address used twice")
}).catch()
});
})
describe("exercise contract", function(){
it('exercising a call contract ', async function() {
let strike = 100
let expiry = Date.now()/1000 + 600
let multiple = 1000000000
let instrument = new PublicKey('D3KCKMTY8rgWtVmxKdCeugKEFbJMjSHk7iWiLMAKrrMf')
let strike_instrument = new PublicKey('6Uhbk6FwCLLQfsKJ8kn8uCsiF5iZNVQDw5QxMNJh9XiJ')
return create_call(
connection,strike, expiry, multiple, creator_acc, instrument, strike_instrument, creator_instrument_acc, creator_strike_instrument_acc
).then(([sig, contract])=>{
expect(contract.strike).to.equal(strike, "strike value")
expect(contract.expiry).to.equal(expiry)
expect(contract.multiple).to.equal(multiple)
expect(contract.instrument.toString()).to.equal(instrument.toString(), "inst value")
expect(contract.strike_instrument.toString()).to.equal(strike_instrument.toString(), "strike instr value")
// expect(contract.nft_id.toString()).to.equal(mocked_keypair.publicKey.toString(), "nft id")
expect(contract.nft_account.toString()).to.equal(mocked_keypair.publicKey.toString(), "nft account")
// expect(contract.account_id.toString()).to.equal(mocked_keypair.publicKey.toString(), "account id")
expect(contract.writer_recv_acc).to.equal(creator_strike_instrument_acc, "receiving acc")
expect(contract.kind).to.equal(OptionType.call, "contract type")
expect(send_tx.callCount).to.equal(1, "tx sent only once")
expect(find_address_stub.callCount).to.equal(2, "find address used twice")
let buyer_nft_acc = new Keypair().publicKey
let buyer_send_acc= new Keypair().publicKey
let buyer_receive_acc = new Keypair().publicKey
let buyer_acc = creator_acc
return exercise_call(connection, contract, buyer_acc, buyer_nft_acc, buyer_receive_acc, buyer_send_acc).then(sig=>{
expect(send_tx.callCount).to.equal(2, "tx sent only once")
expect(find_address_stub.callCount).to.equal(3, "find address used twice")
})
}).catch()
});
it('exercising an expired contract should fail ', async function() {
let strike =100
let expiry = Date.now()/1000 - 600
let multiple = 1000000000
let instrument = new PublicKey('D3KCKMTY8rgWtVmxKdCeugKEFbJMjSHk7iWiLMAKrrMf')
let strike_instrument = new PublicKey('6Uhbk6FwCLLQfsKJ8kn8uCsiF5iZNVQDw5QxMNJh9XiJ')
return create_call(
connection,strike, expiry, multiple, creator_acc, instrument, strike_instrument, creator_instrument_acc, creator_strike_instrument_acc
).then(async ([sig, contract])=>{
expect(contract.strike).to.equal(strike, "strike value")
expect(contract.expiry).to.equal(expiry)
expect(contract.multiple).to.equal(multiple)
expect(contract.instrument.toString()).to.equal(instrument.toString(), "inst value")
expect(contract.strike_instrument.toString()).to.equal(strike_instrument.toString(), "strike instr value")
// expect(contract.nft_id.toString()).to.equal(mocked_keypair.publicKey.toString(), "nft id")
expect(contract.nft_account.toString()).to.equal(mocked_keypair.publicKey.toString(), "nft account")
// expect(contract.account_id.toString()).to.equal(mocked_keypair.publicKey.toString(), "account id")
expect(contract.writer_recv_acc).to.equal(creator_strike_instrument_acc, "receiving acc")
expect(contract.kind).to.equal(OptionType.call, "contract type")
expect(send_tx.callCount).to.equal(1, "tx sent only once")
expect(find_address_stub.callCount).to.equal(2, "find address used twice")
let buyer_nft_acc = new Keypair().publicKey
let buyer_send_acc= new Keypair().publicKey
let buyer_receive_acc = new Keypair().publicKey
let buyer_acc = creator_acc
return exercise_call(connection, contract, buyer_acc, buyer_nft_acc, buyer_receive_acc, buyer_send_acc).then(async sig=>{
assert.fail("expire contract should not be exercised")
}).catch(err=>{
expect(err).to.be.equal("contract has exipired")
})
}).catch(err=>{
assert.fail(err)
})
});
})
describe("create contract doc", function(){
this.timeout(60_000);
it('create put contract doc', async function() {
let strike =100
let expiry = Date.now()/1000 + 600
let multiple = 1000000000
let instrument = new PublicKey('D3KCKMTY8rgWtVmxKdCeugKEFbJMjSHk7iWiLMAKrrMf')
let strike_instrument = new PublicKey('6Uhbk6FwCLLQfsKJ8kn8uCsiF5iZNVQDw5QxMNJh9XiJ')
let font_spy = sinon.spy(Jimp, "loadFont")
let qrcode_spy = sinon.spy(QRCode, "toDataURL")
const [sig, contract] = await create_put(
connection, strike, expiry, multiple, creator_acc, instrument, strike_instrument, creator_instrument_acc, creator_strike_instrument_acc
);
const img = await create_doc_img(contract);
expect(font_spy.callCount).to.equal(1);
expect(qrcode_spy.callCount).to.equal(1);
});
})