Skip to content

Commit

Permalink
Add bsv-20 put option.
Browse files Browse the repository at this point in the history
  • Loading branch information
msinkec committed Nov 22, 2023
1 parent 5a24caf commit b82def5
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
Utils,
} from 'scrypt-ts'

export class Bsv20Option extends BSV20V2 {
export class Bsv20CallOption extends BSV20V2 {
@prop()
grantor: PubKey

Expand Down
163 changes: 163 additions & 0 deletions src/contracts/bsv20PutOption.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import { BSV20V2 } from 'scrypt-ord'
import {
assert,
ByteString,
byteString2Int,
hash256,
method,
prop,
PubKey,
pubKey2Addr,
Sig,
slice,
Utils,
} from 'scrypt-ts'
import { RabinPubKey, RabinSig, RabinVerifier } from 'scrypt-ts-lib'

export class Bsv20PutOption extends BSV20V2 {
@prop()
grantor: PubKey

@prop(true)
grantee: PubKey

@prop()
tokenAmt: bigint

@prop()
strikePrice: bigint

@prop()
expirationTime: bigint

@prop(true)
forSale: boolean

@prop(true)
premium: bigint

@prop()
oraclePubKey: RabinPubKey

constructor(
id: ByteString,
sym: ByteString,
max: bigint,
dec: bigint,
grantor: PubKey,
grantee: PubKey,
tokenAmt: bigint,
strikePrice: bigint,
expirationTime: bigint,
forSale: boolean,
premium: bigint,
oraclePubKey: RabinPubKey
) {
super(id, sym, max, dec)
this.init(...arguments)

this.grantor = grantor
this.grantee = grantee
this.tokenAmt = tokenAmt
this.strikePrice = strikePrice
this.expirationTime = expirationTime
this.forSale = forSale
this.premium = premium
this.oraclePubKey = oraclePubKey
}

@method()
public exercise(
sigGrantee: Sig,
oracleSig: RabinSig,
oracleMsg: ByteString
) {
// Check oracle signature.
assert(
RabinVerifier.verifySig(oracleMsg, oracleSig, this.oraclePubKey),
'oracle sig verify failed'
)

// Check that we're unlocking the UTXO specified in the oracles message.
assert(
slice(this.prevouts, 0n, 36n) == slice(oracleMsg, 0n, 36n),
'first input is not spending specified ordinal UTXO'
)

// Get token amount held by the UTXO from oracle message.
const utxoTokenAmt = byteString2Int(slice(oracleMsg, 36n, 44n))

// Check token amount is correct.
assert(utxoTokenAmt == this.tokenAmt, 'invalid token amount')

// Check grantee sig.
assert(this.checkSig(sigGrantee, this.grantee), 'invalid sig grantee')

// Ensure grantor gets payed tokens.
let outputs = BSV20V2.buildTransferOutput(
pubKey2Addr(this.grantor),
this.id,
this.tokenAmt
)

// Ensure grantee gets payed satoshis.
const satAmt = this.strikePrice * this.tokenAmt
outputs += Utils.buildAddressOutput(pubKey2Addr(this.grantee), satAmt)

outputs += this.buildChangeOutput()

// Enforce outputs in call tx.
assert(hash256(outputs) == this.ctx.hashOutputs, 'hashOutputs mismatch')
}

@method()
public buy(newGrantee: PubKey) {
// Check if option is up for sale.
assert(this.forSale, 'option is not up for sale')

// Set new grantee.
const prevGrantee = this.grantee
this.grantee = newGrantee

// Toggle for sale flag.
this.forSale = false

// Propagate contract.
let outputs = this.buildStateOutput(this.ctx.utxo.value)

// Make sure premium is payed to previous grantee / holder.
outputs += Utils.buildAddressOutput(
pubKey2Addr(prevGrantee),
this.premium
)

outputs += this.buildChangeOutput()
assert(hash256(outputs) == this.ctx.hashOutputs, 'hashOutputs mismatch')
}

@method()
public listForSale(sigGrantee: Sig, premium: bigint) {
// Check grantee sig.
assert(this.checkSig(sigGrantee, this.grantee), 'invalid sig grantee')

// Store premium value in property.
this.premium = premium

// Toggle for sale flag.
this.forSale = true

// Propagate contract.
let outputs = this.buildStateOutput(this.ctx.utxo.value)
outputs += this.buildChangeOutput()
assert(hash256(outputs) == this.ctx.hashOutputs, 'hashOutputs mismatch')
}

@method()
public expiry(sigGrantor: Sig) {
// Check grantor sig.
assert(this.checkSig(sigGrantor, this.grantor), 'invalid sig grantor')

// Check if expired.
assert(this.timeLock(this.expirationTime), 'option has not yet expired')
}
}
2 changes: 1 addition & 1 deletion src/contracts/bsv20SellLimitOrder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export class BSV20SellLimitOrder extends BSV20V2 {
}

@method()
public buy(amount: bigint, buyerAddr: Addr) {
public buy(amount: bigint) {
// Check token amount doesn't exceed total.
assert(
this.tokenAmtSold + amount < this.tokenAmt,
Expand Down

0 comments on commit b82def5

Please sign in to comment.