Oracle
The Oracle contract is a way to access off-chain data on the blockchain. Below is a solution for implementing Oracle in a BTC environment with the OP_CAT opcode enabled.
Architecture
As shown in the diagram, the user provides their UTXO and feeRate. The Oracle service uses the UTXO to construct transactions. The outputs include a payload commit contract, user-defined outputs, service fees, and change for fees. Once the commit transaction is constructed, the Oracle service creates and signs a reveal transaction using the payload commit output. It returns both the commit and reveal PSBT hex strings along with the raw payload commit output. The following code demonstrates how to obtain a timestamp using this setup.
After receiving the commit and reveal transactions, the user signs the commit transaction and uses the signed reveal input to construct a new contract call transaction. This allows the contract to access Oracle data.
curl -X 'POST' \
'https://oracle.scrypt.io/v1/timestamp' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"utxos": [
{
"txId": "985fc2fb0ed1179672c1fd08afa81f19179aa6939622f6809659a3acfd9e0000",
"outputIndex": 5,
"script": "5120d9b50025be40bffc75ef262c020bfe2b52d0f5ecfeb280ecf5c35fbfdb856300",
"satoshis": 184814984
}
],
"publicKey": "02943e87ed8c2d38a8b3ec76999b70e4a605dbe041632099cac7387238b4012200",
"feeRate": 1
}'
{
"commitPsbt": "70736274ff0100d7020000000100009efdaca3599680f6229693a69a17191fa8af08fdc1729617d10efbc25f980500000000ffffffff0400000000000000001a6a1863617401f4d5ea814aabc273f329f3563848a090fc8c77cf4a01000000000000220020a110df5f2a3e0c0ea9ec2a96b58bc81a798175309b9134c5deca57111d9a2b3d220200000000000022512057a120ae7176b7ee20fbd6325aa98ea60c69b49ddf4c505756e2b8a5668d06023309040b00000000225120d9b50025be40bffc75ef262c020bfe2b52d0f5ecfeb280ecf5c35fbfdb856300000000000001012b880d040b00000000225120d9b50025be40bffc75ef262c020bfe2b52d0f5ecfeb280ecf5c35fbfdb8563000000000000",
"commitOptions": {
"autoFinalized": false,
"toSignInputs": [
{
"index": 0,
"address": "bc1pmx6sqfd7gzllca00yckqyzl79dfdpa0vl6egpm84cd0mlku9vvqqc5j5ng"
}
]
},
"revealPsbt": "70736274ff01005602000000011a7092fd91f79a8be40502c710165274348d7b2a6cbab776bc18b9b8ae81f4160100000000ffffffff0100000000000000001a6a1863617401f4d5ea814aabc273f329f3563848a090fc8c77cf000000000001012b4a01000000000000220020a110df5f2a3e0c0ea9ec2a96b58bc81a798175309b9134c5deca57111d9a2b3d22020293992068c88f47b988debe61ea9731c2a4d132da47f3591b2b89cfb0f9f7a25c473044022024c0c9f7622d5b5b2ba17377933f272a4d94bf8c742dd2bbb56b44839fc856e902206907a06457389df7a474f57a3d1aeab49b4a4945ab6b9940d1f35f01373c67c38201030482000000010567210293992068c88f47b988debe61ea9731c2a4d132da47f3591b2b89cfb0f9f7a25c14bbfa32c40aa80e2321daf64feb88ec4c0dba9e512102943e87ed8c2d38a8b3ec76999b70e4a605dbe041632099cac7387238b401220054795379ad537978ac6b6d6d6c770000",
"data": [
{
"name": "marker",
"type": "Int",
"value": "1"
},
{
"name": "timestamp",
"type": "Int",
"value": "1734450823"
}
],
"serializedData": "01879e6167"
}
DexUseTimestamp
https://oracle.scrypt.io/v1/timestamp
Contract invocation script link
import {
SHPreimage,
SigHashUtils,
SpentScriptsCtx,
} from '@cat-protocol/cat-sdk'
import { PubKey, SmartContract, assert, hash160, method, prop } from 'scrypt-ts'
import { OracleLib } from '../oracleLib'
import {
OracleTimestampState,
OracleTimestampStateProto,
} from './timestampProto'
export class DexUseTimestamp extends SmartContract {
@prop()
oracleKey: PubKey
constructor(oracleKey: PubKey) {
super(...arguments)
this.oracleKey = oracleKey
}
@method()
public unlock(
timestampData: OracleTimestampState,
publickey: PubKey,
oracleInputIndex: bigint,
shPreimage: SHPreimage,
spentScriptsCtx: SpentScriptsCtx
) {
// Check sighash preimage.
assert(
this.checkSig(
SigHashUtils.checkSHPreimage(shPreimage),
SigHashUtils.Gx
),
'preimage check error'
)
SigHashUtils.checkSpentScriptsCtx(
spentScriptsCtx,
shPreimage.hashSpentScripts
)
const oracleLocking = OracleLib.buildOracleP2wsh(
hash160(OracleTimestampStateProto.toByteString(timestampData)),
this.oracleKey,
publickey
)
assert(
spentScriptsCtx[Number(oracleInputIndex)] == oracleLocking,
'oracle script mismatch'
)
}
}
DexUsePrice
https://oracle.scrypt.io/v1/price
Contract invocation script link
import {
SHPreimage,
SigHashUtils,
SpentScriptsCtx,
} from '@cat-protocol/cat-sdk'
import { PubKey, SmartContract, assert, hash160, method, prop } from 'scrypt-ts'
import { OracleLib } from '../oracleLib'
import { OraclePriceState, OraclePriceStateProto } from './priceProto'
export class DexUsePrice extends SmartContract {
@prop()
oracleKey: PubKey
constructor(oracleKey: PubKey) {
super(...arguments)
this.oracleKey = oracleKey
}
@method()
public unlock(
priceData: OraclePriceState,
publickey: PubKey,
oracleInputIndex: bigint,
shPreimage: SHPreimage,
spentScriptsCtx: SpentScriptsCtx
) {
// Check sighash preimage.
assert(
this.checkSig(
SigHashUtils.checkSHPreimage(shPreimage),
SigHashUtils.Gx
),
'preimage check error'
)
SigHashUtils.checkSpentScriptsCtx(
spentScriptsCtx,
shPreimage.hashSpentScripts
)
const oracleLocking = OracleLib.buildOracleP2wsh(
hash160(OraclePriceStateProto.toByteString(priceData)),
this.oracleKey,
publickey
)
assert(
spentScriptsCtx[Number(oracleInputIndex)] == oracleLocking,
'oracle script mismatch'
)
}
}