Settings

Blockchain
Network
Unit
Language
Theme
Sound New Block

Transaction

9a48f9e69458d8bb013bc0c8e2c538913c40f7faf7b32c2bec21a8dd129b5e9e
Timestamp (utc)
2023-12-14 23:45:44
Fee Paid
0.00000011 BSV
(
0.00099110 BSV
-
0.00099099 BSV
)
Fee Rate
1.021 sat/KB
Version
1
Confirmations
143,877
Size Stats
10,766 B

2 Outputs

Total Output:
0.00099099 BSV
  • cordQ text/plainM) import { Addr, ByteString, FixedArray, SmartContract, fill, prop, toByteString, method, hash256, PubKeyHash, slice, reverseByteString, UTXO, byteString2Int, Utils, PubKey, Sig, hash160,assert, MethodCallOptions, ContractTransaction, bsv, StatefulNext, } from "scrypt-ts"; import { getDefaultSigner } from '../../tests/utils/txHelper' export type Vehicle = { name : ByteString maker : ByteString model : ByteString price : bigint outpoint : ByteString dealerAddr : Addr isEmptySlot : boolean hasRequestingBuyer : boolean requestingBuyer : Addr } export class AutoTrading extends SmartContract { static readonly VEHICLE_SLOTS = 20 @prop(true) vehicles : FixedArray<Vehicle, typeof AutoTrading.VEHICLE_SLOTS> constructor() { super(...arguments) this.vehicles = fill({ name : toByteString(''), maker : toByteString(''), model : toByteString(''), price : 0n, outpoint : toByteString(''), dealerAddr : Addr(toByteString('0000000000000000000000000000000000000000')), isEmptySlot : true, hasRequestingBuyer : false, requestingBuyer :Addr(toByteString('00000000000000000000000000000000000000')), }, AutoTrading.VEHICLE_SLOTS ) } @method() public listVehicle (vehicle : Vehicle, vehicleIdx : bigint) { assert(this.vehicles[Number(vehicleIdx)].isEmptySlot, ' No slot to display vehicle') assert(!vehicle.isEmptySlot, 'new vehicle can not have the "isEmptySlot" flag set to true" ') assert(vehicle.price>0n, 'vehicle price must be atleast 1 satoshi') assert(!vehicle.hasRequestingBuyer, 'new vehicle can not have requesting buyer set to true') this.vehicles[Number(vehicleIdx)] = vehicle let outputs = this.buildStateOutput(this.ctx.utxo.value) outputs += this.buildChangeOutput() this.debug.diffOutputs(outputs) assert(hash256(outputs) == this.ctx.hashOutputs, 'hash output mismatched') } @method() public requestBuy(vehicleIdx : bigint, buyerAddr : Addr) { assert(!this.vehicles[Number(vehicleIdx)].isEmptySlot, 'vehicle slot empty') const vehicle = this.vehicles[Number(vehicleIdx)] this.vehicles[Number(vehicleIdx)].hasRequestingBuyer = true this.vehicles[Number(vehicleIdx)].requestingBuyer = buyerAddr // make sure buyer made deposite to smart contract. let outputs = this.buildStateOutput(this.ctx.utxo.value + vehicle.price) outputs += this.buildChangeOutput() this.debug.diffOutputs(outputs) assert(hash256(outputs) == this.ctx.hashOutputs, 'hashoutput mismatch') } @method() public confirmBuy(vehicleIdx : bigint) { assert(!this.vehicles[Number(vehicleIdx)].isEmptySlot, ' vehicle slot empty') const vehicle = this.vehicles[Number(vehicleIdx)] this.vehicles[Number(vehicleIdx)].hasRequestingBuyer = true this.vehicles[Number(vehicleIdx)].requestingBuyer = PubKeyHash(toByteString ('00000000000000000000000000000000000000')) //make sure first input unlock the ordinal. assert(slice(this.prevouts, 0n, 36n) == vehicle.outpoint, 'first output is not spending specific ordinal utxo') //first output will transfer the ordinal to the buyer. let outputs = Utils.buildPublicKeyHashOutput(vehicle.requestingBuyer, 1n) //second output will be the platform contract itself. outputs = this.buildStateOutput(this.ctx.utxo.value - vehicle.price) // third output pay the dealer outputs += Utils.buildPublicKeyHashOutput(vehicle.dealerAddr, vehicle.price) //handle change and check output outputs += this.buildChangeOutput() this.debug.diffOutputs(outputs) assert(hash256(outputs) == this.ctx.hashOutputs, 'hashoutput mismatch') } @method() public cancelBuy(vehicleIdx: bigint, buyerPubKey : PubKey, buyerSig : Sig) { assert(!this.vehicles[Number(vehicleIdx)].isEmptySlot, ' vehicle slot empty') const vehicle = this.vehicles[Number(vehicleIdx)] // check buyer public key and signature assert(hash160(buyerPubKey) == vehicle.requestingBuyer, ' buyer pubkey is invalid') assert(this.checkSig(buyerSig, buyerPubKey), 'buyer sig invalid') this.vehicles[Number(vehicleIdx)].hasRequestingBuyer = false this.vehicles[Number(vehicleIdx)].requestingBuyer = Addr(toByteString('0000000000000000000000000000000000000000')) //subtract buyers deposit from contract and refund his address let output = this.buildStateOutput(this.ctx.utxo.value - vehicle.price) output += Utils.buildPublicKeyHashOutput(hash160(buyerPubKey), vehicle.price) output += this.buildChangeOutput() this.debug.diffOutputs(output) assert(hash256(output) == this.ctx.hashOutputs, 'hash output mismatched') } @method() public cancelListing(vehicleIdx: bigint, dealerPubKey: PubKey, dealerSig: Sig) { assert(!this.vehicles[Number(vehicleIdx)].isEmptySlot, 'vehicle slot empty') const vehicle = this.vehicles[Number(vehicleIdx)] // Check seller pubkey and sig. assert(hash160(dealerPubKey) == vehicle.dealerAddr, 'dealer invalid pubkey') assert(this.checkSig(dealerSig, dealerPubKey), 'dealer sig invalid') this.vehicles[Number(vehicleIdx)].isEmptySlot = true // Subtract buyers deposit from contract and refund his address. let outputs = this.buildStateOutput(this.ctx.utxo.value) outputs += this.buildChangeOutput() this.debug.diffOutputs(outputs) assert(hash256(outputs) == this.ctx.hashOutputs, 'hashOutputs mismatch') } static async buildTxForConfirmBuy( current: AutoTrading, options: MethodCallOptions<AutoTrading>, vehicleIdx : bigint ): Promise<ContractTransaction> { const changeAddress = await current.signer.getDefaultAddress() const next = options.next as StatefulNext<AutoTrading> const unsignedTx: bsv.Transaction = new bsv.Transaction() const vehiclePrice = Number(current.vehicles[Number(vehicleIdx)].price) //Fetch ordinal TX and extract UTXO. const outpoint = current.vehicles[Number(vehicleIdx)].outpoint const ordinalTxid = reverseByteString(slice(outpoint, 0n, 32n), 32n) const ordinalVout = Number(byteString2Int(slice(outpoint, 32n, 36n))) const signer = getDefaultSigner() const tx = await signer.provider!.getTransaction(ordinalTxid) const out = tx.outputs[ordinalVout] const ordinalUTXO: UTXO = { address: current.vehicles[Number(vehicleIdx)].dealerAddr, txId: ordinalTxid, outputIndex: ordinalVout, script: out.script.toHex(), satoshis: out.satoshis, } // Add input that unlocks ordinal UTXO. unsignedTx .addInput( new bsv.Transaction.Input({ prevTxId: ordinalUTXO.txId, outputIndex: ordinalUTXO.outputIndex, script: bsv.Script.fromHex('00'.repeat(34)), // Dummy script }), bsv.Script.fromHex(ordinalUTXO.script), ordinalUTXO.satoshis ) .addInput(current.buildContractInput()) //Build next instance output .addOutput(new bsv.Transaction.Output({ script : next.instance.lockingScript, satoshis : next.balance, })) // Build ordinal destination output. unsignedTx .addOutput( new bsv.Transaction.Output({ script: bsv.Script.fromHex( Utils.buildPublicKeyHashScript( current.vehicles[Number(vehicleIdx)].requestingBuyer ) ) , satoshis: 1, }) ) //Build next instance .addOutput(new bsv.Transaction.Output({ script : next.instance.lockingScript, satoshis : current.utxo.satoshis - vehiclePrice, })) // Build dealer payment output. .addOutput( new bsv.Transaction.Output({ script: bsv.Script.fromHex( Utils.buildPublicKeyHashScript( current.vehicles[Number(vehicleIdx)].dealerAddr ) ), satoshis: current.utxo.satoshis, }) ) if (options.changeAddress) { unsignedTx.change(options.changeAddress) } return Promise.resolve({ tx: unsignedTx, atInputIndex: 1, nexts: [], }) } static async buildTxForCancelBuy( current: AutoTrading, options: MethodCallOptions<AutoTrading>, vehicleIdx : bigint, buyerPubKey : PubKey, buyerSig : Sig, ): Promise<ContractTransaction> { const changeAddress = await current.signer.getDefaultAddress() hash160(buyerPubKey) == current.vehicles[Number(vehicleIdx)].requestingBuyer const next = options.next as StatefulNext<AutoTrading> const unsignedTx: bsv.Transaction = new bsv.Transaction() const vehiclePrice = Number(current.vehicles[Number(vehicleIdx)].price) // Add input that unlocks ordinal UTXO. unsignedTx .addInput(current.buildContractInput()) //Build next instance output .addOutput(new bsv.Transaction.Output({ script : next.instance.lockingScript, satoshis : current.utxo.satoshis - vehiclePrice, })) // Build buyer refund output. unsignedTx .addOutput( new bsv.Transaction.Output({ script: bsv.Script.fromHex( Utils.buildPublicKeyHashScript( next.instance.vehicles[Number(vehicleIdx)].requestingBuyer ) ) , satoshis: vehiclePrice, }) ) if (options.changeAddress) { //Build change output unsignedTx.change(options.changeAddress) } return Promise.resolve({ tx: unsignedTx, atInputIndex: 0, nexts: [{ instance : next.instance, atOutputIndex : 0, balance : next.balance - vehiclePrice, }], }) } }hv©]±!#ËæCÑUü xp»Ã—ˈ¬
    https://whatsonchain.com/tx/9a48f9e69458d8bb013bc0c8e2c538913c40f7faf7b32c2bec21a8dd129b5e9e