UNPKG

parkingsv-contract

Version:

ParkingSV library

228 lines (190 loc) 6.43 kB
import { PrivateKey, PublicKey, Script, Signature, Transaction } from "bsv"; import { buildContractClass, ContractDescription, } from "./scryptlib/contract"; import { Bytes, PubKey, Sig } from "./scryptlib/scryptTypes"; import { bsv, getPreimage, signTx, toHex } from "./scryptlib/utils"; import { ContractCallHelper } from "./contractHelper"; import { createLockingTx, createUnlockingTx, sendTx, sendTxHex } from "./helper"; import { Ledger } from "./ledger"; import { TokenContract } from "./tokenContract"; import { TxUtil } from "./txUtil"; export class ParkingToken { tokenContract: TokenContract; ledger: Ledger; ownerPrivateKey: PrivateKey; ownerPublicKey: PublicKey; amount: number = 30000; fee: number = 2000; lockingTx: Transaction; lockingTxid: string; private constructor(privateKey: PrivateKey, desc: ContractDescription) { const Token = buildContractClass(desc); this.tokenContract = new Token(); this.ledger = new Ledger(); this.ownerPrivateKey = privateKey; this.ownerPublicKey = bsv.PublicKey.fromPrivateKey(this.ownerPrivateKey); } static async deployContract( privateKey: PrivateKey, desc: ContractDescription ): Promise<ParkingToken> { const instance = new ParkingToken(privateKey, desc); await instance.initContract(); return instance; } async initContract(): Promise<void> { try { this.ledger.addHolder(toHex(this.ownerPublicKey)); this.ledger.setBalance(toHex(this.ownerPublicKey), 100); this.tokenContract.dataLoad = this.ledger.getDataLoad(); const scriptLengthF = this.tokenContract.lockingScript.toHex().length / 2 - this.tokenContract.dataLoad.length / 2; console.log(`Script length: ${scriptLengthF} bytes`); this.lockingTx = await createLockingTx( this.ownerPrivateKey.toAddress(), this.amount, this.fee ); this.lockingTx.outputs[0].setScript( this.tokenContract.lockingScript as Script ); this.lockingTx.sign(this.ownerPrivateKey); console.log("fee: ", this.fee); this.lockingTxid = await sendTx(this.lockingTx); console.log("funding txid: ", this.lockingTxid); } catch (err) { console.log(err); } } public async initFromTxData(txData: string) { this.ledger = Ledger.fromTxData(txData); } async addNewUser(publicKey: string): Promise<string> { // Getting last contract instance console.log(`[TOKEN CONTRACT]: Adding ${publicKey}`); this.ledger.addHolder(publicKey); // update data state [!] const cch = await this.prepareCall(); cch.unlockingScript = this.tokenContract .addNewMember( cch.sender, cch.signature, cch.preimage, cch.newAmount, new PubKey(publicKey) ) .toScript() as Script; this.lockingTxid = await cch.sendTX(); this.amount = cch.newAmount; return this.lockingTxid; } static async fromTransaction( privateKey: PrivateKey, lastKnownTransaction: string, desc: ContractDescription ): Promise<ParkingToken> { const instance = new ParkingToken(privateKey, desc); const lastTransaction = await TxUtil.getLastTransaction( lastKnownTransaction ); const txData = await TxUtil.getTxData(lastTransaction); instance.initFromTxData(txData); instance.lockingTxid = lastKnownTransaction; instance.amount = await TxUtil.getTxUnspentAmount(lastTransaction); console.log(instance.amount) return instance; } async transferTokens(toAddress: string, amount: number): Promise<void> { const transferData = this.ledger.transfer( toHex(this.ownerPublicKey), toAddress, amount ); console.log(this.ledger.toString()); console.log(transferData); const cch = await this.prepareCall(); cch.unlockingScript = this.tokenContract .transfer( cch.sender, cch.signature, cch.preimage, cch.newAmount, transferData.fromIndex, transferData.toIndex, transferData.amount ) .toScript() as Script; this.lockingTxid = await cch.sendTX(); this.amount = cch.newAmount; } async transferTokensJSON(toAddress: string, amount: number): Promise<string> { const transferData = this.ledger.transfer( toHex(this.ownerPublicKey), toAddress, amount ); console.log(this.ledger.toString()); console.log(transferData); const cch = await this.prepareCall(); cch.unlockingScript = this.tokenContract .transfer( cch.sender, cch.signature, cch.preimage, cch.newAmount, transferData.fromIndex, transferData.toIndex, transferData.amount ) .toScript() as Script; return cch.getTxJSON(); } async payByJSON(txHex: string) { const buffer = Buffer.from(txHex); const tx = new Transaction(buffer); console.log(tx); const lockingTxid = await sendTxHex(txHex); const txData = await TxUtil.getTxData(lockingTxid); this.initFromTxData(txData); this.lockingTxid = lockingTxid ; this.amount = await TxUtil.getTxUnspentAmount(lockingTxid ); console.log(this.amount) } private async prepareCall(): Promise<ContractCallHelper> { const prevLockingScript = this.tokenContract.lockingScript; this.tokenContract.dataLoad = this.ledger.getDataLoad(); const newLockingScriptASM = this.tokenContract.lockingScript.toASM(); const newAmount = this.amount - this.fee; if (newAmount < 0) throw new Error("Not enough money on contract!"); const unlockScriptTx = createUnlockingTx( this.lockingTxid, this.amount, prevLockingScript.toASM(), newAmount, newLockingScriptASM ) as Transaction; // here was AWAIT(!) // call contract method to get unlocking script const preimage = getPreimage( unlockScriptTx, prevLockingScript.toASM(), this.amount ); const sig1 = signTx( unlockScriptTx, this.ownerPrivateKey, prevLockingScript.toASM(), this.amount ); // Generating Contract Call Helper structure return new ContractCallHelper({ sender: new PubKey(toHex(this.ownerPublicKey)), signature: new Sig(toHex(sig1)) as Signature, preimage: new Bytes(toHex(preimage)), newAmount, unlockScriptTx, }); } }