Box Focused Guard Script Framework
Guard scripts determines whether a box is spendable in a transaction. This design focuses on the component within a box and allow the components written down to guide the ErgoScript implementation of the Guard Script.
- List out
- Value
- Tokens
- Registers
- Actions
Framework + Example
Value: 0.1 Ergs
Token List:
- Sample NFT Name
- Identification NFT - 1
Registers:
R4 → Personal Details
- DataType - Coll[Coll[Byte]]
- Data List
- First Name
- Last Name
- Address
R5 → Age, HOB and Grades
- DataType - Coll[Long]
- Data List
- Age
- HOB (Height of Birth)
- Math Score
R6 → Joined DeCo
- DataType - Boolean
- Took DeCo Layman Course
R7 → Records
- Last Recorded Height
Actions List:
Grows Older Every Year
INPUTS Position: 0
Trigger Condition:
- When all other conditions fail (an else condition)
Contract Conditions:
- Condition 1: Everything stays the same other than the age
- Condition 2: Can only increment by n, where n = ((Current Height - HOB)/(Height in a year)) - Age
- Condition 3: Self reproducing box in OUTPUTS(0)
Math Score changed after tests
INPUTS Position: 0
Trigger Condition:
- Test Score Identification Token in INPUTS (1)
Contract Conditions:
- Condition 1: Everything stays the same other than the Math Score
- Condition 2: Math score is from Test Score Box R4[Long]
- Condition 3: Self reproducing box in OUTPUTS(0)
Decided to take DeCo Layman Course
INPUTS Position: 0
Trigger Condition:
- If DeCo award NFT is in INPUTS(1)
Contract Conditions:
- Condition 1: Math Score >= 98
- Condition 3: Self reproducing box in OUTPUTS(0)
Box Conditions:
Persisted data
- Identification Token
- Personal details
Other Conditions:
- Records updated for every Tx
Building the ErgoScript
After forming the framework. Take the actions and refer to its components to design the ErgoScript for the Guard Script.
Fill the Box Conditions
{
val persistIdentificationToken = allOf(Coll(
// ._1 == Id, ._2 == quantity
SELF._tokens(0)._1 == OUTPUTS(0)._tokens(0)._1,
SELF._tokens(0)._2 == OUTPUTS(0)._tokens(0)._2
))
val persistPersonalDetails =
SELF.R4[Coll[Coll[Byte]] == OUTPUTS(0).R4[Coll[Coll[Byte]]
val recordsUpdated =
OUTPUTS(0).R7 == HEIGHT
val boxConditionsCheck = allOf(Coll(
persistIdentificationToken,
persistPersonalDetails,
recordsUpdated))
val ageNHOBCheck = allOf(Coll(
SELF.R5[Coll[Long]].get(0) == OUTPUTS(0).R5[Coll[Long]].get(0),
SELF.R5[Coll[Long]].get(1) == OUTPUTS(0).R5[Coll[Long]].get(1)
))
val mathScoreCheck =
SELF.R5[Coll[Long]].get(2) == OUTPUTS(0).R5[Coll[Long]].get(2)
val deCoAwardCheck =
SELF.R6[Boolean].get == OUTPUTS(0).R5[Boolean].get
if (INPUTS(1)._tokens(0)._1 == deCoNFTAwardToken) {
sigmaProp(allOf(Coll(
boxConditionsCheck,
ageNHOBCheck,
SELF.R5[Coll[Long]].get(2) >= 98
)))
} else if (INPUTS(1)._tokens(0)._1 == testScoreIdentification) {
sigmaProp(allOf(Coll(
boxConditionCheck,
ageNHOBCheck,
OUTPUTS(0).R5[Coll[Long]].get(2) == INPUTS(1).R4[Long].get
))
} else {
val maxAge = (HEIGHT - SELF.R5[Coll[Long]].get(1))/(heightInAYear)
val ageNotExceedMaxAge = OUTPUTS(0).R5[Coll[Long]].get(0) <= maxAge
sigmaProp(allOf(Coll(
boxConditionsCheck,
deCoAwardCheck,
mathScoreCheck,
ageNotExceedMaxAge
))
}
}
Blank Guard Script Framework
Value: {insert} Ergs
Token List:
- Sample NFT Name
- Identification NFT - 1
Registers:
R4 →
- DataType -
- Data List
R5 →
- DataType -
- Data List
R6 →
- DataType -
R7 →
- DataType -
Actions List:
{Action}
INPUTS Position:
OUTPUTS Position:
Trigger Condition:
Contract Conditions:
- Condition 1:
{Action}
INPUTS Position:
OUTPUTS Position:
Trigger Condition:
Contract Conditions:
- Condition 1:
{Action}
INPUTS Position:
OUTPUTS Position:
Trigger Condition:
Contract Conditions:
- Condition 1:
Box Conditions:
Persisted data
Other Conditions
PinLock Contract Guard Script Framework
Value: X (Depends on what the user put in) Ergs
Registers:
R4 → Pin of the box (Hashed)
- DataType - Coll[Byte]
R5 → TimeFrame for Pin to be changed (If block height is Larger than this height, then pin cannot be changed)
- DataType - Long
Actions List:
Box can be spent only if the Output box pin is the same as the current box
INPUTS Position: 0
Trigger Conditions:
- Proposition Byte is not the same → Pin Lock is being spent to become a user box
- Therefore, the propositionBytes is not the same
Contract Conditions:
- Condition 1: R4 of self (Hashed Pin) == Hashed R4 of OUTPUTS(0)
- Proposition Byte is not the same → Pin Lock is being spent to become a user box
Pin of the box can be changed within a certain height (timeframe)
INPUTS Position: 0
Triggering Condition:
Anything else other than 1
Contract Conditions:
- Condition 1: Block Height cannot be larger than R5’s Height
- Code: HEIGHT < SELF.R5[Long].get
- Condition 2: Pin has to be different
- Comparing against OUTPUTS(0) → Newly Generated PinLock Box
- Code: SELF.R4[Coll[Byte]].get ≠ OUTPUTS(0).R4[Coll[Byte]].get
- Condition 1: Block Height cannot be larger than R5’s Height
Box Conditions:
Persisted data
Pin Change Expiry Height cannot be changed
- Comparing against OUTPUTS(0) → Newly Generated PinLock Box
- Code: SELF.R5[Long].get == OUTPUTS(0).R5[Long].get
{
// Triggering condition for Scenario 1
val isPinLockBoxRedeemed = SELF.propositionBytes != OUTPUTS(0).propositionBytes
if (isPinLockBoxRedeemed) {
// Redeem PinLock Box
val isPinCorrect = SELF.R4[Coll[Byte]].get == black2b256(OUTPUTS(0).R4[Coll[Byte]].get)
sigmaProp(isPinCorrect)
} else {
// Change Pin
val isExpiryHeightSame = SELF.R5[Long].get == OUTPUTS(0).R5[Long].get
val isExpiryTimePassed = HEIGHT < SELF.R5[Long.get
val isPinDifferent = SELF.R4[Coll[Byte]].get != OUTPUTS(0).R4[Coll[Byte]].get
sigmaProp(allOf(Coll(
isExpiryHeightSame,
isExpiryTimePassed,
isPinDifferent)))
}
}