❤️ 0 Likes · ⚡ 0 Tips
{
"txid": "3f93e9d86c45e8ea407982ad624ca6304c1291cc59c12e88098d725669194665",
"block_height": 801388,
"time": null,
"app": "pow.co",
"type": "post",
"map_content": "# Buyable / Sellable Scrypt Objects\n\n## A simple pattern to \u201cmixin\u201d to any stateful smart object you want to be sellable\nIf a smart contract has an owner property and can be \"ownable\", what makes it \"sellable\"?\n\nAny contract that adds an owner property may also add a price property that indicates that the object becomes \"sellable\". It must then implement a buy() method which assigns the owner to the new owner\u2019s address only if the price is paid. It will also need a setPrice(signature: Signature) method allowing the owner to set the price. Perhaps setting the price to 0 would disallow sales, or perhaps there is another prop called buyable and a method called setBuyable(buyable: boolean) which would allow the user to list it for sale any time.\n\n```\nimport {\n method,\n prop,\n SmartContract,\n hash256,\n hash160,\n assert,\n ByteString,\n SigHash,\n PubKey,\n PubKeyHash,\n Sig\n} from 'scrypt-ts'\n\nexport class Sellable extends SmartContract {\n\n @prop(true)\n pubKeyHash: PubKeyHash\n\n @prop(true)\n price: bigint\n\n @prop(true)\n buyable: boolean\n\n constructor(pubKeyHash: PubKeyHash) {\n super(...arguments)\n this.pubKeyHash = pubKeyHash\n this.matureTime = matureTime\n }\n\n // callable only by the seller\n @method()\n public setPrice(price: bigint, signature: Sig, pubkey: PubKey) {\n assert(hash160(pubkey) === this.pubKeyHash)\n assert(this.checkSig(signature, pubkey), `checkSig failed, pubKeyHash: ${this.pubKeyHash}`)\n\n const stateOutput = this.buildStateOutput(amount)\n assert(this.ctx.hashOutputs == hash256(stateOutput), \"Output hashes don't match\")\n\n this.price = price;\n }\n\n // callable only by the seller\n @method()\n public setBuyable(buyable: boolean, signature: Sig, pubkey: PubKey) {\n assert(hash160(pubkey) === this.pubKeyHash)\n assert(this.checkSig(signature, pubkey), `checkSig failed, pubKeyHash: ${this.pubKeyHash}`)\n\n // Could this somehow be exacted into a re-usable library like so:??\n // import assertState from 'scrypt-assert-state'\n // assertState(this)\n assert(this.ctx.hashOutputs == hash256(this.buildStateOutput(this.ctx.utxo.value)), \"Output hashes don't match\")\n\n this.buyable = buyable;\n }\n\n // callable only by the buyer\n @method()\n public buy(pubKeyHash: PubKeyHash) {\n\n assert(this.buyable)\n\n // Ensure exact payment of the price to the current owner's address\n const paymentOutput = Utils.buildPublicKeyHashOutput(this.pubKeyHash, this.price)\n assert(hash256(paymentOutput) == this.ctx.hashOutputs, 'invalid output for payment to seller')\n\n // Ensure the smart object's state remains entirely intact during the sale\n const stateOutput = this.buildStateOutput(this.ctx.utxo.value)\n assert(this.ctx.hashOutputs == hash256(stateOutput), \"Output hashes don't match\")\n\n // \"transfer\" ownership by assigning a new owner\n this.pubKeyHash = pubKeyHash\n }\n\n}\n```",
"media_type": "text/markdown",
"filename": "|",
"author": "15D5UryaVwjg2ATtLVYm5854RCM2aZL8At",
"display_name": null,
"channel": null,
"parent_txid": null,
"ref_txid": null,
"tags": null,
"reply_count": 0,
"like_count": 0,
"timestamp": "2023-07-19T19:56:02.000Z",
"media_url": null,
"aip_verified": true,
"thread_root_tx": null,
"engagement_score": 0,
"token_ref": null,
"token_type": null,
"kind": null,
"lat": null,
"lng": null,
"category": null,
"locked_sats": "0",
"pow_bits": 0,
"has_access": true,
"attachments": [],
"ui_name": "15D5Ur\u2026L8At",
"ui_display_name": "15D5Ur\u2026L8At",
"ui_handle": null,
"ui_display_raw": null,
"ui_signer": "15D5Ur\u2026L8At",
"ref_ui_name": "unknown",
"ref_ui_signer": "unknown"
}