Skip to content

Commit

Permalink
fixes for the native mints
Browse files Browse the repository at this point in the history
  • Loading branch information
dbanda committed Jan 17, 2022
1 parent d958397 commit 4f03236
Show file tree
Hide file tree
Showing 7 changed files with 329 additions and 162 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ arrayref = "0.3.6"
borsh = "0.9.1"
borsh-derive = "0.9.1"
spl-associated-token-account = { version = "1.0.3", features = ["no-entrypoint"]}
# solana-sdk = "1.8.1"
#solana-sdk = "1.8.1"

[dev-dependencies]
solana-program-test = "1.7.9"
solana-sdk = "1.8.1"

[lib]
name = "solana_options"
crate-type = ["cdylib", "lib"]
crate-type = ["cdylib", "lib"]
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ Let's show how you can create a call contract to sell 420 units of SOL token for
var sol = require("@solana/web3.js");
var sol_options = require("solana-options")

// included for example purpose.
// In practice this would come securely from a wallet such as Phantom e.t.c
const your_private_key = [45,142,52,139,158,173,187,83,102,42,19,164,139,139,205,
206,230,214,180,206,143,85,173,181,255,225,10,156,247,8,71,177,181,140,215,
137,129,185,26,79,119,184,240,246,7,123,174,112,154,172,151,52,204,95,75,118,
Expand All @@ -36,12 +38,12 @@ const connection = new sol.Connection("https://api.devnet.solana.com", 'singleGo
```Javascript
// create a call contract contract

// your account
// your account. In practice this would come securely from a wallet like Phantom e.t.c
let creator_acc = sol.Keypair.fromSecretKey(new Uint8Array(your_private_key))

// set strikes and expiry
let strike = 69
let expiry = Date.now()/1000 + 600 //expire in 10 mins
let expiry = Date.now()/1000 + 600 //expire in 10 mins (600s)
let multiple = 420

// the address or symbol you are selling on this call
Expand Down
2 changes: 1 addition & 1 deletion src/client/nodejs/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "solana-options",
"version": "1.0.8",
"version": "1.0.9",
"description": "Minting of options contract NFTs on the Solana blockchain",
"main": "build/index.js",
"scripts": {
Expand Down
57 changes: 52 additions & 5 deletions src/client/nodejs/src/e2e_live_tests/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { AccountInfo, Keypair } from "@solana/web3.js";
import { Account, Connection, PublicKey, SystemProgram, SYSVAR_RENT_PUBKEY, Transaction, TransactionInstruction } from "@solana/web3.js";
import BN from "bn.js";
import Jimp from "jimp/*";
import { create_call, create_put, exercise_call, exercise_put, get_contract_from_blockchain } from "..";
import { close_option, create_call, create_put, exercise_call, exercise_put, get_contract_from_blockchain } from "..";
import { create_doc_img, publish_doc } from "../doc";
import { OPTION_ACCOUNT_DATA_LAYOUT, OptionLayout } from "../layout";
import { print_contract, verify_contract } from "../utils";
Expand Down Expand Up @@ -179,7 +179,6 @@ describe("create option then exercise", function(){
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_a_key
Expand All @@ -203,7 +202,7 @@ describe("create option then exercise", function(){
await connection.confirmTransaction(sig_sell, "finalized");


// confirm the nft ownership token wasn't received
// confirm the nft ownership token
let nft_bal_before = await connection.getTokenAccountBalance(buyer_nft_acc, "finalized")
assert.equal(nft_bal_before.value.amount, "1")

Expand All @@ -222,8 +221,8 @@ describe("create option then exercise", function(){
let send_bal_after = await connection.getTokenAccountBalance(buyer_send_acc, "finalized")
let recv_bal_after = await connection.getTokenAccountBalance(buyer_receive_acc, "finalized")

assert.equal(send_bal_after.value.uiAmount, send_bal_before.value.uiAmount-(strike*multiple))
assert.equal(recv_bal_after.value.uiAmount, recv_bal_before.value.uiAmount+multiple)
assert.equal(send_bal_after.value.uiAmount, send_bal_before.value.uiAmount-multiple)
assert.equal(recv_bal_after.value.uiAmount, recv_bal_before.value.uiAmount+(strike*multiple))

})

Expand Down Expand Up @@ -285,6 +284,54 @@ describe("create option then exercise", function(){
})
})

describe("create option then close", function(){
this.timeout(600_000); // 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 create_call(
connection,strike, expiry, multiple, alice_acc, toka_key, tokb_key, alice_a_key, alice_b_key
)

console.log(contract, print_contract(contract))

await connection.confirmTransaction(s, "finalized") // wait a while to confirm the transactions
let option_layout = await get_contract_from_blockchain(connection, contract.account_id)
console.log(option_layout)
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, 180_000)); // wait for contract to expire
console.log("close call")
let sig = await 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 create_call(
connection,strike, expiry, multiple, alice_acc, toka_key, tokb_key, alice_a_key, alice_b_key
)

console.log(contract, print_contract(contract))

await connection.confirmTransaction(s, "finalized") // wait a while to confirm the transactions
let option_layout = await get_contract_from_blockchain(connection, contract.account_id)
console.log(option_layout)
verify_contract(contract, option_layout) // verify the contract matches what is on the blockchain

console.log("close call")
await assert.rejects(close_option(connection, contract, alice_acc, alice_a_key))
})
})

async function create_and_init_token_acc(mint: PublicKey, acc: PublicKey, owner: PublicKey, tx: Transaction): Promise<Transaction>{
let create_acc_ix = SystemProgram.createAccount( {
programId: TOKEN_PROGRAM_ID,
Expand Down
30 changes: 15 additions & 15 deletions src/client/nodejs/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@

import { AccountLayout, MintLayout, 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 { Keypair, Signer } from "@solana/web3.js";
import { Connection, PublicKey, SystemProgram, SYSVAR_RENT_PUBKEY, Transaction, TransactionInstruction } from "@solana/web3.js";
import BN from "bn.js";
import dayjs from "dayjs";
import { OPTION_ACCOUNT_DATA_LAYOUT, OptionLayout } from "./layout";
import { OPTION_ACCOUNT_DATA_LAYOUT } from "./layout";
import {print_contract, get_contract_from_blockchain, verify_contract} from "./utils";
export {get_contract_from_blockchain, verify_contract, print_contract};
import { publish_doc, create_doc_img } from "./doc";
export {publish_doc, create_doc_img};
import { TokenListProvider, TokenInfo } from '@solana/spl-token-registry';

const OPTIONS_PROGRAM_ID = "DV4NugS55eXXposgxLnLr7WxySCTpaDd3cQPegFenHaj"
const OPTIONS_PROGRAM_ID = process.env.OPTIONS_PROGRAM_ID || process.env.REACT_APP_OPTIONS_PROGRAM_ID || "DV4NugS55eXXposgxLnLr7WxySCTpaDd3cQPegFenHaj"
const SEED = "optionsnft";
let TOKEN_LIST : TokenInfo[] = null
const CLUSTER_SLUG = "mainnet-beta"
Expand Down Expand Up @@ -71,7 +70,7 @@ async function get_token_map(): Promise<Map<string,string>>{
* @param creator_strike_instrument_acc
* @returns
*/
export async function create_call(connection: Connection, strike: number, expiry: number, multiple: number, creator_account: Keypair,
export async function create_call(connection: Connection, strike: number, expiry: number, multiple: number, creator_account: Signer,
instrument: PublicKey | string, strike_instrument: PublicKey | string, creator_instrument_acc: PublicKey,
creator_strike_instrument_acc: PublicKey) : Promise<[string, Contract]>{
console.log("creating call contract")
Expand Down Expand Up @@ -103,7 +102,7 @@ export async function create_put(connection: Connection, strike: number, expiry:
strike_instrument, creator_instrument_acc, creator_strike_instrument_acc, OptionType.put)
}

async function create_new_nft_mint(connection: Connection, multiple: number, creator_account: Keypair) {
async function create_new_nft_mint(connection: Connection, multiple: number, creator_account: Signer) {
const instrument_mint_acc = new Keypair();
console.log("instrument mint account key: ", instrument_mint_acc.publicKey.toString())
const mint_rent = await connection.getMinimumBalanceForRentExemption(MintLayout.span, 'confirmed')
Expand All @@ -122,7 +121,6 @@ async function create_new_nft_mint(connection: Connection, multiple: number, cre
const [creator_instrument_acc, _] = await PublicKey.findProgramAddress(
[
creator_account.publicKey.toBytes(),
// ASSOCIATED_TOKEN_PROGRAM_ID.toBuffer(),
TOKEN_PROGRAM_ID.toBytes(),
instrument_mint_acc.publicKey.toBytes()
], ASSOCIATED_TOKEN_PROGRAM_ID);
Expand Down Expand Up @@ -179,7 +177,7 @@ async function create_new_nft_mint(connection: Connection, multiple: number, cre
return [sig, instrument_mint_acc, creator_instrument_acc]
}

export async function create_option(connection: Connection, strike: number, expiry: number, multiple: number, creator_account: Keypair,
export async function create_option(connection: Connection, strike: number, expiry: number, multiple: number, creator_account: Signer,
instrument: PublicKey | string, strike_instrument: PublicKey | string, creator_instrument_acc: PublicKey, creator_strike_instrument_acc: PublicKey, kind: OptionType) : Promise<[string, Contract]>{

// check if the either instrument or strike_instrument is a symbol or address(public); assume symbol if string
Expand Down Expand Up @@ -250,13 +248,11 @@ export async function create_option(connection: Connection, strike: number, expi
const [nft_associated_account, _] = await PublicKey.findProgramAddress(
[
creator_account.publicKey.toBytes(),
// ASSOCIATED_TOKEN_PROGRAM_ID.toBuffer(),
TOKEN_PROGRAM_ID.toBytes(),
nftTokenAccount.publicKey.toBytes()
], ASSOCIATED_TOKEN_PROGRAM_ID);

console.log("nft token: ", nftTokenAccount.publicKey.toString())
// console.log("nft token receive acc", nft_pda_acc.publicKey.toString())
console.log("nft associated account: ", nft_associated_account.toString())

const [pda, _bump_seed] = await PublicKey.findProgramAddress([Buffer.from(SEED)], optionsProgramId)
Expand Down Expand Up @@ -352,13 +348,13 @@ export async function create_option(connection: Connection, strike: number, expi

}

export async function exercise_call(connection: Connection, contract: Contract, buyer_acc: Keypair, buyer_nft_acc: PublicKey,
export async function exercise_call(connection: Connection, contract: Contract, buyer_acc: Signer, buyer_nft_acc: PublicKey,
buyer_receive_acc: PublicKey, buyer_send_acc: PublicKey){
return exercise_option(connection, contract, buyer_acc, buyer_nft_acc,
buyer_receive_acc, buyer_send_acc, OptionType.call)
}

export async function exercise_put(connection: Connection, contract: Contract, buyer_acc: Keypair, buyer_nft_acc: PublicKey,
export async function exercise_put(connection: Connection, contract: Contract, buyer_acc: Signer, buyer_nft_acc: PublicKey,
buyer_receive_acc: PublicKey, buyer_send_acc: PublicKey){
return exercise_option(connection, contract, buyer_acc, buyer_nft_acc,
buyer_receive_acc, buyer_send_acc, OptionType.put)
Expand All @@ -368,14 +364,14 @@ export async function exercise_put(connection: Connection, contract: Contract, b
* Exercises the options contract
* @param connection connection to the cluster
* @param contract the Contract
* @param buyer_acc buyer's keypair
* @param buyer_acc buyer's account
* @param buyer_nft_acc the buyer's account that holds the ownership nft. This get burned by the exercise instruction
* @param buyer_receive_acc account the buyers expects to receive the options collateral
* @param buyer_send_acc the account holding the tokens the buyer is sending to exercise this contract
* @param kind call or put
* @returns signature
*/
export async function exercise_option(connection: Connection, contract: Contract, buyer_acc: Keypair, buyer_nft_acc: PublicKey,
export async function exercise_option(connection: Connection, contract: Contract, buyer_acc: Signer, buyer_nft_acc: PublicKey,
buyer_receive_acc: PublicKey, buyer_send_acc: PublicKey, kind: OptionType) : Promise<string> {

let strike = contract.strike;
Expand Down Expand Up @@ -445,6 +441,10 @@ export async function exercise_option(connection: Connection, contract: Contract
pubkey: pda_account,
isSigner: false,
isWritable: true
}, {
pubkey: SystemProgram.programId,
isSigner: false,
isWritable: false
}
],
data: Buffer.from(Uint8Array.of(1, ...new BN(strike).toArray("le", 8),
Expand Down
2 changes: 1 addition & 1 deletion src/client/nodejs/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export async function verify_contract(contract: Contract, option_layout: OptionL
let expiry = Number(option_layout.expiry_date.readBigInt64LE())
if (today > dayjs(expiry*1000)){
console.error("This contract details are correct but it expired on %s , today is %s",
dayjs(expiry).format(), today.format())
dayjs(expiry*1000).format(), today.format())
throw "contract has exipired"
}
return true
Expand Down
Loading

0 comments on commit 4f03236

Please sign in to comment.